aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/kern_conf.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>2004-02-21 20:29:52 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>2004-02-21 20:29:52 +0000
commitb0b03348789e3dd9b271ed132b29adb8045293a7 (patch)
treec97716bebd5c7c784bf841850192a5addeaf0347 /sys/kern/kern_conf.c
parentc9c7976f7ffdd0ed81e12d8930651a9a858defa1 (diff)
downloadsrc-b0b03348789e3dd9b271ed132b29adb8045293a7.tar.gz
src-b0b03348789e3dd9b271ed132b29adb8045293a7.zip
Device megapatch 2/6:
This commit adds a couple of functions for pseudodrivers to use for implementing cloning in a manner we will be able to lock down (shortly). Basically what happens is that pseudo drivers get a way to ask for "give me the dev_t with this unit number" or alternatively "give me a dev_t with the lowest guaranteed free unit number" (there is unfortunately a lot of non-POLA in the exact numeric value of this number, just live with it for now) Managing the unit number space this way removes the need to use rman(9) to do so in the drivers this greatly simplifies the code in the drivers because even using rman(9) they still needed to manage their dev_t's anyway. I have taken the if_tun, if_tap, snp and nmdm drivers through the mill, partly because they (ab)used makedev(), but mostly because together they represent three different problems for device-cloning: if_tun and snp is the plain case: just give me a device. if_tap has two kinds of devices, with a flag for device type. nmdm has paired devices (ala pty) can you can clone either of them.
Notes
Notes: svn path=/head/; revision=126077
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r--sys/kern/kern_conf.c137
1 files changed, 129 insertions, 8 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index c99173d79429..62033c7e366a 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -324,10 +324,22 @@ makeudev(int x, int y)
}
static void
-prep_cdevsw(struct cdevsw *devsw)
+find_major(struct cdevsw *devsw)
{
int i;
+ for (i = NUMCDEVSW - 1; i > 0; i--)
+ if (reserved_majors[i] != i)
+ break;
+ KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
+ devsw->d_maj = i;
+ reserved_majors[i] = i;
+}
+
+static void
+prep_cdevsw(struct cdevsw *devsw)
+{
+
if (devsw->d_open == NULL) devsw->d_open = null_open;
if (devsw->d_close == NULL) devsw->d_close = null_close;
if (devsw->d_read == NULL) devsw->d_read = no_read;
@@ -339,12 +351,7 @@ prep_cdevsw(struct cdevsw *devsw)
if (devsw->d_dump == NULL) devsw->d_dump = no_dump;
if (devsw->d_kqfilter == NULL) devsw->d_kqfilter = no_kqfilter;
if (devsw->d_maj == MAJOR_AUTO) {
- for (i = NUMCDEVSW - 1; i > 0; i--)
- if (reserved_majors[i] != i)
- break;
- KASSERT(i > 0, ("Out of major numbers (%s)", devsw->d_name));
- devsw->d_maj = i;
- reserved_majors[i] = i;
+ find_major(devsw);
} else {
if (devsw->d_maj == 256) /* XXX: tty_cons.c is magic */
devsw->d_maj = 0;
@@ -458,17 +465,22 @@ destroy_dev(dev_t dev)
}
devfs_destroy(dev);
+ dev->si_flags &= ~SI_NAMED;
+
if (dev->si_flags & SI_CHILD) {
LIST_REMOVE(dev, si_siblings);
dev->si_flags &= ~SI_CHILD;
}
while (!LIST_EMPTY(&dev->si_children))
destroy_dev(LIST_FIRST(&dev->si_children));
+ if (dev->si_flags & SI_CLONELIST) {
+ LIST_REMOVE(dev, si_clone);
+ dev->si_flags &= ~SI_CLONELIST;
+ }
dev->si_drv1 = 0;
dev->si_drv2 = 0;
dev->si_devsw = 0;
bzero(&dev->__si_u, sizeof(dev->__si_u));
- dev->si_flags &= ~SI_NAMED;
dev->si_flags &= ~SI_ALIAS;
freedev(dev);
}
@@ -523,6 +535,115 @@ dev_stdclone(char *name, char **namep, const char *stem, int *unit)
}
/*
+ * Helper functions for cloning device drivers.
+ *
+ * The objective here is to make it unnecessary for the device drivers to
+ * use rman or similar to manage their unit number space. Due to the way
+ * we do "on-demand" devices, using rman or other "private" methods
+ * will be very tricky to lock down properly once we lock down this file.
+ *
+ * Instead we give the drivers these routines which puts the dev_t's that
+ * are to be managed on their own list, and gives the driver the ability
+ * to ask for the first free unit number or a given specified unit number.
+ *
+ * In addition these routines support paired devices (pty, nmdm and similar)
+ * by respecting a number of "flag" bits in the minor number.
+ *
+ */
+
+struct clonedevs {
+ LIST_HEAD(,cdev) head;
+};
+
+int
+clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, dev_t *dp, u_int extra)
+{
+ struct clonedevs *cd;
+ dev_t dev, dl, de;
+ int unit, low, u;
+
+ KASSERT(!(extra & CLONE_UNITMASK),
+ ("Illegal extra bits (0x%x) in clone_create", extra));
+ KASSERT(*up <= CLONE_UNITMASK,
+ ("Too high unit (0x%x) in clone_create", *up));
+
+ if (csw->d_maj == MAJOR_AUTO)
+ find_major(csw);
+ /* if clonedevs have not been initialized, we do it here */
+ cd = *cdp;
+ if (cd == NULL) {
+ cd = malloc(sizeof *cd, M_DEVBUF, M_WAITOK | M_ZERO);
+ LIST_INIT(&cd->head);
+ *cdp = cd;
+ }
+
+ /*
+ * Search the list for a lot of things in one go:
+ * A preexisting match is returned immediately.
+ * The lowest free unit number if we are passed -1, and the place
+ * in the list where we should insert that new element.
+ * The place to insert a specified unit number, if applicable
+ * the end of the list.
+ */
+ unit = *up;
+ low = 0;
+ de = dl = NULL;
+ LIST_FOREACH(dev, &cd->head, si_clone) {
+ u = dev2unit(dev);
+ if (u == (unit | extra)) {
+ *dp = dev;
+ return (0);
+ }
+ if (unit == -1 && u == low) {
+ low++;
+ de = dev;
+ continue;
+ }
+ if (u > unit) {
+ dl = dev;
+ break;
+ }
+ de = dev;
+ }
+ if (unit == -1)
+ unit = low;
+ dev = makedev(csw->d_maj, unit2minor(unit | extra));
+ KASSERT(!(dev->si_flags & SI_CLONELIST),
+ ("Dev %p should not be on clonelist", dev));
+ if (dl != NULL)
+ LIST_INSERT_BEFORE(dl, dev, si_clone);
+ else if (de != NULL)
+ LIST_INSERT_AFTER(de, dev, si_clone);
+ else
+ LIST_INSERT_HEAD(&cd->head, dev, si_clone);
+ dev->si_flags |= SI_CLONELIST;
+ *up = unit;
+ return (1);
+}
+
+/*
+ * Kill everything still on the list. The driver should already have
+ * disposed of any softc hung of the dev_t's at this time.
+ */
+void
+clone_cleanup(struct clonedevs **cdp)
+{
+ dev_t dev, tdev;
+ struct clonedevs *cd;
+
+ cd = *cdp;
+ if (cd == NULL)
+ return;
+ LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) {
+ KASSERT(dev->si_flags & SI_NAMED,
+ ("Driver has goofed in cloning underways udev %x", dev->si_udev));
+ destroy_dev(dev);
+ }
+ free(cd, M_DEVBUF);
+ *cdp = NULL;
+}
+
+/*
* Helper sysctl for devname(3). We're given a {u}dev_t and return
* the name, if any, registered by the device driver.
*/