zvol_os: Fix handling of zvol private data

zvol private data is supposed to be nulled by zvol_clear_private before
zvol_free is called as an indicator that the zvol is going away.

Implement zvol_clear_private for volmode=dev.

Assert that zvol_clear_private has been called before zvol_free.

Check that zvol_clear_private has not been called when updating
volsize.  If it has, fail with ENXIO.

Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Matt Macy <mmacy@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes #11117
This commit is contained in:
Ryan Moeller 2020-10-21 18:09:17 +00:00 committed by Brian Behlendorf
parent 0c270bb6c4
commit aaeffd09bf
1 changed files with 18 additions and 9 deletions

View File

@ -424,7 +424,6 @@ zvol_geom_destroy(zvol_state_t *zv)
VERIFY(zsg->zsg_state == ZVOL_GEOM_RUNNING); VERIFY(zsg->zsg_state == ZVOL_GEOM_RUNNING);
mutex_exit(&zv->zv_state_lock); mutex_exit(&zv->zv_state_lock);
zsg->zsg_provider = NULL; zsg->zsg_provider = NULL;
pp->private = NULL;
g_wither_geom(pp->geom, ENXIO); g_wither_geom(pp->geom, ENXIO);
} }
@ -1216,6 +1215,9 @@ zvol_free(zvol_state_t *zv)
if (zv->zv_zso->zso_volmode == ZFS_VOLMODE_GEOM) { if (zv->zv_zso->zso_volmode == ZFS_VOLMODE_GEOM) {
struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom; struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom;
struct g_provider *pp __maybe_unused = zsg->zsg_provider;
ASSERT3P(pp->private, ==, NULL);
g_topology_lock(); g_topology_lock();
zvol_geom_destroy(zv); zvol_geom_destroy(zv);
@ -1225,8 +1227,9 @@ zvol_free(zvol_state_t *zv)
struct zvol_state_dev *zsd = &zv->zv_zso->zso_dev; struct zvol_state_dev *zsd = &zv->zv_zso->zso_dev;
struct cdev *dev = zsd->zsd_cdev; struct cdev *dev = zsd->zsd_cdev;
if (dev != NULL) ASSERT3P(dev->si_drv2, ==, NULL);
destroy_dev(dev);
destroy_dev(dev);
} }
mutex_destroy(&zv->zv_state_lock); mutex_destroy(&zv->zv_state_lock);
@ -1384,7 +1387,7 @@ zvol_clear_private(zvol_state_t *zv)
struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom; struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom;
struct g_provider *pp = zsg->zsg_provider; struct g_provider *pp = zsg->zsg_provider;
if (pp == NULL) /* XXX when? */ if (pp->private == NULL) /* already cleared */
return; return;
mtx_lock(&zsg->zsg_queue_mtx); mtx_lock(&zsg->zsg_queue_mtx);
@ -1392,11 +1395,15 @@ zvol_clear_private(zvol_state_t *zv)
pp->private = NULL; pp->private = NULL;
wakeup_one(&zsg->zsg_queue); wakeup_one(&zsg->zsg_queue);
while (zsg->zsg_state != ZVOL_GEOM_RUNNING) while (zsg->zsg_state != ZVOL_GEOM_RUNNING)
msleep(&zsg->zsg_state, msleep(&zsg->zsg_state, &zsg->zsg_queue_mtx,
&zsg->zsg_queue_mtx,
0, "zvol:w", 0); 0, "zvol:w", 0);
mtx_unlock(&zsg->zsg_queue_mtx); mtx_unlock(&zsg->zsg_queue_mtx);
ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock)); ASSERT(!RW_LOCK_HELD(&zv->zv_suspend_lock));
} else if (zv->zv_zso->zso_volmode == ZFS_VOLMODE_DEV) {
struct zvol_state_dev *zsd = &zv->zv_zso->zso_dev;
struct cdev *dev = zsd->zsd_cdev;
dev->si_drv2 = NULL;
} }
} }
@ -1408,11 +1415,13 @@ zvol_update_volsize(zvol_state_t *zv, uint64_t volsize)
struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom; struct zvol_state_geom *zsg = &zv->zv_zso->zso_geom;
struct g_provider *pp = zsg->zsg_provider; struct g_provider *pp = zsg->zsg_provider;
if (pp == NULL) /* XXX when? */
return (0);
g_topology_lock(); g_topology_lock();
if (pp->private == NULL) {
g_topology_unlock();
return (SET_ERROR(ENXIO));
}
/* /*
* Do not invoke resize event when initial size was zero. * Do not invoke resize event when initial size was zero.
* ZVOL initializes the size on first open, this is not * ZVOL initializes the size on first open, this is not