From: Steve Dickson <SteveD@redhat.com> Subject: Re: [RHEL5.1][PATCH] NFS: enable 'nosharecache' mounts. Date: Wed, 12 Sep 2007 13:41:46 -0400 Bugzilla: 243913 Message-Id: <46E824DA.5060602@RedHat.com> Changelog: [nfs] enable 'nosharecache' mounts fixes [modified to deal with rhel5's fs-cache patches] commit e89a5a43b95cdc4305b7c8e8121a380f02476636 Author: Trond Myklebust <Trond.Myklebust@netapp.com> NFS: Fix the mount regression This avoids the recent NFS mount regression (returning EBUSY when mounting the same filesystem twice with different parameters). The best I can do given the constraints appears to be to have the kernel first look for a superblock that matches both the fsid and the user-specified mount options, and then spawn off a new superblock if that search fails. Note that this is not the same as specifying nosharecache everywhere since nosharecache will never attempt to match an existing superblock. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Tested-by: Hua Zhong <hzhong@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> --- linux-2.6.18.i686/fs/nfs/super.c.orig 2007-09-11 09:52:28.118771000 -0400 +++ linux-2.6.18.i686/fs/nfs/super.c 2007-09-12 13:19:28.403336000 -0400 @@ -599,36 +599,16 @@ nfs_initialise_sb(sb); } -static int nfs_set_super(struct super_block *s, void *_server) -{ - struct nfs_server *server = _server; - int ret; - - s->s_fs_info = server; - ret = set_anon_super(s, server); - if (ret == 0) - server->s_dev = s->s_dev; - return ret; -} +#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) -static int nfs_compare_super(struct super_block *sb, void *data) +static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) { - struct nfs_server *server = data, *old = NFS_SB(sb); + const struct nfs_server *a = s->s_fs_info; + const struct rpc_clnt *clnt_a = a->client; + const struct rpc_clnt *clnt_b = b->client; - if (memcmp(&old->nfs_client->cl_addr, - &server->nfs_client->cl_addr, - sizeof(old->nfs_client->cl_addr)) != 0) - return 0; - /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ - if (old->flags & NFS_MOUNT_UNSHARED) - return 0; - if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) - return 0; - return 1; -} - -static int nfs_compare_mount_options(const struct nfs_server *a, const struct nfs_server *b) -{ + if ((s->s_flags & NFS_MS_MASK) != (flags & NFS_MS_MASK)) + goto Ebusy; if (a->nfs_client != b->nfs_client) goto Ebusy; if (a->flags != b->flags) @@ -645,9 +625,62 @@ goto Ebusy; if (a->acdirmax != b->acdirmax) goto Ebusy; - return 0; + if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) + goto Ebusy; + return 1; Ebusy: - return -EBUSY; + return 0; +} + +struct nfs_sb_mountdata { + struct nfs_server *server; + int mntflags; + int err_on_noshare; +}; + +static int nfs_set_super(struct super_block *s, void *data) +{ + struct nfs_sb_mountdata *sb_mntdata = data; + struct nfs_server *server = sb_mntdata->server; + int ret; + + s->s_flags = sb_mntdata->mntflags; + s->s_fs_info = server; + ret = set_anon_super(s, server); + if (ret == 0) + server->s_dev = s->s_dev; + return ret; +} + +static int nfs_compare_super(struct super_block *sb, void *data) +{ + struct nfs_sb_mountdata *sb_mntdata = data; + struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb); + int mntflags = sb_mntdata->mntflags; + int cc, fsc; + + if (memcmp(&old->nfs_client->cl_addr, + &server->nfs_client->cl_addr, + sizeof(old->nfs_client->cl_addr)) != 0) + return 0; + if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) + return 0; + /* + * With FS-Cache super block sharing is a must with the + * same server and exported filesystem + */ + fsc = ((server->flags & NFS_MOUNT_FSCACHE) && + (old->flags & NFS_MOUNT_FSCACHE)); + + /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ + if (old->flags & NFS_MOUNT_UNSHARED) { + sb_mntdata->err_on_noshare |= fsc; + return 0; + } + cc = nfs_compare_mount_options(sb, server, mntflags); + if (!cc) + sb_mntdata->err_on_noshare |= fsc; + return cc; } static int nfs_get_sb(struct file_system_type *fs_type, @@ -659,6 +692,10 @@ struct nfs_mount_data *data = raw_data; struct dentry *mntroot; int (*compare_super)(struct super_block *,void *) = nfs_compare_super; + struct nfs_sb_mountdata sb_mntdata = { + .mntflags = flags, + .err_on_noshare = 0, + }; int error; /* Validate the mount data */ @@ -672,32 +709,29 @@ error = PTR_ERR(server); goto out_err_noserver; } + sb_mntdata.server = server; if (server->flags & NFS_MOUNT_UNSHARED) { - if (server->flags & NFS_MOUNT_FSCACHE) { - error = -EINVAL; - goto out_err_nosb; - } - compare_super = NULL; + /* Always do the comapre with fsc mounts */ + if (!(server->flags & NFS_MOUNT_FSCACHE)) + compare_super = NULL; } /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, server); + s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; } - if (s->s_fs_info != server) { - error = nfs_compare_mount_options(server, NFS_SB(s)); nfs_free_server(server); server = NULL; - if (error < 0) - goto error_splat_super; + } else if (sb_mntdata.err_on_noshare) { + error = -EINVAL; + goto error_splat_super; } if (!s->s_root) { /* initial superblock/root creation */ - s->s_flags = flags; nfs_fill_super(s, data); } @@ -746,6 +780,10 @@ struct nfs_server *server; struct dentry *mntroot; int (*compare_super)(struct super_block *,void *) = nfs_compare_super; + struct nfs_sb_mountdata sb_mntdata = { + .mntflags = flags, + .err_on_noshare = 0, + }; int error; dprintk("--> nfs_xdev_get_sb()\n"); @@ -756,16 +794,15 @@ error = PTR_ERR(server); goto out_err_noserver; } + sb_mntdata.server = server; if (server->flags & NFS_MOUNT_UNSHARED) { - if (server->flags & NFS_MOUNT_FSCACHE) { - error = -EINVAL; - goto out_err_nosb; - } - compare_super = NULL; + /* Always do the comapre with fsc mounts */ + if (!(server->flags & NFS_MOUNT_FSCACHE)) + compare_super = NULL; } /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; @@ -774,11 +811,13 @@ if (s->s_fs_info != server) { nfs_free_server(server); server = NULL; + } else if (sb_mntdata.err_on_noshare) { + error = -EINVAL; + goto error_splat_super; } if (!s->s_root) { /* initial superblock/root creation */ - s->s_flags = flags; nfs_clone_super(s, data->sb); } @@ -871,6 +910,10 @@ char *mntpath = NULL, *hostname = NULL, ip_addr[16]; void *p; int (*compare_super)(struct super_block *,void *) = nfs_compare_super; + struct nfs_sb_mountdata sb_mntdata = { + .mntflags = flags, + .err_on_noshare = 0, + }; int error; if (data == NULL) { @@ -938,32 +981,30 @@ error = PTR_ERR(server); goto out_err_noserver; } + sb_mntdata.server = server; if (server->flags & NFS_MOUNT_UNSHARED) { - if (server->flags & NFS_MOUNT_FSCACHE) { - error = -EINVAL; - goto out_free; - } - compare_super = NULL; + /* Always do the comapre with fsc mounts */ + if (!(server->flags & NFS_MOUNT_FSCACHE)) + compare_super = NULL; } /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, server); + s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_free; } if (s->s_fs_info != server) { - error = nfs_compare_mount_options(server, NFS_SB(s)); nfs_free_server(server); server = NULL; - if (error < 0) - goto error_splat_super; + } else if (sb_mntdata.err_on_noshare) { + error = -EINVAL; + goto error_splat_super; } if (!s->s_root) { /* initial superblock/root creation */ - s->s_flags = flags; nfs4_fill_super(s); } @@ -1020,6 +1061,10 @@ struct nfs_server *server; struct dentry *mntroot; int (*compare_super)(struct super_block *,void *) = nfs_compare_super; + struct nfs_sb_mountdata sb_mntdata = { + .mntflags = flags, + .err_on_noshare = 0, + }; int error; dprintk("--> nfs4_xdev_get_sb()\n"); @@ -1030,16 +1075,15 @@ error = PTR_ERR(server); goto out_err_noserver; } + sb_mntdata.server = server; if (server->flags & NFS4_MOUNT_UNSHARED) { - if (server->flags & NFS4_MOUNT_FSCACHE) { - error = -EINVAL; - goto out_err_nosb; - } - compare_super = NULL; + /* Always do the comapre with fsc mounts */ + if (!(server->flags & NFS_MOUNT_FSCACHE)) + compare_super = NULL; } /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; @@ -1048,11 +1092,13 @@ if (s->s_fs_info != server) { nfs_free_server(server); server = NULL; + } else if (sb_mntdata.err_on_noshare) { + error = -EINVAL; + goto error_splat_super; } if (!s->s_root) { /* initial superblock/root creation */ - s->s_flags = flags; nfs4_clone_super(s, data->sb); } @@ -1095,6 +1141,10 @@ struct dentry *mntroot; struct nfs_fh mntfh; int (*compare_super)(struct super_block *,void *) = nfs_compare_super; + struct nfs_sb_mountdata sb_mntdata = { + .mntflags = flags, + .err_on_noshare = 0, + }; int error; dprintk("--> nfs4_referral_get_sb()\n"); @@ -1105,16 +1155,15 @@ error = PTR_ERR(server); goto out_err_noserver; } + sb_mntdata.server = server; if (server->flags & NFS4_MOUNT_UNSHARED) { - if (server->flags & NFS4_MOUNT_FSCACHE) { - error = -EINVAL; - goto out_err_nosb; - } - compare_super = NULL; + /* Always do the comapre with fsc mounts */ + if (!(server->flags & NFS_MOUNT_FSCACHE)) + compare_super = NULL; } /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); + s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; @@ -1123,11 +1172,13 @@ if (s->s_fs_info != server) { nfs_free_server(server); server = NULL; + } else if (sb_mntdata.err_on_noshare) { + error = -EINVAL; + goto error_splat_super; } if (!s->s_root) { /* initial superblock/root creation */ - s->s_flags = flags; nfs4_fill_super(s); }