aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/zfs_ctldir.c49
1 files changed, 32 insertions, 17 deletions
diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c
index b70eb6623..745a467b7 100644
--- a/module/zfs/zfs_ctldir.c
+++ b/module/zfs/zfs_ctldir.c
@@ -1051,6 +1051,7 @@ zfsctl_snapshot_mount(struct path *path, int flags)
char *argv[] = { "/bin/sh", "-c", NULL, NULL };
char *envp[] = { NULL };
int error;
+ struct path spath;
if (ip == NULL)
return (EISDIR);
@@ -1095,10 +1096,22 @@ zfsctl_snapshot_mount(struct path *path, int flags)
argv[2] = kmem_asprintf(SET_MOUNT_CMD, full_name, full_path);
error = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
strfree(argv[2]);
- if (error && !(error & MOUNT_BUSY << 8)) {
- cmn_err(CE_WARN, "Unable to automount %s/%s: %d",
- full_path, full_name, error);
- error = SET_ERROR(EISDIR);
+ if (error) {
+ if (!(error & MOUNT_BUSY << 8)) {
+ cmn_err(CE_WARN, "Unable to automount %s/%s: %d",
+ full_path, full_name, error);
+ error = SET_ERROR(EISDIR);
+ } else {
+ /*
+ * EBUSY, this could mean a concurrent mount, or the
+ * snapshot has already been mounted at completely
+ * different place. We return 0 so VFS will retry. For
+ * the latter case the VFS will retry several times
+ * and return ELOOP, which is probably not a very good
+ * behavior.
+ */
+ error = 0;
+ }
goto error;
}
@@ -1106,20 +1119,22 @@ zfsctl_snapshot_mount(struct path *path, int flags)
* Follow down in to the mounted snapshot and set MNT_SHRINKABLE
* to identify this as an automounted filesystem.
*/
- zpl_follow_down_one(path);
- snap_zsb = ITOZSB(path->dentry->d_inode);
- snap_zsb->z_parent = zsb;
- dentry = path->dentry;
- path->mnt->mnt_flags |= MNT_SHRINKABLE;
- zpl_follow_up(path);
- error = 0;
+ spath = *path;
+ path_get(&spath);
+ if (zpl_follow_down_one(&spath)) {
+ snap_zsb = ITOZSB(spath.dentry->d_inode);
+ snap_zsb->z_parent = zsb;
+ dentry = spath.dentry;
+ spath.mnt->mnt_flags |= MNT_SHRINKABLE;
- mutex_enter(&zfs_snapshot_lock);
- se = zfsctl_snapshot_alloc(full_name, full_path,
- dmu_objset_id(snap_zsb->z_os), dentry);
- zfsctl_snapshot_add(se);
- zfsctl_snapshot_unmount_delay_impl(se, zfs_expire_snapshot);
- mutex_exit(&zfs_snapshot_lock);
+ mutex_enter(&zfs_snapshot_lock);
+ se = zfsctl_snapshot_alloc(full_name, full_path,
+ dmu_objset_id(snap_zsb->z_os), dentry);
+ zfsctl_snapshot_add(se);
+ zfsctl_snapshot_unmount_delay_impl(se, zfs_expire_snapshot);
+ mutex_exit(&zfs_snapshot_lock);
+ }
+ path_put(&spath);
error:
kmem_free(full_name, MAXNAMELEN);
kmem_free(full_path, MAXPATHLEN);