-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow mounting datasets more than once #7207
Conversation
return 0 | ||
} | ||
|
||
log_assert "Verify recovery from a lazy unmount is possilbe" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: possible
.
|
||
log_must pkill -P $PPID tail | ||
|
||
log_assert "Recovering from a lazy unmount is possilbe" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log_pass
?
|
||
log_must pkill -P $PPID tail | ||
|
||
log_assert "Recovering from a lazy unmount is possilbe" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: possible
.
@@ -98,7 +98,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [ | |||
ZFS_AC_KERNEL_TRUNCATE_SETSIZE | |||
ZFS_AC_KERNEL_6ARGS_SECURITY_INODE_INIT_SECURITY | |||
ZFS_AC_KERNEL_CALLBACK_SECURITY_INODE_INIT_SECURITY | |||
ZFS_AC_KERNEL_MOUNT_NODEV | |||
ZFS_AC_KERNEL_FST_MOUNT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Looks like the .m4
file defining this is missing.
Is this also going to allow multiple normal mounts of a dataset in a single namespace?
|
include/linux/vfs_compat.h
Outdated
#define SB_SILENT MS_SILENT | ||
#endif | ||
|
||
#ifndef SB_ACTIVE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should be a tab.
module/zfs/zpl_super.c
Outdated
if (IS_ERR(s)) | ||
return (ERR_CAST(s)); | ||
|
||
if (!s->s_root) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s->s_root == NULL
.
log_onexit cleanup | ||
|
||
# 1. Create fs | ||
TESTFS="$TESTPOOL/multi-mount-test" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than redefine TESTFS
, which is already defined in default.cfg
, how about using the existing definitition and $TESTPOOL/$TESTFS
. Or alternately TESTDS=$TESTPOOL/multi-mount-test
to avoid the reusing the variable name which could otherwise be confusing.
|
||
# 2. Create and hold open file in filesystem | ||
FILENAME="$MNTPFS/file" | ||
log_must dd if=/dev/urandom of=$FILENAME bs=128k count=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mkfile
is perhaps a simpler alternative here.
TAILPPID=$! | ||
|
||
# 3. Lazy umount | ||
log_must umount -l $MNTPFS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also check that it was removed from the namespace.
# 5. Verify multiple mounts of the same dataset are possible | ||
log_must mkdir $MNTFS2 | ||
log_must mount -t zfs zfsutil $TESTFS $MNTPFS2 | ||
log_must mkdir $MNTFS2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean $MNTFS3
here.
|
||
# 5. Verify multiple mounts of the same dataset are possible | ||
log_must mkdir $MNTFS2 | ||
log_must mount -t zfs zfsutil $TESTFS $MNTPFS2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be -o zfsutil
.
@vozhyk- yes. |
12843f1
to
93ba799
Compare
I think I've addressed all of the review comments now |
module/zfs/zpl_super.c
Outdated
|
||
static struct super_block * | ||
zpl_mount_impl(struct file_system_type *fs_type, int flags, | ||
zfs_mnt_t zm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this fits on the previous line, and you should pass this as a zfs_mnt_t *
.
module/zfs/zpl_super.c
Outdated
} | ||
#else | ||
static int | ||
zpl_get_sb(struct file_system_type *fs_type, int flags, | ||
const char *osname, void *data, struct vfsmount *mnt) | ||
{ | ||
zfs_mnt_t zm = { .mnt_osname = osname, .mnt_data = data }; | ||
|
||
return (get_sb_nodev(fs_type, flags, &zm, zpl_fill_super, mnt)); | ||
struct super_block *sb = zpl_mount_impl(zm); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be, zpl_mount_impl(fs_type, flags, &zm)
.
690a924
to
058ed29
Compare
thanks for taking another look, I've updated the patch with the latest feedback and I've also added the "bind mount, then rename" test from Seth's original PR |
module/zfs/zpl_super.c
Outdated
objset_t *os; | ||
int err; | ||
|
||
err = dmu_objset_hold(zm.mnt_osname, FTAG, &os); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/zm./zm->/
module/zfs/zpl_super.c
Outdated
return (ERR_CAST(s)); | ||
|
||
if (s->s_root == NULL) { | ||
err = zpl_fill_super(s, &zm, flags & SB_SILENT ? 1 : 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/&zm/zm/
I think I've addressed all the build issues, not sure what's going on w/ the tests though. |
@alek-p my guess is they all panicked while running the
|
no panic, but |
module/zfs/zpl_super.c
Outdated
static struct dentry * | ||
zpl_mount(struct file_system_type *fs_type, int flags, | ||
const char *osname, void *data) | ||
{ | ||
zfs_mnt_t zm = { .mnt_osname = osname, .mnt_data = data }; | ||
|
||
return (mount_nodev(fs_type, flags, &zm, zpl_fill_super)); | ||
struct super_block *sb = zpl_mount_impl(fs_type, flags, &zm); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There needs to be an:
if (IS_ERR(sb))
return (PTR_ERR(sb));
check here to avoid NULL dereferencing sb->s_root
in the next line. Otherwise this can happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for all the help @behlendorf
I've added something similar here and in zpl_test_super()
. I'm now seeing test failures in the cli_root
group, looking into those.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I think this is correct. I am not an expert on the Linux kernel's mounting code, but I understand the fix and this seems reasonable. Just a few things I would like to see cleaned up before it gets merged.
module/zfs/zpl_super.c
Outdated
} | ||
s->s_flags |= SB_ACTIVE; | ||
} else if ((flags ^ s->s_flags) & SB_RDONLY) { | ||
return (ERR_PTR(-EBUSY)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaks locked super?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, yes we're missing a call to deactivate_locked_super
here.
log_must zfs rename $TESTDS $RENAMEFS | ||
log_must zfs rename $RENAMEFS $TESTDS | ||
|
||
log_pass "Multiple mounts are possible" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In sections 5 and 6 I would want a check for the created files as well (just to be sure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
module/zfs/zpl_super.c
Outdated
} | ||
s->s_flags |= SB_ACTIVE; | ||
} else if ((flags ^ s->s_flags) & SB_RDONLY) { | ||
return (ERR_PTR(-EBUSY)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, yes we're missing a call to deactivate_locked_super
here.
log_must zfs rename $TESTDS $RENAMEFS | ||
log_must zfs rename $RENAMEFS $TESTDS | ||
|
||
log_pass "Multiple mounts are possible" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea
@alek-p when you get a chance the only thing holding up this PR is to run down the CentOS 6 zfs_multi_mount test failure.
|
I haven't forgotten about this PR, but I was preempted with other work. I should be able to return to this task shortly. |
50cf75a
to
230be2c
Compare
Thanks for all the help here @behlendorf, I think this one is ready to go now. I had to change the test so that execution order of commands is guaranteed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Thank's for wrapping this up!
if [ ! -f $FILENAME ]; then | ||
log_fail "Rename failed" | ||
fi | ||
log_must zfs rename $RENAMEFS $TESTDS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, one last thing. We need a cleanup function to make sure this new filesystem has been unmounted and destroyed when this test case exits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
function cleanup | ||
{ | ||
datasetexists $TESTDS && datasetdestroy $TESTDS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no datasetdestroy
funciton. You can use the destroy_dataset
hlper function for this. You should also make sure to explicitly unmount the additional mountpoints first.
destroy_dataset "$TESTDS" "-f"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn it, I left out log_must
on that destroy so didn't notice that function didn't exist... Made updates to cleanup and it's looking better now:
SUCCESS: umount /testpool/multi-mount-test
SUCCESS: umount /testpool/multi-mount-test-second
SUCCESS: umount /testpool/multi-mount-test-third
SUCCESS: zfs destroy -f testpool/multi-mount-test
SUCCESS: destroy_dataset testpool/multi-mount-test -f```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better! Thanks, just waiting on the test results now.
Currently mounting an already mounted zfs dataset results in an error, whereas it is typically allowed with other filesystems. This causes some bad interactions with mount namespaces. Take this sequence for example: - Create a dataset - Create a snapshot of the dataset - Create a clone of the snapshot - Create a new mount namespace - Rename the original dataset The rename results in unmounting and remounting the clone in the original mount namespace, however the remount fails because the dataset is still mounted in the new mount namespace. (Note that this means the mount in the new mount namespace is never being unmounted, so perhaps the unmount/remount of the clone isn't actually necessary.) The problem here is a result of the way mounting is implemented in the kernel module. Since it is not mounting block devices it uses mount_nodev() instead of the usual mount_bdev(). However, mount_nodev() is written for filesystems for which each mount is a new instance (i.e. a new super block), and zfs should be able to detect when a mount request can be satisfied using an existing super block. Change zpl_mount() to call sget() directly with it's own test callback. Passing the objset_t object as the fs data allows checking if a superblock already exists for the dataset, and in that case we just need to return a new reference for the sb's root dentry. Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Closes openzfs#5796
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tcaputi can you look at this one final time.
LGTM (to the best of my ability). |
Codecov Report
@@ Coverage Diff @@
## master #7207 +/- ##
==========================================
+ Coverage 76.2% 76.39% +0.19%
==========================================
Files 330 330
Lines 104294 104259 -35
==========================================
+ Hits 79474 79647 +173
+ Misses 24820 24612 -208
Continue to review full report at Codecov.
|
Currently mounting an already mounted zfs dataset results in an error, whereas it is typically allowed with other filesystems. This causes some bad interactions with mount namespaces. Take this sequence for example: - Create a dataset - Create a snapshot of the dataset - Create a clone of the snapshot - Create a new mount namespace - Rename the original dataset The rename results in unmounting and remounting the clone in the original mount namespace, however the remount fails because the dataset is still mounted in the new mount namespace. (Note that this means the mount in the new mount namespace is never being unmounted, so perhaps the unmount/remount of the clone isn't actually necessary.) The problem here is a result of the way mounting is implemented in the kernel module. Since it is not mounting block devices it uses mount_nodev() instead of the usual mount_bdev(). However, mount_nodev() is written for filesystems for which each mount is a new instance (i.e. a new super block), and zfs should be able to detect when a mount request can be satisfied using an existing super block. Change zpl_mount() to call sget() directly with it's own test callback. Passing the objset_t object as the fs data allows checking if a superblock already exists for the dataset, and in that case we just need to return a new reference for the sb's root dentry. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tom Caputi <tcaputi@datto.com> Signed-off-by: Alek Pinchuk <apinchuk@datto.com> Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Closes openzfs#5796 Closes openzfs#7207
Currently mounting an already mounted zfs dataset results in an error, whereas it is typically allowed with other filesystems. This causes some bad interactions with mount namespaces. Take this sequence for example: - Create a dataset - Create a snapshot of the dataset - Create a clone of the snapshot - Create a new mount namespace - Rename the original dataset The rename results in unmounting and remounting the clone in the original mount namespace, however the remount fails because the dataset is still mounted in the new mount namespace. (Note that this means the mount in the new mount namespace is never being unmounted, so perhaps the unmount/remount of the clone isn't actually necessary.) The problem here is a result of the way mounting is implemented in the kernel module. Since it is not mounting block devices it uses mount_nodev() instead of the usual mount_bdev(). However, mount_nodev() is written for filesystems for which each mount is a new instance (i.e. a new super block), and zfs should be able to detect when a mount request can be satisfied using an existing super block. Change zpl_mount() to call sget() directly with it's own test callback. Passing the objset_t object as the fs data allows checking if a superblock already exists for the dataset, and in that case we just need to return a new reference for the sb's root dentry. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tom Caputi <tcaputi@datto.com> Signed-off-by: Alek Pinchuk <apinchuk@datto.com> Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Closes openzfs#5796 Closes openzfs#7207
Currently mounting an already mounted zfs dataset results in an error, whereas it is typically allowed with other filesystems. This causes some bad interactions with mount namespaces. Take this sequence for example: - Create a dataset - Create a snapshot of the dataset - Create a clone of the snapshot - Create a new mount namespace - Rename the original dataset The rename results in unmounting and remounting the clone in the original mount namespace, however the remount fails because the dataset is still mounted in the new mount namespace. (Note that this means the mount in the new mount namespace is never being unmounted, so perhaps the unmount/remount of the clone isn't actually necessary.) The problem here is a result of the way mounting is implemented in the kernel module. Since it is not mounting block devices it uses mount_nodev() instead of the usual mount_bdev(). However, mount_nodev() is written for filesystems for which each mount is a new instance (i.e. a new super block), and zfs should be able to detect when a mount request can be satisfied using an existing super block. Change zpl_mount() to call sget() directly with it's own test callback. Passing the objset_t object as the fs data allows checking if a superblock already exists for the dataset, and in that case we just need to return a new reference for the sb's root dentry. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tom Caputi <tcaputi@datto.com> Signed-off-by: Alek Pinchuk <apinchuk@datto.com> Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Closes openzfs#5796 Closes openzfs#7207
Currently mounting an already mounted zfs dataset results in an error, whereas it is typically allowed with other filesystems. This causes some bad interactions with mount namespaces. Take this sequence for example: - Create a dataset - Create a snapshot of the dataset - Create a clone of the snapshot - Create a new mount namespace - Rename the original dataset The rename results in unmounting and remounting the clone in the original mount namespace, however the remount fails because the dataset is still mounted in the new mount namespace. (Note that this means the mount in the new mount namespace is never being unmounted, so perhaps the unmount/remount of the clone isn't actually necessary.) The problem here is a result of the way mounting is implemented in the kernel module. Since it is not mounting block devices it uses mount_nodev() instead of the usual mount_bdev(). However, mount_nodev() is written for filesystems for which each mount is a new instance (i.e. a new super block), and zfs should be able to detect when a mount request can be satisfied using an existing super block. Change zpl_mount() to call sget() directly with it's own test callback. Passing the objset_t object as the fs data allows checking if a superblock already exists for the dataset, and in that case we just need to return a new reference for the sb's root dentry. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tom Caputi <tcaputi@datto.com> Signed-off-by: Alek Pinchuk <apinchuk@datto.com> Signed-off-by: Seth Forshee <seth.forshee@canonical.com> Closes #5796 Closes #7207
This is a refresh of #7120
I've added a couple extra tests along with the test that is described below.
Let's see what buildbot thinks.
Currently mounting an already mounted zfs dataset results in an
error, whereas it is typically allowed with other filesystems.
This causes some bad interactions with mount namespaces. Take
this sequence for example:
The rename results in unmounting and remounting the clone in the
original mount namespace, however the remount fails because the
dataset is still mounted in the new mount namespace. (Note that
this means the mount in the new mount namespace is never being
unmounted, so perhaps the unmount/remount of the clone isn't
actually necessary.)
The problem here is a result of the way mounting is implemented
in the kernel module. Since it is not mounting block devices it
uses mount_nodev() instead of the usual mount_bdev(). However,
mount_nodev() is written for filesystems for which each mount is
a new instance (i.e. a new super block), and zfs should be able
to detect when a mount request can be satisfied using an existing
super block.
Change zpl_mount() to call sget() directly with it's own test
callback. Passing the objset_t object as the fs data allows
checking if a superblock already exists for the dataset, and in
that case we just need to return a new reference for the sb's
root dentry.
Signed-off-by: Seth Forshee seth.forshee@canonical.com
Closes #5796
Description
Motivation and Context
How Has This Been Tested?
Types of changes
Checklist:
Signed-off-by
.