--- a/sys/dev/virtual/nvmm/nvmm_dragonfly.c +++ b/sys/dev/virtual/nvmm/nvmm_dragonfly.c @@ -281,6 +281,14 @@ static d_ioctl_t dfbsd_nvmm_ioctl; static d_priv_dtor_t dfbsd_nvmm_dtor; +/* + * The cdev and an open-file counter. MOD_UNLOAD must not tear the module + * down while descriptors are open: their cdevpriv dtor (dfbsd_nvmm_dtor) + * and every in-flight ioctl are module text/state. + */ +static cdev_t nvmm_dev = NULL; +static volatile unsigned int nvmm_opens; + static struct dev_ops nvmm_ops = { { "nvmm", 0, D_MPSAFE }, .d_open = dfbsd_nvmm_open, @@ -319,6 +327,8 @@ return error; } + os_atomic_inc_uint(&nvmm_opens); + return 0; } @@ -332,6 +342,10 @@ if (owner != &nvmm_root_owner) { os_mem_free(owner, sizeof(*owner)); } + + /* Released only after nvmm_kill_machines(), so that a concurrent + * nvmm_detach() observes nmachines==0 before opens==0. */ + os_atomic_dec_uint(&nvmm_opens); } static int @@ -366,9 +380,37 @@ static int nvmm_detach(void) { + /* + * Refuse the unload while any descriptor is open or any machine + * exists: the dtor and the in-flight ioctls live in this module. + */ + if (os_atomic_load_uint(&nvmm_opens) > 0) + return EBUSY; if (os_atomic_load_uint(&nmachines) > 0) return EBUSY; + /* + * Remove the device FIRST: destroy_dev() revectors the ops to + * dead_dev_ops, drains in-flight calls and blocks new ones, so the + * teardown below cannot race concurrent ioctls/opens. + */ + if (nvmm_dev == NULL) + return 0; + destroy_dev(nvmm_dev); + nvmm_dev = NULL; + + /* + * An open() may have been in flight across the drain above; with the + * device gone the counters are now stable. Restore the device and + * refuse rather than tearing down underneath the descriptor. + */ + if (os_atomic_load_uint(&nvmm_opens) > 0 || + os_atomic_load_uint(&nmachines) > 0) { + nvmm_dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640, + "nvmm"); + return EBUSY; + } + nvmm_fini(); return 0; } @@ -376,7 +418,6 @@ static int nvmm_modevent(module_t mod __unused, int type, void *data __unused) { - static cdev_t dev = NULL; int error; switch (type) { @@ -389,19 +430,18 @@ if (error) return error; - dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640, "nvmm"); - if (dev == NULL) { + nvmm_dev = make_dev(&nvmm_ops, 0, UID_ROOT, GID_NVMM, 0640, + "nvmm"); + if (nvmm_dev == NULL) { os_printf("nvmm: unable to create device\n"); error = ENOMEM; } break; case MOD_UNLOAD: - if (dev == NULL) - return 0; + /* destroy_dev() is done by nvmm_detach(), BEFORE the + * implementation is torn down. */ error = nvmm_detach(); - if (error == 0) - destroy_dev(dev); break; case MOD_SHUTDOWN: