diff options
author | phk <phk@FreeBSD.org> | 2005-01-24 12:44:56 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2005-01-24 12:44:56 +0000 |
commit | e5b74a2850fcb72f8b86a8034c494dc10c45982d (patch) | |
tree | ebd1ee0f4f58804b79bd5f7728a9ac4612db661f /sys/kern/kern_conf.c | |
parent | a33ba4ee87457d899ef3a3bb2c4af236044387e3 (diff) | |
download | FreeBSD-src-e5b74a2850fcb72f8b86a8034c494dc10c45982d.zip FreeBSD-src-e5b74a2850fcb72f8b86a8034c494dc10c45982d.tar.gz |
Fix a list corruption issue in cloning device management using the
western strategy ("allocate first, ask questions later") so we can
extend the devmtx coverage to the clone list.
Diffstat (limited to 'sys/kern/kern_conf.c')
-rw-r--r-- | sys/kern/kern_conf.c | 57 |
1 files changed, 41 insertions, 16 deletions
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c index 05df892..d808381 100644 --- a/sys/kern/kern_conf.c +++ b/sys/kern/kern_conf.c @@ -60,7 +60,7 @@ static LIST_HEAD(, cdev) dev_hash[DEVT_HASH]; static struct mtx devmtx; static void freedev(struct cdev *dev); -static struct cdev *newdev(int x, int y); +static struct cdev *newdev(int x, int y, struct cdev *); void dev_lock(void) @@ -286,21 +286,23 @@ allocdev(void) } static struct cdev * -newdev(int x, int y) +newdev(int x, int y, struct cdev *si) { - struct cdev *si; + struct cdev *si2; dev_t udev; int hash; + mtx_assert(&devmtx, MA_OWNED); if (x == umajor(NODEV) && y == uminor(NODEV)) panic("newdev of NODEV"); udev = (x << 8) | y; hash = udev % DEVT_HASH; - LIST_FOREACH(si, &dev_hash[hash], si_hash) { - if (si->si_udev == udev) - return (si); + LIST_FOREACH(si2, &dev_hash[hash], si_hash) { + if (si2->si_udev == udev) { + freedev(si); + return (si2); + } } - si = allocdev(); si->si_udev = udev; LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash); return (si); @@ -327,14 +329,17 @@ findcdev(dev_t udev) struct cdev *si; int hash; + mtx_assert(&devmtx, MA_NOTOWNED); if (udev == NODEV) return (NULL); + dev_lock(); hash = udev % DEVT_HASH; LIST_FOREACH(si, &dev_hash[hash], si_hash) { if (si->si_udev == udev) - return (si); + break; } - return (NULL); + dev_unlock(); + return (si); } int @@ -449,7 +454,9 @@ make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, con if (!(devsw->d_flags & D_INIT)) prep_cdevsw(devsw); - dev = newdev(devsw->d_maj, minornr); + dev = allocdev(); + dev_lock(); + dev = newdev(devsw->d_maj, minornr, dev); if (dev->si_flags & SI_CHEAPCLONE && dev->si_flags & SI_NAMED && dev->si_devsw == devsw) { @@ -458,9 +465,9 @@ make_dev(struct cdevsw *devsw, int minornr, uid_t uid, gid_t gid, int perms, con * simplifies cloning devices. * XXX: still ?? */ + dev_unlock(); return (dev); } - dev_lock(); KASSERT(!(dev->si_flags & SI_NAMED), ("make_dev() by driver %s on pre-existing device (maj=%d, min=%d, name=%s)", devsw->d_name, major(dev), minor(dev), devtoname(dev))); @@ -693,7 +700,7 @@ int clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev **dp, u_int extra) { struct clonedevs *cd; - struct cdev *dev, *dl, *de; + struct cdev *dev, *ndev, *dl, *de; int unit, low, u; KASSERT(*cdp != NULL, @@ -715,13 +722,19 @@ clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev ** * the end of the list. */ unit = *up; + ndev = allocdev(); + dev_lock(); low = extra; de = dl = NULL; cd = *cdp; LIST_FOREACH(dev, &cd->head, si_clone) { + KASSERT(dev->si_flags & SI_CLONELIST, + ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); u = dev2unit(dev); if (u == (unit | extra)) { *dp = dev; + freedev(ndev); + dev_unlock(); return (0); } if (unit == -1 && u == low) { @@ -733,13 +746,20 @@ clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev ** dl = dev; break; } - de = dev; } if (unit == -1) unit = low & CLONE_UNITMASK; - dev = newdev(csw->d_maj, unit2minor(unit | extra)); + dev = newdev(csw->d_maj, unit2minor(unit | extra), ndev); + if (dev->si_flags & SI_CLONELIST) { + printf("dev %p (%s) is on clonelist\n", dev, dev->si_name); + printf("unit=%d\n", unit); + LIST_FOREACH(dev, &cd->head, si_clone) { + printf("\t%p %s\n", dev, dev->si_name); + } + panic("foo"); + } KASSERT(!(dev->si_flags & SI_CLONELIST), - ("Dev %p should not be on clonelist", dev)); + ("Dev %p(%s) should not be on clonelist", dev, dev->si_name)); if (dl != NULL) LIST_INSERT_BEFORE(dl, dev, si_clone); else if (de != NULL) @@ -748,6 +768,7 @@ clone_create(struct clonedevs **cdp, struct cdevsw *csw, int *up, struct cdev ** LIST_INSERT_HEAD(&cd->head, dev, si_clone); dev->si_flags |= SI_CLONELIST; *up = unit; + dev_unlock(); return (1); } @@ -764,11 +785,15 @@ clone_cleanup(struct clonedevs **cdp) cd = *cdp; if (cd == NULL) return; + dev_lock(); LIST_FOREACH_SAFE(dev, &cd->head, si_clone, tdev) { + KASSERT(dev->si_flags & SI_CLONELIST, + ("Dev %p(%s) should be on clonelist", dev, dev->si_name)); KASSERT(dev->si_flags & SI_NAMED, ("Driver has goofed in cloning underways udev %x", dev->si_udev)); - destroy_dev(dev); + idestroy_dev(dev); } + dev_unlock(); free(cd, M_DEVBUF); *cdp = NULL; } |