aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2014-01-13 14:27:33 -0800
committerBrian Behlendorf <[email protected]>2014-01-14 14:46:12 -0800
commit35d3e32274ff05d9b080ea0a77ade1f9c9d7bafc (patch)
treeb3c7209d05a4bf7f54911d7b564501ddfc465a42
parent0f62f3f9abc4bfa0bcafee9bfa3d55e91dcb371d (diff)
Use long holds in zvol_set_volsize()
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 #2039
-rw-r--r--module/zfs/zvol.c122
1 files changed, 67 insertions, 55 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c
index e6980271d..fa5c7eb4e 100644
--- a/module/zfs/zvol.c
+++ b/module/zfs/zvol.c
@@ -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.
*/
@@ -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;
@@ -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);
}
@@ -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);
@@ -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);
}
/*