diff options
author | ck153898 <none@none> | 2008-02-22 11:48:10 -0800 |
---|---|---|
committer | ck153898 <none@none> | 2008-02-22 11:48:10 -0800 |
commit | 8bf40bf00a7564d0ccb4a33906734188cb2d1e9b (patch) | |
tree | a7b833703f8b6962d2f1afbf60564dd9d57cd590 /usr/src/uts/common/fs/zfs/zfs_ctldir.c | |
parent | b12d9c4ee30871ca7d3602e62c9e3dde6b64f4ca (diff) | |
download | illumos-gate-8bf40bf00a7564d0ccb4a33906734188cb2d1e9b.tar.gz |
6564619 panic after a filesystem with snapshots that have files open was destroyed or umounted
6632751 Different in use detect between create a snapshot by 'mkdir' vs 'zfs snapshot'
Diffstat (limited to 'usr/src/uts/common/fs/zfs/zfs_ctldir.c')
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_ctldir.c | 132 |
1 files changed, 65 insertions, 67 deletions
diff --git a/usr/src/uts/common/fs/zfs/zfs_ctldir.c b/usr/src/uts/common/fs/zfs/zfs_ctldir.c index 6a3bc4a4c2..98a3c67245 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c +++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -62,7 +62,8 @@ * (ie: snapshots) are ZFS nodes and have their own unique vfs_t. * However, vnodes within these mounted on file systems have their v_vfsp * fields set to the head filesystem to make NFS happy (see - * zfsctl_snapdir_lookup()). + * zfsctl_snapdir_lookup()). We VFS_HOLD the head filesystem's vfs_t + * so that it cannot be freed until all snapshots have been unmounted. */ #include <fs/fs_subr.h> @@ -76,6 +77,18 @@ #include <sys/dsl_deleg.h> #include <sys/mount.h> +typedef struct zfsctl_node { + gfs_dir_t zc_gfs_private; + uint64_t zc_id; + timestruc_t zc_cmtime; /* ctime and mtime, always the same */ +} zfsctl_node_t; + +typedef struct zfsctl_snapdir { + zfsctl_node_t sd_node; + kmutex_t sd_lock; + avl_tree_t sd_snaps; +} zfsctl_snapdir_t; + typedef struct { char *se_name; vnode_t *se_root; @@ -107,6 +120,7 @@ static const fs_operation_def_t zfsctl_tops_snapshot[]; static vnode_t *zfsctl_mknode_snapdir(vnode_t *); static vnode_t *zfsctl_snapshot_mknode(vnode_t *, uint64_t objset); +static int zfsctl_unmount_snap(zfs_snapentry_t *, int, cred_t *); static gfs_opsvec_t zfsctl_opsvec[] = { { ".zfs", zfsctl_tops_root, &zfsctl_ops_root }, @@ -115,18 +129,6 @@ static gfs_opsvec_t zfsctl_opsvec[] = { { NULL } }; -typedef struct zfsctl_node { - gfs_dir_t zc_gfs_private; - uint64_t zc_id; - timestruc_t zc_cmtime; /* ctime and mtime, always the same */ -} zfsctl_node_t; - -typedef struct zfsctl_snapdir { - zfsctl_node_t sd_node; - kmutex_t sd_lock; - avl_tree_t sd_snaps; -} zfsctl_snapdir_t; - /* * Root directory elements. We have only a single static entry, 'snapshot'. */ @@ -432,43 +434,39 @@ zfsctl_snapshot_zname(vnode_t *vp, const char *name, int len, char *zname) return (0); } -int -zfsctl_unmount_snap(vnode_t *dvp, const char *name, int force, cred_t *cr) +static int +zfsctl_unmount_snap(zfs_snapentry_t *sep, int fflags, cred_t *cr) { - zfsctl_snapdir_t *sdp = dvp->v_data; - zfs_snapentry_t search, *sep; - avl_index_t where; - int err; - - ASSERT(MUTEX_HELD(&sdp->sd_lock)); - - search.se_name = (char *)name; - if ((sep = avl_find(&sdp->sd_snaps, &search, &where)) == NULL) - return (ENOENT); + vnode_t *svp = sep->se_root; + int error; - ASSERT(vn_ismntpt(sep->se_root)); + ASSERT(vn_ismntpt(svp)); /* this will be dropped by dounmount() */ - if ((err = vn_vfswlock(sep->se_root)) != 0) - return (err); + if ((error = vn_vfswlock(svp)) != 0) + return (error); - VN_HOLD(sep->se_root); - err = dounmount(vn_mountedvfs(sep->se_root), force, kcred); - if (err) { - VN_RELE(sep->se_root); - return (err); + VN_HOLD(svp); + error = dounmount(vn_mountedvfs(svp), fflags, cr); + if (error) { + VN_RELE(svp); + return (error); } - ASSERT(sep->se_root->v_count == 1); - gfs_vop_inactive(sep->se_root, cr, NULL); + VFS_RELE(svp->v_vfsp); + /* + * We can't use VN_RELE(), as that will try to invoke + * zfsctl_snapdir_inactive(), which would cause us to destroy + * the sd_lock mutex held by our caller. + */ + ASSERT(svp->v_count == 1); + gfs_vop_inactive(svp, cr, NULL); - avl_remove(&sdp->sd_snaps, sep); kmem_free(sep->se_name, strlen(sep->se_name) + 1); kmem_free(sep, sizeof (zfs_snapentry_t)); return (0); } - static void zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm) { @@ -575,6 +573,8 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr, caller_context_t *ct, int flags) { zfsctl_snapdir_t *sdp = dvp->v_data; + zfs_snapentry_t *sep; + zfs_snapentry_t search; char snapname[MAXNAMELEN]; int err; @@ -587,14 +587,19 @@ zfsctl_snapdir_remove(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr, mutex_enter(&sdp->sd_lock); - err = zfsctl_unmount_snap(dvp, name, MS_FORCE, cr); - if (err) { - mutex_exit(&sdp->sd_lock); - return (err); + search.se_name = name; + sep = avl_find(&sdp->sd_snaps, &search, NULL); + if (sep) { + avl_remove(&sdp->sd_snaps, sep); + err = zfsctl_unmount_snap(sep, MS_FORCE, cr); + if (err) + avl_add(&sdp->sd_snaps, sep); + else + err = dmu_objset_destroy(snapname); + } else { + err = ENOENT; } - err = dmu_objset_destroy(snapname); - mutex_exit(&sdp->sd_lock); return (err); @@ -692,6 +697,13 @@ zfsctl_snapdir_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, * try to remount it. */ goto domount; + } else { + /* + * VROOT was set during the traverse call. We need + * to clear it since we're pretending to be part + * of our parent's vfs. + */ + (*vpp)->v_flag &= ~VROOT; } mutex_exit(&sdp->sd_lock); ZFS_EXIT(zfsvfs); @@ -914,6 +926,7 @@ zfsctl_snapshot_mknode(vnode_t *pvp, uint64_t objset) zfsctl_ops_snapshot, NULL, NULL, MAXNAMELEN, NULL, NULL); zcp = vp->v_data; zcp->zc_id = objset; + VFS_HOLD(vp->v_vfsp); return (vp); } @@ -952,6 +965,7 @@ zfsctl_snapshot_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) mutex_exit(&sdp->sd_lock); VN_RELE(dvp); + VFS_RELE(vp->v_vfsp); /* * Dispose of the vnode for the snapshot mount point. @@ -1037,7 +1051,7 @@ int zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) { zfsvfs_t *zfsvfs = vfsp->vfs_data; - vnode_t *dvp, *svp; + vnode_t *dvp; zfsctl_snapdir_t *sdp; zfs_snapentry_t *sep, *next; int error; @@ -1053,7 +1067,6 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) sep = avl_first(&sdp->sd_snaps); while (sep != NULL) { - svp = sep->se_root; next = AVL_NEXT(&sdp->sd_snaps, sep); /* @@ -1061,32 +1074,17 @@ zfsctl_umount_snapshots(vfs_t *vfsp, int fflags, cred_t *cr) * have just been unmounted by somebody else, and * will be cleaned up by zfsctl_snapdir_inactive(). */ - if (vn_ismntpt(svp)) { - if ((error = vn_vfswlock(svp)) != 0) - goto out; - - VN_HOLD(svp); - error = dounmount(vn_mountedvfs(svp), fflags, cr); + if (vn_ismntpt(sep->se_root)) { + avl_remove(&sdp->sd_snaps, sep); + error = zfsctl_unmount_snap(sep, fflags, cr); if (error) { - VN_RELE(svp); - goto out; + avl_add(&sdp->sd_snaps, sep); + break; } - - avl_remove(&sdp->sd_snaps, sep); - kmem_free(sep->se_name, strlen(sep->se_name) + 1); - kmem_free(sep, sizeof (zfs_snapentry_t)); - - /* - * We can't use VN_RELE(), as that will try to - * invoke zfsctl_snapdir_inactive(), and that - * would lead to an attempt to re-grab the sd_lock. - */ - ASSERT3U(svp->v_count, ==, 1); - gfs_vop_inactive(svp, cr, NULL); } sep = next; } -out: + mutex_exit(&sdp->sd_lock); VN_RELE(dvp); |