From fd4f76160cb34539f875781fe7f2dea4b937ace5 Mon Sep 17 00:00:00 2001 From: Tim Chase Date: Wed, 6 Nov 2013 23:55:18 -0600 Subject: [PATCH] Handle concurrent snapshot automounts failing due to EBUSY. In the current snapshot automount implementation, it is possible for multiple mounts to attempted concurrently. Only one of the mounts will succeed and the other will fail. The failed mounts will cause an EREMOTE to be propagated back to the application. This commit works around the problem by adding a new exit status, MOUNT_BUSY to the mount.zfs program which is used when the underlying mount(2) call returns EBUSY. The zfs code detects this condition and treats it as if the mount had succeeded. Signed-off-by: Brian Behlendorf Closes #1819 --- cmd/mount_zfs/mount_zfs.c | 2 +- lib/libspl/include/sys/mntent.h | 1 + lib/libzfs/libzfs_mount.c | 2 ++ module/zfs/zfs_ctldir.c | 14 +++++++++++--- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd/mount_zfs/mount_zfs.c b/cmd/mount_zfs/mount_zfs.c index 4db33ed69..83b57f4e9 100644 --- a/cmd/mount_zfs/mount_zfs.c +++ b/cmd/mount_zfs/mount_zfs.c @@ -528,7 +528,7 @@ main(int argc, char **argv) case EBUSY: (void) fprintf(stderr, gettext("filesystem " "'%s' is already mounted\n"), dataset); - return (MOUNT_SYSERR); + return (MOUNT_BUSY); default: (void) fprintf(stderr, gettext("filesystem " "'%s' can not be mounted due to error " diff --git a/lib/libspl/include/sys/mntent.h b/lib/libspl/include/sys/mntent.h index 8fad65b56..736c3f866 100644 --- a/lib/libspl/include/sys/mntent.h +++ b/lib/libspl/include/sys/mntent.h @@ -39,6 +39,7 @@ #define MOUNT_FILEIO 0x10 /* Error updating/locking /etc/mtab */ #define MOUNT_FAIL 0x20 /* Mount failed */ #define MOUNT_SOMEOK 0x40 /* At least on mount succeeded */ +#define MOUNT_BUSY 0x80 /* Mount failed due to EBUSY */ #define MNTOPT_ASYNC "async" /* all I/O is asynchronous */ #define MNTOPT_ATIME "atime" /* update atime for files */ diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index bded1f001..68e4ef4de 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -292,6 +292,8 @@ do_mount(const char *src, const char *mntpt, char *opts) return EINTR; if (rc & MOUNT_SOFTWARE) return EPIPE; + if (rc & MOUNT_BUSY) + return EBUSY; if (rc & MOUNT_SYSERR) return EAGAIN; if (rc & MOUNT_USAGE) diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c index c08e9dd9b..5bea0b6c9 100644 --- a/module/zfs/zfs_ctldir.c +++ b/module/zfs/zfs_ctldir.c @@ -692,7 +692,7 @@ zfsctl_snapdir_inactive(struct inode *ip) * best effort. In the case where it does fail, perhaps because * it's in use, the unmount will fail harmlessly. */ -#define SET_UNMOUNT_CMD \ +#define SET_UNMOUNT_CMD \ "exec 0/dev/null " \ " 2>/dev/null; " \ @@ -801,7 +801,9 @@ zfsctl_unmount_snapshots(zfs_sb_t *zsb, int flags, int *count) return ((*count > 0) ? EEXIST : 0); } -#define SET_MOUNT_CMD \ +#define MOUNT_BUSY 0x80 /* Mount failed due to EBUSY (from mntent.h) */ + +#define SET_MOUNT_CMD \ "exec 0/dev/null " \ " 2>/dev/null; " \ @@ -839,17 +841,23 @@ zfsctl_mount_snapshot(struct path *path, int flags) * function is marked GPL-only and cannot be used. On error we * careful to log the real error to the console and return EISDIR * to safely abort the automount. This should be very rare. + * + * If the user mode helper happens to return EBUSY, a concurrent + * mount is already in progress in which case the error is ignored. + * Take note that if the program was executed successfully the return + * value from call_usermodehelper() will be (exitcode << 8 + signal). */ 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) { + if (error && !(error & MOUNT_BUSY << 8)) { printk("ZFS: Unable to automount %s at %s: %d\n", full_name, full_path, error); error = SET_ERROR(EISDIR); goto error; } + error = 0; mutex_enter(&zsb->z_ctldir_lock); /*