--- sys/contrib/openzfs/module/os/freebsd/zfs/zvol_os.c.orig +++ sys/contrib/openzfs/module/os/freebsd/zfs/zvol_os.c @@ -128,7 +128,8 @@ struct g_provider *zsg_provider; } _zso_geom; } _zso_state; - int zso_dying; + boolean_t zso_opening; + boolean_t zso_dying; }; static uint32_t zvol_minors; @@ -226,12 +227,13 @@ } retry: - zv = atomic_load_ptr(&pp->private); + zv = pp->private; if (zv == NULL) return (SET_ERROR(ENXIO)); mutex_enter(&zv->zv_state_lock); - if (zv->zv_zso->zso_dying || zv->zv_flags & ZVOL_REMOVING) { + g_topology_unlock(); + if (zv->zv_flags & ZVOL_REMOVING || zv->zv_zso->zso_dying) { err = SET_ERROR(ENXIO); goto out_locked; } @@ -245,18 +247,16 @@ if (zv->zv_open_count == 0) { drop_suspend = B_TRUE; if (!rw_tryenter(&zv->zv_suspend_lock, ZVOL_RW_READER)) { - mutex_exit(&zv->zv_state_lock); - /* - * Removal may happen while the locks are down, so - * we can't trust zv any longer; we have to start over. + * Set a flag to interlock with zvol_os_remove_minor() + * while locks are dropped. */ - zv = atomic_load_ptr(&pp->private); - if (zv == NULL) - return (SET_ERROR(ENXIO)); - + zv->zv_zso->zso_opening = B_TRUE; + mutex_exit(&zv->zv_state_lock); rw_enter(&zv->zv_suspend_lock, ZVOL_RW_READER); mutex_enter(&zv->zv_state_lock); + zv->zv_zso->zso_opening = B_FALSE; + cv_broadcast(&zv->zv_removing_cv); if (zv->zv_zso->zso_dying || zv->zv_flags & ZVOL_REMOVING) { @@ -289,6 +289,7 @@ rw_exit(&zv->zv_suspend_lock); drop_suspend = B_FALSE; kern_yield(PRI_USER); + g_topology_lock(); goto retry; } else { drop_namespace = B_TRUE; @@ -337,6 +338,7 @@ mutex_exit(&zv->zv_state_lock); if (drop_suspend) rw_exit(&zv->zv_suspend_lock); + g_topology_lock(); return (err); } @@ -348,11 +350,12 @@ boolean_t drop_suspend = B_TRUE; int new_open_count; - zv = atomic_load_ptr(&pp->private); + zv = pp->private; if (zv == NULL) return (SET_ERROR(ENXIO)); mutex_enter(&zv->zv_state_lock); + g_topology_unlock(); if (zv->zv_flags & ZVOL_EXCL) { ASSERT3U(zv->zv_open_count, ==, 1); zv->zv_flags &= ~ZVOL_EXCL; @@ -413,6 +416,7 @@ if (drop_suspend) rw_exit(&zv->zv_suspend_lock); + g_topology_lock(); return (0); } @@ -448,7 +452,7 @@ ("Unsupported access request to %s (acr=%d, acw=%d, ace=%d).", pp->name, acr, acw, ace)); - if (atomic_load_ptr(&pp->private) == NULL) { + if (pp->private == NULL) { if (acr <= 0 && acw <= 0 && ace <= 0) return (0); return (pp->error); @@ -473,24 +477,16 @@ if (acw != 0) flags |= FWRITE; - g_topology_unlock(); if (count > 0) error = zvol_geom_open(pp, flags, count); else error = zvol_geom_close(pp, flags, -count); - g_topology_lock(); return (error); } static void zvol_geom_bio_start(struct bio *bp) { - zvol_state_t *zv = bp->bio_to->private; - - if (zv == NULL) { - g_io_deliver(bp, ENXIO); - return; - } if (bp->bio_cmd == BIO_GETATTR) { if (zvol_geom_bio_getattr(bp)) g_io_deliver(bp, EOPNOTSUPP); @@ -507,7 +503,10 @@ zvol_state_t *zv; zv = bp->bio_to->private; - ASSERT3P(zv, !=, NULL); + if (zv == NULL) { + g_io_deliver(bp, ENXIO); + return (0); + } spa_t *spa = dmu_objset_spa(zv->zv_objset); uint64_t refd, avail, usedobjs, availobjs; @@ -920,7 +919,7 @@ return (SET_ERROR(ENXIO)); mutex_enter(&zv->zv_state_lock); - if (zv->zv_zso->zso_dying || zv->zv_flags & ZVOL_REMOVING) { + if (zv->zv_flags & ZVOL_REMOVING || zv->zv_zso->zso_dying) { err = SET_ERROR(ENXIO); goto out_locked; } @@ -1251,24 +1250,32 @@ { int error = 0; - ASSERT(RW_LOCK_HELD(&zvol_state_lock)); + ASSERT(RW_WRITE_HELD(&zvol_state_lock)); ASSERT(MUTEX_HELD(&zv->zv_state_lock)); /* Move to a new hashtable entry. */ zv->zv_hash = zvol_name_hash(newname); hlist_del(&zv->zv_hlink); hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash)); + strlcpy(zv->zv_name, newname, sizeof (zv->zv_name)); + dataset_kstats_rename(&zv->zv_kstat, newname); if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom; - struct g_provider *pp = zsg->zsg_provider; + struct g_provider *pp; struct g_geom *gp; + mutex_exit(&zv->zv_state_lock); g_topology_lock(); + pp = zsg->zsg_provider; + if (pp->private == NULL) { + g_topology_unlock(); + mutex_enter(&zv->zv_state_lock); + return (SET_ERROR(ENXIO)); + } gp = pp->geom; ASSERT3P(gp, !=, NULL); - zsg->zsg_provider = NULL; g_wither_provider(pp, ENXIO); pp = g_new_providerf(gp, "%s/%s", ZVOL_DRIVER, newname); @@ -1278,6 +1285,7 @@ pp->private = zv; zsg->zsg_provider = pp; g_error_provider(pp, 0); + mutex_enter(&zv->zv_state_lock); g_topology_unlock(); } else if (zv->zv_volmode == ZFS_VOLMODE_DEV) { struct zvol_state_dev *zsd = &zv->zv_zso->zso_dev; @@ -1310,8 +1318,6 @@ zsd->zsd_cdev = dev; } } - strlcpy(zv->zv_name, newname, sizeof (zv->zv_name)); - dataset_kstats_rename(&zv->zv_kstat, newname); return (error); } @@ -1400,27 +1406,32 @@ void zvol_os_remove_minor(zvol_state_t *zv) { + struct zvol_state_os *zso = zv->zv_zso; + ASSERT(MUTEX_HELD(&zv->zv_state_lock)); ASSERT0(zv->zv_open_count); ASSERT0(atomic_read(&zv->zv_suspend_ref)); ASSERT(zv->zv_flags & ZVOL_REMOVING); - struct zvol_state_os *zso = zv->zv_zso; - zv->zv_zso = NULL; - if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { struct zvol_state_geom *zsg = &zso->zso_geom; - struct g_provider *pp = zsg->zsg_provider; - atomic_store_ptr(&pp->private, NULL); - mutex_exit(&zv->zv_state_lock); + struct g_provider *pp; + while (zso->zso_opening) + cv_wait(&zv->zv_removing_cv, &zv->zv_state_lock); + zv->zv_zso = NULL; + mutex_exit(&zv->zv_state_lock); g_topology_lock(); + pp = zsg->zsg_provider; + pp->private = NULL; g_wither_geom(pp->geom, ENXIO); g_topology_unlock(); + g_waitidle(curthread); } else if (zv->zv_volmode == ZFS_VOLMODE_DEV) { struct zvol_state_dev *zsd = &zso->zso_dev; struct cdev *dev = zsd->zsd_cdev; + zv->zv_zso = NULL; if (dev != NULL) atomic_store_ptr(&dev->si_drv2, NULL); mutex_exit(&zv->zv_state_lock); @@ -1545,6 +1556,7 @@ g_error_provider(zv->zv_zso->zso_geom.zsg_provider, 0); /* geom was locked inside zvol_alloc() function */ g_topology_unlock(); + g_waitidle(curthread); } out_doi: kmem_free(doi, sizeof (dmu_object_info_t)); @@ -1565,10 +1577,10 @@ zv->zv_volsize = volsize; if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom; - struct g_provider *pp = zsg->zsg_provider; + struct g_provider *pp; g_topology_lock(); - + pp = zsg->zsg_provider; if (pp->private == NULL) { g_topology_unlock(); return (SET_ERROR(ENXIO)); --- sys/contrib/openzfs/module/os/linux/zfs/zvol_os.c.orig +++ sys/contrib/openzfs/module/os/linux/zfs/zvol_os.c @@ -1796,7 +1796,7 @@ { int readonly = get_disk_ro(zv->zv_zso->zvo_disk); - ASSERT(RW_LOCK_HELD(&zvol_state_lock)); + ASSERT(RW_WRITE_HELD(&zvol_state_lock)); ASSERT(MUTEX_HELD(&zv->zv_state_lock)); strlcpy(zv->zv_name, newname, sizeof (zv->zv_name)); --- sys/contrib/openzfs/module/zfs/zvol.c.orig +++ sys/contrib/openzfs/module/zfs/zvol.c @@ -1762,9 +1762,10 @@ if (zvol_inhibit_dev) return; + last_error = 0; oldnamelen = strlen(oldname); - rw_enter(&zvol_state_lock, RW_READER); + rw_enter(&zvol_state_lock, RW_WRITER); for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { zv_next = list_next(&zvol_state_list, zv); @@ -1781,6 +1782,8 @@ zv->zv_name + oldnamelen + 1); error = zvol_os_rename_minor(zv, name); kmem_strfree(name); + } else { + error = 0; } if (error) { last_error = error; @@ -1936,6 +1939,10 @@ uint64_t zsda_value; zprop_source_t zsda_source; zfs_prop_t zsda_prop; + taskqid_t zsda_taskqid; + boolean_t zsda_dispatched; + kmutex_t zsda_lock; + kcondvar_t zsda_cv; } zvol_set_prop_int_arg_t; /* @@ -1966,6 +1973,7 @@ char dsname[ZFS_MAX_DATASET_NAME_LEN]; zvol_task_t *task; uint64_t prop; + taskqid_t id; const char *prop_name = zfs_prop_to_name(zsda->zsda_prop); dsl_dataset_name(ds, dsname); @@ -1984,8 +1992,12 @@ } task->zt_value = prop; strlcpy(task->zt_name1, dsname, sizeof (task->zt_name1)); - (void) taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb, - task, TQ_SLEEP); + id = taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb, task, + TQ_SLEEP); + mutex_enter(&zsda->zsda_lock); + if (id != TASKQID_INVALID && id > zsda->zsda_taskqid) + zsda->zsda_taskqid = id; + mutex_exit(&zsda->zsda_lock); return (0); } @@ -2018,6 +2030,11 @@ dmu_objset_find_dp(dp, dd->dd_object, zvol_set_common_sync_cb, zsda, DS_FIND_CHILDREN); + mutex_enter(&zsda->zsda_lock); + zsda->zsda_dispatched = TRUE; + cv_broadcast(&zsda->zsda_cv); + mutex_exit(&zsda->zsda_lock); + dsl_dir_rele(dd, FTAG); } @@ -2026,14 +2043,38 @@ uint64_t val) { zvol_set_prop_int_arg_t zsda; + spa_t *spa; + int error; zsda.zsda_name = ddname; zsda.zsda_source = source; zsda.zsda_value = val; zsda.zsda_prop = prop; + zsda.zsda_taskqid = TASKQID_INVALID; + zsda.zsda_dispatched = FALSE; + mutex_init(&zsda.zsda_lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&zsda.zsda_cv, NULL, CV_DEFAULT, NULL); - return (dsl_sync_task(ddname, zvol_set_common_check, - zvol_set_common_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE)); + error = spa_open(ddname, &spa, FTAG); + if (error != 0) + goto out; + error = dsl_sync_task(ddname, zvol_set_common_check, + zvol_set_common_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE); + if (error == 0) { + mutex_enter(&zsda.zsda_lock); + while (!zsda.zsda_dispatched) + cv_wait(&zsda.zsda_cv, &zsda.zsda_lock); + mutex_exit(&zsda.zsda_lock); + + if (zsda.zsda_taskqid != TASKQID_INVALID) + taskq_wait_outstanding(spa->spa_zvol_taskq, + zsda.zsda_taskqid); + } + spa_close(spa, FTAG); +out: + cv_destroy(&zsda.zsda_cv); + mutex_destroy(&zsda.zsda_lock); + return (error); } void