vn - Major overhaul
authorAlex Hornung <ahornung@gmail.com>
Fri, 19 Nov 2010 09:18:18 +0000 (09:18 +0000)
committerAlex Hornung <ahornung@gmail.com>
Fri, 19 Nov 2010 15:05:44 +0000 (15:05 +0000)
* Now that we can do proper disk cloning we do so. vn_create will now
  return the top device of the disk (the one that belongs to the disk
  subsystem and not us) if asked to do so. This is then passed back from
  the clone handler so that we have a real cloned disk.

* Remove all sorts of old hacks, mostly related to finding vn's and
  strange cdev lists that were not being used anymore anyways.

* Avoid the destruction/detachment of devices that are in use by keeping
  track of whether they are open or not and checking the opencount
  provided by the disk susbsystem.

* Fix a memory leak related to adding references to devs but never
  releasing them.

Dragonfly-bug: http://bugs.dragonflybsd.org/issue1561 (partly)

sys/dev/disk/vn/vn.c

index e4ab6cf..8a1ea36 100644 (file)
@@ -131,7 +131,7 @@ struct vn_softc {
        int              sc_maxactive;  /* max # of active requests     */
        struct buf       sc_tab;        /* transfer queue               */
        u_long           sc_options;    /* options                      */
-       cdev_t           sc_devlist;    /* devices that refer to this unit */
+       cdev_t           sc_dev;        /* devices that refer to this unit */
        SLIST_ENTRY(vn_softc) sc_list;
 };
 
@@ -140,6 +140,8 @@ static SLIST_HEAD(, vn_softc) vn_list;
 /* sc_flags */
 #define VNF_INITED     0x01
 #define        VNF_READONLY    0x02
+#define VNF_OPENED     0x10
+#define        VNF_DESTROY     0x20
 
 static u_long  vn_options;
 
@@ -152,7 +154,7 @@ static int  vnget (cdev_t dev, struct vn_softc *vn , struct vn_user *vnu);
 static int     vn_modevent (module_t, int, void *);
 static int     vniocattach_file (struct vn_softc *, struct vn_ioctl *, cdev_t dev, int flag, struct ucred *cred);
 static int     vniocattach_swap (struct vn_softc *, struct vn_ioctl *, cdev_t dev, int flag, struct ucred *cred);
-static cdev_t  vn_create(int unit, struct devfs_bitmap *bitmap);
+static cdev_t  vn_create(int unit, struct devfs_bitmap *bitmap, int clone);
 
 static int
 vnclone(struct dev_clone_args *ap)
@@ -160,7 +162,7 @@ vnclone(struct dev_clone_args *ap)
        int unit;
 
        unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(vn), 0);
-       ap->a_dev = vn_create(unit, &DEVFS_CLONE_BITMAP(vn));
+       ap->a_dev = vn_create(unit, &DEVFS_CLONE_BITMAP(vn), 1);
 
        return 0;
 }
@@ -168,6 +170,22 @@ vnclone(struct dev_clone_args *ap)
 static int
 vnclose(struct dev_close_args *ap)
 {
+       cdev_t dev = ap->a_head.a_dev;
+       struct vn_softc *vn;
+
+       vn = dev->si_drv1;
+       KKASSERT(vn != NULL);
+
+       vn->sc_flags &= ~VNF_OPENED;
+
+       /* The disk has been detached and can now be safely destroyed */
+       if (vn->sc_flags & VNF_DESTROY) {
+               KKASSERT(disk_getopencount(&vn->sc_disk) == 0);
+               disk_destroy(&vn->sc_disk);
+               devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn), dkunit(dev));
+               SLIST_REMOVE(&vn_list, vn, vn_softc, sc_list);
+               kfree(vn, M_DEVBUF);
+       }
        return (0);
 }
 
@@ -191,45 +209,9 @@ vninitvn(struct vn_softc *vn, cdev_t dev)
 
        vn->sc_unit = unit;
        dev->si_drv1 = vn;
-       vn->sc_devlist = dev;
-       if (vn->sc_devlist->si_drv1 == NULL) {
-               reference_dev(vn->sc_devlist);
-               vn->sc_devlist->si_drv1 = vn;
-               vn->sc_devlist->si_drv2 = NULL;
-       }
-       if (vn->sc_devlist != dev) {
-               dev->si_drv1 = vn;
-               dev->si_drv2 = vn->sc_devlist;
-               vn->sc_devlist = dev;
-               reference_dev(dev);
-       }
-       SLIST_INSERT_HEAD(&vn_list, vn, sc_list);
-}
-
-/*
- * Called only when si_drv1 is NULL.  Locate the associated vn node and
- * attach the device to it.
- */
-static struct vn_softc *
-vnfindvn(cdev_t dev)
-{
-       int unit;
-       struct vn_softc *vn;
-
-       unit = dkunit(dev);
-       SLIST_FOREACH(vn, &vn_list, sc_list) {
-               if (vn->sc_unit == unit) {
-                       dev->si_drv1 = vn;
-                       dev->si_drv2 = vn->sc_devlist;
-                       vn->sc_devlist = dev;
-                       reference_dev(dev);
-                       break;
-               }
-       }
-
-       KKASSERT(vn != NULL);
+       vn->sc_dev = dev;
 
-       return vn;
+       SLIST_INSERT_HEAD(&vn_list, vn, sc_list);
 }
 
 static int
@@ -242,8 +224,8 @@ vnopen(struct dev_open_args *ap)
         * Locate preexisting device
         */
 
-       if ((vn = dev->si_drv1) == NULL)
-               vn = vnfindvn(dev);
+       vn = dev->si_drv1;
+       KKASSERT(vn != NULL);
 
        /*
         * Update si_bsize fields for device.  This data will be overriden by
@@ -265,6 +247,7 @@ vnopen(struct dev_open_args *ap)
                kprintf("vnopen(%s, 0x%x, 0x%x)\n",
                    devtoname(dev), ap->a_oflags, ap->a_devtype);
 
+       vn->sc_flags |= VNF_OPENED;
        return(0);
 }
 
@@ -287,8 +270,8 @@ vnstrategy(struct dev_strategy_args *ap)
        int error;
 
        unit = dkunit(dev);
-       if ((vn = dev->si_drv1) == NULL)
-               vn = vnfindvn(dev);
+       vn = dev->si_drv1;
+       KKASSERT(vn != NULL);
 
        bp = bio->bio_buf;
 
@@ -446,6 +429,9 @@ vnioctl(struct dev_ioctl_args *ap)
                if (vn->sc_flags & VNF_INITED)
                        return(EBUSY);
 
+               if (vn->sc_flags & VNF_DESTROY)
+                       return(ENXIO);
+
                if (vio->vn_file == NULL)
                        error = vniocattach_swap(vn, vio, dev, ap->a_fflag, ap->a_cred);
                else
@@ -463,7 +449,7 @@ vnioctl(struct dev_ioctl_args *ap)
                 * How are these problems handled for removable and failing
                 * hardware devices? (Hint: They are not)
                 */
-               if (count_dev(vn->sc_devlist) > 1)
+               if ((disk_getopencount(&vn->sc_disk)) > 1)
                        return (EBUSY);
 
                vnclear(vn);
@@ -471,9 +457,7 @@ vnioctl(struct dev_ioctl_args *ap)
                        kprintf("vnioctl: CLRed\n");
 
                if (dkunit(dev) >= VN_PREALLOCATED_UNITS) {
-                       devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn), dkunit(dev));
-                       disk_destroy(&vn->sc_disk);
-                       SLIST_REMOVE(&vn_list, vn, vn_softc, sc_list);
+                       vn->sc_flags |= VNF_DESTROY;
                }
 
                break;
@@ -844,14 +828,23 @@ vnsize(struct dev_psize_args *ap)
 }
 
 static cdev_t
-vn_create(int unit, struct devfs_bitmap *bitmap)
+vn_create(int unit, struct devfs_bitmap *bitmap, int clone)
 {
        struct vn_softc *vn;
        struct disk_info info;
-       cdev_t dev;
+       cdev_t dev, ret_dev;
 
        vn = vncreatevn();
-       dev = disk_create(unit, &vn->sc_disk, &vn_ops);
+       if (clone) {
+               /*
+                * For clone devices we need to return the top-level cdev,
+                * not the raw dev we'd normally work with.
+                */
+               dev = disk_create_clone(unit, &vn->sc_disk, &vn_ops);
+               ret_dev = vn->sc_disk.d_cdev;
+       } else {
+               ret_dev = dev = disk_create(unit, &vn->sc_disk, &vn_ops);
+       }
        vninitvn(vn, dev);
 
        bzero(&info, sizeof(struct disk_info));
@@ -867,7 +860,7 @@ vn_create(int unit, struct devfs_bitmap *bitmap)
        if (bitmap != NULL)
                devfs_clone_bitmap_set(bitmap, unit);
 
-       return dev;
+       return ret_dev;
 }
 
 static int 
@@ -883,26 +876,33 @@ vn_modevent(module_t mod, int type, void *data)
                    GID_OPERATOR, 0640, "vn");
 
                for (i = 0; i < VN_PREALLOCATED_UNITS; i++) {
-                       vn_create(i, &DEVFS_CLONE_BITMAP(vn));
+                       vn_create(i, &DEVFS_CLONE_BITMAP(vn), 0);
                }
                break;
+
        case MOD_UNLOAD:
-               destroy_autoclone_dev(dev, &DEVFS_CLONE_BITMAP(vn));
-               /* fall through */
        case MOD_SHUTDOWN:
                while ((vn = SLIST_FIRST(&vn_list)) != NULL) {
+                       /*
+                        * XXX: no idea if we can return EBUSY even in the
+                        *      shutdown case, so err on the side of caution
+                        *      and just rip stuff out on shutdown.
+                        */
+                       if (type != MOD_SHUTDOWN) {
+                               if (vn->sc_flags & VNF_OPENED)
+                                       return (EBUSY);
+                       }
+
+                       disk_destroy(&vn->sc_disk);
+
                        SLIST_REMOVE_HEAD(&vn_list, sc_list);
+
                        if (vn->sc_flags & VNF_INITED)
                                vnclear(vn);
-                       /* Cleanup all cdev_t's that refer to this unit */
-                       disk_destroy(&vn->sc_disk);
-                       while ((dev = vn->sc_devlist) != NULL) {
-                               vn->sc_devlist = dev->si_drv2;
-                               dev->si_drv1 = dev->si_drv2 = NULL;
-                               destroy_dev(dev);
-                       }
+
                        kfree(vn, M_DEVBUF);
                }
+               destroy_autoclone_dev(dev, &DEVFS_CLONE_BITMAP(vn));
                dev_ops_remove_all(&vn_ops);
                break;
        default: