Fix various issues with zvols
When performing snapshot renames we could deadlock due to the locking in zvol_rename_minors. In order to avoid this use the same workaround as zvol_open in zvol_rename_minors. Add missing zvol_rename_minors to dsl_dataset_promote_sync. Protect against invalid index into zv_name in zvol_remove_minors. Replace zvol_remove_minor calls with zvol_remove_minors to ensure any potential children are also renamed. Don't fail zvol_create_minors if zvol_create_minor returns EEXIST. Restore the valid pool check in zfs_ioc_destroy_snaps to ensure we don't call zvol_remove_minors when zfs_unmount_snap fails. PR: 193803 MFC after: 1 week Sponsored by: Multiplay
This commit is contained in:
parent
cb08295d5d
commit
99140218aa
@ -2257,6 +2257,9 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
|
||||
dsl_dir_t *odd = NULL;
|
||||
uint64_t oldnext_obj;
|
||||
int64_t delta;
|
||||
#if defined(__FreeBSD__) && defined(_KERNEL)
|
||||
char *oldname, *newname;
|
||||
#endif
|
||||
|
||||
VERIFY0(promote_hold(ddpa, dp, FTAG));
|
||||
hds = ddpa->ddpa_clone;
|
||||
@ -2322,6 +2325,14 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
|
||||
dd->dd_phys->dd_clones, origin_head->ds_object, tx));
|
||||
}
|
||||
|
||||
#if defined(__FreeBSD__) && defined(_KERNEL)
|
||||
/* Take the spa_namespace_lock early so zvol renames don't deadlock. */
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
|
||||
oldname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
|
||||
newname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
|
||||
#endif
|
||||
|
||||
/* move snapshots to this dir */
|
||||
for (snap = list_head(&ddpa->shared_snaps); snap;
|
||||
snap = list_next(&ddpa->shared_snaps, snap)) {
|
||||
@ -2356,6 +2367,12 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
|
||||
VERIFY0(dsl_dir_hold_obj(dp, dd->dd_object,
|
||||
NULL, ds, &ds->ds_dir));
|
||||
|
||||
#if defined(__FreeBSD__) && defined(_KERNEL)
|
||||
dsl_dataset_name(ds, newname);
|
||||
zfsvfs_update_fromname(oldname, newname);
|
||||
zvol_rename_minors(oldname, newname);
|
||||
#endif
|
||||
|
||||
/* move any clone references */
|
||||
if (ds->ds_phys->ds_next_clones_obj &&
|
||||
spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) {
|
||||
@ -2393,6 +2410,12 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
|
||||
ASSERT(!dsl_prop_hascb(ds));
|
||||
}
|
||||
|
||||
#if defined(__FreeBSD__) && defined(_KERNEL)
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
|
||||
kmem_free(newname, MAXPATHLEN);
|
||||
kmem_free(oldname, MAXPATHLEN);
|
||||
#endif
|
||||
/*
|
||||
* Change space accounting.
|
||||
* Note, pa->*usedsnap and dd_used_breakdown[SNAP] will either
|
||||
|
@ -3540,6 +3540,7 @@ zfs_destroy_unmount_origin(const char *fsname)
|
||||
static int
|
||||
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
int error, poollen;
|
||||
nvlist_t *snaps;
|
||||
nvpair_t *pair;
|
||||
boolean_t defer;
|
||||
@ -3548,13 +3549,24 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
return (SET_ERROR(EINVAL));
|
||||
defer = nvlist_exists(innvl, "defer");
|
||||
|
||||
poollen = strlen(poolname);
|
||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(snaps, pair)) {
|
||||
const char *name = nvpair_name(pair);
|
||||
|
||||
(void) zfs_unmount_snap(name);
|
||||
/*
|
||||
* The snap must be in the specified pool to prevent the
|
||||
* invalid removal of zvol minors below.
|
||||
*/
|
||||
if (strncmp(name, poolname, poollen) != 0 ||
|
||||
(name[poollen] != '/' && name[poollen] != '@'))
|
||||
return (SET_ERROR(EXDEV));
|
||||
|
||||
error = zfs_unmount_snap(name);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
#if defined(__FreeBSD__)
|
||||
(void) zvol_remove_minor(name);
|
||||
zvol_remove_minors(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3679,7 +3691,11 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
|
||||
else
|
||||
err = dsl_destroy_head(zc->zc_name);
|
||||
if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
|
||||
#ifdef __FreeBSD__
|
||||
zvol_remove_minors(zc->zc_name);
|
||||
#else
|
||||
(void) zvol_remove_minor(zc->zc_name);
|
||||
#endif
|
||||
return (err);
|
||||
}
|
||||
|
||||
|
@ -882,7 +882,8 @@ zvol_remove_minors(const char *name)
|
||||
LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) {
|
||||
if (strcmp(zv->zv_name, name) == 0 ||
|
||||
(strncmp(zv->zv_name, name, namelen) == 0 &&
|
||||
zv->zv_name[namelen] == '/')) {
|
||||
strlen(zv->zv_name) > namelen && (zv->zv_name[namelen] == '/' ||
|
||||
zv->zv_name[namelen] == '@'))) {
|
||||
(void) zvol_remove_zv(zv);
|
||||
}
|
||||
}
|
||||
@ -2570,9 +2571,10 @@ zvol_create_minors(const char *name)
|
||||
if (dmu_objset_type(os) == DMU_OST_ZVOL) {
|
||||
dsl_dataset_long_hold(os->os_dsl_dataset, FTAG);
|
||||
dsl_pool_rele(dmu_objset_pool(os), FTAG);
|
||||
if ((error = zvol_create_minor(name)) == 0)
|
||||
error = zvol_create_minor(name);
|
||||
if (error == 0 || error == EEXIST) {
|
||||
error = zvol_create_snapshots(os, name);
|
||||
else {
|
||||
} else {
|
||||
printf("ZFS WARNING: Unable to create ZVOL %s (error=%d).\n",
|
||||
name, error);
|
||||
}
|
||||
@ -2673,12 +2675,17 @@ zvol_rename_minors(const char *oldname, const char *newname)
|
||||
size_t oldnamelen, newnamelen;
|
||||
zvol_state_t *zv;
|
||||
char *namebuf;
|
||||
boolean_t locked = B_FALSE;
|
||||
|
||||
oldnamelen = strlen(oldname);
|
||||
newnamelen = strlen(newname);
|
||||
|
||||
DROP_GIANT();
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
/* See comment in zvol_open(). */
|
||||
if (!MUTEX_HELD(&spa_namespace_lock)) {
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
locked = B_TRUE;
|
||||
}
|
||||
|
||||
LIST_FOREACH(zv, &all_zvols, zv_links) {
|
||||
if (strcmp(zv->zv_name, oldname) == 0) {
|
||||
@ -2693,7 +2700,8 @@ zvol_rename_minors(const char *oldname, const char *newname)
|
||||
}
|
||||
}
|
||||
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
if (locked)
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
PICKUP_GIANT();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user