]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/nfs/super.c
Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6
[linux-2.6-omap-h63xx.git] / fs / nfs / super.c
index 757aa3b7e64bba266eab8bbeab72a02a018add81..adffe1615c51e422b3cfa5ce1b76d644fab53aa2 100644 (file)
@@ -100,6 +100,7 @@ enum {
        Opt_udp, Opt_tcp,
        Opt_acl, Opt_noacl,
        Opt_rdirplus, Opt_nordirplus,
+       Opt_sharecache, Opt_nosharecache,
 
        /* Mount options that take integer arguments */
        Opt_port,
@@ -146,6 +147,8 @@ static match_table_t nfs_mount_option_tokens = {
        { Opt_noacl, "noacl" },
        { Opt_rdirplus, "rdirplus" },
        { Opt_nordirplus, "nordirplus" },
+       { Opt_sharecache, "sharecache" },
+       { Opt_nosharecache, "nosharecache" },
 
        { Opt_port, "port=%u" },
        { Opt_rsize, "rsize=%u" },
@@ -297,7 +300,10 @@ static const struct super_operations nfs4_sops = {
 };
 #endif
 
-static struct shrinker *acl_shrinker;
+static struct shrinker acl_shrinker = {
+       .shrink         = nfs_access_cache_shrinker,
+       .seeks          = DEFAULT_SEEKS,
+};
 
 /*
  * Register the NFS filesystems
@@ -318,7 +324,7 @@ int __init register_nfs_fs(void)
        if (ret < 0)
                goto error_2;
 #endif
-       acl_shrinker = set_shrinker(DEFAULT_SEEKS, nfs_access_cache_shrinker);
+       register_shrinker(&acl_shrinker);
        return 0;
 
 #ifdef CONFIG_NFS_V4
@@ -336,8 +342,7 @@ error_0:
  */
 void __exit unregister_nfs_fs(void)
 {
-       if (acl_shrinker != NULL)
-               remove_shrinker(acl_shrinker);
+       unregister_shrinker(&acl_shrinker);
 #ifdef CONFIG_NFS_V4
        unregister_filesystem(&nfs4_fs_type);
        nfs_unregister_sysctl();
@@ -450,6 +455,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                { NFS_MOUNT_NONLM, ",nolock", "" },
                { NFS_MOUNT_NOACL, ",noacl", "" },
                { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
+               { NFS_MOUNT_UNSHARED, ",nosharecache", ""},
                { 0, NULL, NULL }
        };
        const struct proc_nfs_info *nfs_infop;
@@ -714,6 +720,12 @@ static int nfs_parse_mount_options(char *raw,
                case Opt_nordirplus:
                        mnt->flags |= NFS_MOUNT_NORDIRPLUS;
                        break;
+               case Opt_sharecache:
+                       mnt->flags &= ~NFS_MOUNT_UNSHARED;
+                       break;
+               case Opt_nosharecache:
+                       mnt->flags |= NFS_MOUNT_UNSHARED;
+                       break;
 
                case Opt_port:
                        if (match_int(args, &option))
@@ -1307,13 +1319,51 @@ static int nfs_compare_super(struct super_block *sb, void *data)
 {
        struct nfs_server *server = data, *old = NFS_SB(sb);
 
-       if (old->nfs_client != server->nfs_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;
 }
 
+#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
+
+static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
+{
+       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 ((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)
+               goto Ebusy;
+       if (a->wsize != b->wsize)
+               goto Ebusy;
+       if (a->rsize != b->rsize)
+               goto Ebusy;
+       if (a->acregmin != b->acregmin)
+               goto Ebusy;
+       if (a->acregmax != b->acregmax)
+               goto Ebusy;
+       if (a->acdirmin != b->acdirmin)
+               goto Ebusy;
+       if (a->acdirmax != b->acdirmax)
+               goto Ebusy;
+       if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor)
+               goto Ebusy;
+       return 0;
+Ebusy:
+       return -EBUSY;
+}
+
 static int nfs_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
@@ -1322,6 +1372,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
        struct nfs_fh mntfh;
        struct nfs_mount_data *data = raw_data;
        struct dentry *mntroot;
+       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        int error;
 
        /* Validate the mount data */
@@ -1336,16 +1387,22 @@ static int nfs_get_sb(struct file_system_type *fs_type,
                goto out;
        }
 
+       if (server->flags & NFS_MOUNT_UNSHARED)
+               compare_super = NULL;
+
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
+       s = sget(fs_type, compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_err_nosb;
        }
 
        if (s->s_fs_info != server) {
+               error = nfs_compare_mount_options(s, server, flags);
                nfs_free_server(server);
                server = NULL;
+               if (error < 0)
+                       goto error_splat_super;
        }
 
        if (!s->s_root) {
@@ -1402,6 +1459,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
        struct super_block *s;
        struct nfs_server *server;
        struct dentry *mntroot;
+       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        int error;
 
        dprintk("--> nfs_xdev_get_sb()\n");
@@ -1413,16 +1471,22 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
                goto out_err_noserver;
        }
 
+       if (server->flags & NFS_MOUNT_UNSHARED)
+               compare_super = NULL;
+
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       s = sget(&nfs_fs_type, compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_err_nosb;
        }
 
        if (s->s_fs_info != server) {
+               error = nfs_compare_mount_options(s, server, flags);
                nfs_free_server(server);
                server = NULL;
+               if (error < 0)
+                       goto error_splat_super;
        }
 
        if (!s->s_root) {
@@ -1541,8 +1605,90 @@ static int nfs4_validate_mount_data(struct nfs4_mount_data **options,
                *ip_addr = c;
 
                break;
-       default:
-               goto out_bad_version;
+       default: {
+               unsigned int len;
+               struct nfs_parsed_mount_data args = {
+                       .rsize          = NFS_MAX_FILE_IO_SIZE,
+                       .wsize          = NFS_MAX_FILE_IO_SIZE,
+                       .timeo          = 600,
+                       .retrans        = 2,
+                       .acregmin       = 3,
+                       .acregmax       = 60,
+                       .acdirmin       = 30,
+                       .acdirmax       = 60,
+                       .nfs_server.protocol = IPPROTO_TCP,
+               };
+
+               if (nfs_parse_mount_options((char *) *options, &args) == 0)
+                       return -EINVAL;
+
+               if (!nfs_verify_server_address((struct sockaddr *)
+                                               &args.nfs_server.address))
+                       return -EINVAL;
+               *addr = args.nfs_server.address;
+
+               switch (args.auth_flavor_len) {
+               case 0:
+                       *authflavour = RPC_AUTH_UNIX;
+                       break;
+               case 1:
+                       *authflavour = (rpc_authflavor_t) args.auth_flavors[0];
+                       break;
+               default:
+                       goto out_inval_auth;
+               }
+
+               /*
+                * Translate to nfs4_mount_data, which nfs4_fill_super
+                * can deal with.
+                */
+               data = kzalloc(sizeof(*data), GFP_KERNEL);
+               if (data == NULL)
+                       return -ENOMEM;
+               *options = data;
+
+               data->version   = 1;
+               data->flags     = args.flags & NFS4_MOUNT_FLAGMASK;
+               data->rsize     = args.rsize;
+               data->wsize     = args.wsize;
+               data->timeo     = args.timeo;
+               data->retrans   = args.retrans;
+               data->acregmin  = args.acregmin;
+               data->acregmax  = args.acregmax;
+               data->acdirmin  = args.acdirmin;
+               data->acdirmax  = args.acdirmax;
+               data->proto     = args.nfs_server.protocol;
+
+               /*
+                * Split "dev_name" into "hostname:mntpath".
+                */
+               c = strchr(dev_name, ':');
+               if (c == NULL)
+                       return -EINVAL;
+               /* while calculating len, pretend ':' is '\0' */
+               len = c - dev_name;
+               if (len > NFS4_MAXNAMLEN)
+                       return -EINVAL;
+               *hostname = kzalloc(len, GFP_KERNEL);
+               if (*hostname == NULL)
+                       return -ENOMEM;
+               strncpy(*hostname, dev_name, len - 1);
+
+               c++;                    /* step over the ':' */
+               len = strlen(c);
+               if (len > NFS4_MAXPATHLEN)
+                       return -EINVAL;
+               *mntpath = kzalloc(len + 1, GFP_KERNEL);
+               if (*mntpath == NULL)
+                       return -ENOMEM;
+               strncpy(*mntpath, c, len);
+
+               dprintk("MNTPATH: %s\n", *mntpath);
+
+               *ip_addr = args.client_address;
+
+               break;
+               }
        }
 
        return 0;
@@ -1559,11 +1705,6 @@ out_inval_auth:
 out_no_address:
        dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
        return -EINVAL;
-
-out_bad_version:
-       dfprintk(MOUNT, "NFS4: bad nfs_mount_data version %d\n",
-                data->version);
-       return -EINVAL;
 }
 
 /*
@@ -1580,6 +1721,7 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
        struct nfs_fh mntfh;
        struct dentry *mntroot;
        char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL;
+       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        int error;
 
        /* Validate the mount data */
@@ -1596,8 +1738,11 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
                goto out;
        }
 
+       if (server->flags & NFS4_MOUNT_UNSHARED)
+               compare_super = NULL;
+
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
+       s = sget(fs_type, compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_free;
@@ -1663,6 +1808,7 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
        struct super_block *s;
        struct nfs_server *server;
        struct dentry *mntroot;
+       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        int error;
 
        dprintk("--> nfs4_xdev_get_sb()\n");
@@ -1674,8 +1820,11 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags,
                goto out_err_noserver;
        }
 
+       if (server->flags & NFS4_MOUNT_UNSHARED)
+               compare_super = NULL;
+
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       s = sget(&nfs_fs_type, compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_err_nosb;
@@ -1730,6 +1879,7 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
        struct nfs_server *server;
        struct dentry *mntroot;
        struct nfs_fh mntfh;
+       int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        int error;
 
        dprintk("--> nfs4_referral_get_sb()\n");
@@ -1741,8 +1891,11 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags,
                goto out_err_noserver;
        }
 
+       if (server->flags & NFS4_MOUNT_UNSHARED)
+               compare_super = NULL;
+
        /* Get a superblock - note that we may end up sharing one that already exists */
-       s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server);
+       s = sget(&nfs_fs_type, compare_super, nfs_set_super, server);
        if (IS_ERR(s)) {
                error = PTR_ERR(s);
                goto out_err_nosb;