DF-4563 / fix.diff
--- a/sys/dev/disk/vn/vn.c +++ b/sys/dev/disk/vn/vn.c @@ -71,6 +71,7 @@ #include <sys/stat.h> #include <sys/module.h> #include <sys/vnioctl.h> +#include <sys/callout.h> #include <vm/vm.h> #include <vm/vm_object.h> @@ -132,6 +133,7 @@ u_long sc_options; /* options */ cdev_t sc_dev; /* devices that refer to this unit */ SLIST_ENTRY(vn_softc) sc_list; + struct callout sc_callout; /* deferred destroy (DF-4563) */ }; static SLIST_HEAD(, vn_softc) vn_list; @@ -166,6 +168,25 @@ return 0; } +/* + * DF-4563: the VNF_DESTROY teardown must not run from inside the rawdev + * close path, where it races the concurrent last-but-one close()'s cdev + * sysref releases (double-put -> sysref_put panic) and frees the struct + * disk that the generic diskclose() of the losing close still + * dereferences. Run the teardown deferred instead. + */ +static void +vn_destroy_deferred(void *arg) +{ + struct vn_softc *vn = arg; + + KKASSERT(disk_getopencount(&vn->sc_disk) == 0); + disk_destroy(&vn->sc_disk); + devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn), vn->sc_unit); + SLIST_REMOVE(&vn_list, vn, vn_softc, sc_list); + kfree(vn, M_VN); +} + static int vnclose(struct dev_close_args *ap) { @@ -177,13 +198,10 @@ vn->sc_flags &= ~VNF_OPENED; - /* The disk has been detached and can now be safely destroyed */ + /* The disk has been detached: destroy it deferred (DF-4563) */ 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_VN); + callout_reset(&vn->sc_callout, hz, vn_destroy_deferred, vn); } return (0); } @@ -194,6 +212,7 @@ struct vn_softc *vn; vn = kmalloc(sizeof *vn, M_VN, M_WAITOK | M_ZERO); + callout_init_mp(&vn->sc_callout); return vn; } @@ -909,6 +928,9 @@ if (vn->sc_flags & VNF_INITED) vnclear(vn); + /* DF-4563: cancel any pending deferred destroy */ + callout_drain(&vn->sc_callout); + kfree(vn, M_VN); } destroy_autoclone_dev(dev, &DEVFS_CLONE_BITMAP(vn)); |