Skip to content

Commit

Permalink
Use long holds in zvol_set_volsize()
Browse files Browse the repository at this point in the history
Under Linux the zvol_set_volsize() function was originally written
to use dmu_objset_hold()/dmu_objset_rele().  Subsequently, the
dmu_objset_own()/dmu_objset_disown() interfaces were added but
the ZVOL code wasn't updated to take advantage of them.

This was never an issue but after the dsl_pool_config changes
the code now takes the config lock twice.  The cleanest solution
is to shift to using dmu_objset_own() which takes a long hold
on the dataset and does not hold the dsl pool lock.

This patch also slightly restructures the existing code such
that it more closely resembles the upstream Illumos code.

Signed-off-by: Ned Bass <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes openzfs#2039
  • Loading branch information
behlendorf committed Jan 14, 2014
1 parent 0f62f3f commit 35d3e32
Showing 1 changed file with 67 additions and 55 deletions.
122 changes: 67 additions & 55 deletions module/zfs/zvol.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,31 @@ zvol_get_stats(objset_t *os, nvlist_t *nv)
return (SET_ERROR(error));
}

static void
zvol_size_changed(zvol_state_t *zv, uint64_t volsize)
{
struct block_device *bdev;

bdev = bdget_disk(zv->zv_disk, 0);
if (bdev == NULL)
return;
/*
* 2.6.28 API change
* Added check_disk_size_change() helper function.
*/
#ifdef HAVE_CHECK_DISK_SIZE_CHANGE
set_capacity(zv->zv_disk, volsize >> 9);
zv->zv_volsize = volsize;
check_disk_size_change(zv->zv_disk, bdev);
#else
zv->zv_volsize = volsize;
zv->zv_changed = 1;
(void) check_disk_change(bdev);
#endif /* HAVE_CHECK_DISK_SIZE_CHANGE */

bdput(bdev);
}

/*
* Sanity check volume size.
*/
Expand All @@ -254,9 +279,8 @@ zvol_check_volsize(uint64_t volsize, uint64_t blocksize)
* Ensure the zap is flushed then inform the VFS of the capacity change.
*/
static int
zvol_update_volsize(zvol_state_t *zv, uint64_t volsize, objset_t *os)
zvol_update_volsize(uint64_t volsize, objset_t *os)
{
struct block_device *bdev;
dmu_tx_t *tx;
int error;

Expand All @@ -274,32 +298,23 @@ zvol_update_volsize(zvol_state_t *zv, uint64_t volsize, objset_t *os)
&volsize, tx);
dmu_tx_commit(tx);

if (error)
return (SET_ERROR(error));
if (error == 0)
error = dmu_free_long_range(os,
ZVOL_OBJ, volsize, DMU_OBJECT_END);

error = dmu_free_long_range(os,
ZVOL_OBJ, volsize, DMU_OBJECT_END);
if (error)
return (SET_ERROR(error));
return (error);
}

bdev = bdget_disk(zv->zv_disk, 0);
if (!bdev)
return (SET_ERROR(EIO));
/*
* 2.6.28 API change
* Added check_disk_size_change() helper function.
*/
#ifdef HAVE_CHECK_DISK_SIZE_CHANGE
set_capacity(zv->zv_disk, volsize >> 9);
zv->zv_volsize = volsize;
check_disk_size_change(zv->zv_disk, bdev);
#else
zv->zv_volsize = volsize;
zv->zv_changed = 1;
(void) check_disk_change(bdev);
#endif /* HAVE_CHECK_DISK_SIZE_CHANGE */
static int
zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize)
{
zvol_size_changed(zv, volsize);

bdput(bdev);
/*
* We should post a event here describing the expansion. However,
* the zfs_ereport_post() interface doesn't nicely support posting
* events for zvols, it assumes events relate to vdevs or zios.
*/

return (0);
}
Expand All @@ -310,11 +325,12 @@ zvol_update_volsize(zvol_state_t *zv, uint64_t volsize, objset_t *os)
int
zvol_set_volsize(const char *name, uint64_t volsize)
{
zvol_state_t *zv;
dmu_object_info_t *doi;
zvol_state_t *zv = NULL;
objset_t *os = NULL;
uint64_t readonly;
int error;
dmu_object_info_t *doi;
uint64_t readonly;
boolean_t owned = B_FALSE;

error = dsl_prop_get_integer(name,
zfs_prop_to_name(ZFS_PROP_READONLY), &readonly, NULL);
Expand All @@ -324,44 +340,40 @@ zvol_set_volsize(const char *name, uint64_t volsize)
return (SET_ERROR(EROFS));

mutex_enter(&zvol_state_lock);

zv = zvol_find_by_name(name);
if (zv == NULL) {
error = SET_ERROR(ENXIO);
goto out;

if (zv == NULL || zv->zv_objset == NULL) {
if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE,
FTAG, &os)) != 0) {
mutex_exit(&zvol_state_lock);
return (SET_ERROR(error));
}
owned = B_TRUE;
if (zv != NULL)
zv->zv_objset = os;
} else {
os = zv->zv_objset;
}

doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP);

error = dmu_objset_hold(name, FTAG, &os);
if (error)
goto out_doi;

if ((error = dmu_object_info(os, ZVOL_OBJ, doi)) ||
(error = zvol_check_volsize(volsize, doi->doi_data_block_size)))
goto out_doi;

VERIFY(dsl_prop_get_integer(name, "readonly", &readonly, NULL) == 0);
if (readonly) {
error = SET_ERROR(EROFS);
goto out_doi;
}

if (zv->zv_flags & ZVOL_RDONLY) {
error = SET_ERROR(EROFS);
goto out_doi;
}
goto out;

error = zvol_update_volsize(zv, volsize, os);
out_doi:
error = zvol_update_volsize(volsize, os);
kmem_free(doi, sizeof (dmu_object_info_t));
out:
if (os)
dmu_objset_rele(os, FTAG);

if (error == 0 && zv != NULL)
error = zvol_update_live_volsize(zv, volsize);
out:
if (owned) {
dmu_objset_disown(os, FTAG);
if (zv != NULL)
zv->zv_objset = NULL;
}
mutex_exit(&zvol_state_lock);

return (SET_ERROR(error));
return (error);
}

/*
Expand Down

0 comments on commit 35d3e32

Please sign in to comment.