Merge tag 'nfs-for-3.7-4' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[linux-2.6-block.git] / fs / nfs / super.c
index d2c7f5db0847656bb0f2a9d109b5ad9fc8af5cbc..652d3f7176a98fcc4e70e6f5e3d812e4b3d90b83 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/parser.h>
 #include <linux/nsproxy.h>
 #include <linux/rcupdate.h>
+#include <linux/kthread.h>
 
 #include <asm/uaccess.h>
 
@@ -88,6 +89,7 @@ enum {
        Opt_sharecache, Opt_nosharecache,
        Opt_resvport, Opt_noresvport,
        Opt_fscache, Opt_nofscache,
+       Opt_migration, Opt_nomigration,
 
        /* Mount options that take integer arguments */
        Opt_port,
@@ -147,6 +149,8 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_noresvport, "noresvport" },
        { Opt_fscache, "fsc" },
        { Opt_nofscache, "nofsc" },
+       { Opt_migration, "migration" },
+       { Opt_nomigration, "nomigration" },
 
        { Opt_port, "port=%s" },
        { Opt_rsize, "rsize=%s" },
@@ -412,6 +416,54 @@ void nfs_sb_deactive(struct super_block *sb)
 }
 EXPORT_SYMBOL_GPL(nfs_sb_deactive);
 
+static int nfs_deactivate_super_async_work(void *ptr)
+{
+       struct super_block *sb = ptr;
+
+       deactivate_super(sb);
+       module_put_and_exit(0);
+       return 0;
+}
+
+/*
+ * same effect as deactivate_super, but will do final unmount in kthread
+ * context
+ */
+static void nfs_deactivate_super_async(struct super_block *sb)
+{
+       struct task_struct *task;
+       char buf[INET6_ADDRSTRLEN + 1];
+       struct nfs_server *server = NFS_SB(sb);
+       struct nfs_client *clp = server->nfs_client;
+
+       if (!atomic_add_unless(&sb->s_active, -1, 1)) {
+               rcu_read_lock();
+               snprintf(buf, sizeof(buf),
+                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+               rcu_read_unlock();
+
+               __module_get(THIS_MODULE);
+               task = kthread_run(nfs_deactivate_super_async_work, sb,
+                               "%s-deactivate-super", buf);
+               if (IS_ERR(task)) {
+                       pr_err("%s: kthread_run: %ld\n",
+                               __func__, PTR_ERR(task));
+                       /* make synchronous call and hope for the best */
+                       deactivate_super(sb);
+                       module_put(THIS_MODULE);
+               }
+       }
+}
+
+void nfs_sb_deactive_async(struct super_block *sb)
+{
+       struct nfs_server *server = NFS_SB(sb);
+
+       if (atomic_dec_and_test(&server->active))
+               nfs_deactivate_super_async(sb);
+}
+EXPORT_SYMBOL_GPL(nfs_sb_deactive_async);
+
 /*
  * Deliver file system statistics to userspace
  */
@@ -676,6 +728,9 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
        if (nfss->options & NFS_OPTION_FSCACHE)
                seq_printf(m, ",fsc");
 
+       if (nfss->options & NFS_OPTION_MIGRATION)
+               seq_printf(m, ",migration");
+
        if (nfss->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) {
                if (nfss->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
                        seq_printf(m, ",lookupcache=none");
@@ -765,7 +820,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root)
        int err = 0;
        if (!page)
                return -ENOMEM;
-       devname = nfs_path(&dummy, root, page, PAGE_SIZE);
+       devname = nfs_path(&dummy, root, page, PAGE_SIZE, 0);
        if (IS_ERR(devname))
                err = PTR_ERR(devname);
        else
@@ -1106,7 +1161,7 @@ static int nfs_get_option_ul(substring_t args[], unsigned long *option)
        string = match_strdup(args);
        if (string == NULL)
                return -ENOMEM;
-       rc = strict_strtoul(string, 10, option);
+       rc = kstrtoul(string, 10, option);
        kfree(string);
 
        return rc;
@@ -1243,6 +1298,12 @@ static int nfs_parse_mount_options(char *raw,
                        kfree(mnt->fscache_uniq);
                        mnt->fscache_uniq = NULL;
                        break;
+               case Opt_migration:
+                       mnt->options |= NFS_OPTION_MIGRATION;
+                       break;
+               case Opt_nomigration:
+                       mnt->options &= NFS_OPTION_MIGRATION;
+                       break;
 
                /*
                 * options that take numeric values
@@ -1535,6 +1596,10 @@ static int nfs_parse_mount_options(char *raw,
        if (mnt->minorversion && mnt->version != 4)
                goto out_minorversion_mismatch;
 
+       if (mnt->options & NFS_OPTION_MIGRATION &&
+           mnt->version != 4 && mnt->minorversion != 0)
+               goto out_migration_misuse;
+
        /*
         * verify that any proto=/mountproto= options match the address
         * families in the addr=/mountaddr= options.
@@ -1572,6 +1637,10 @@ out_minorversion_mismatch:
        printk(KERN_INFO "NFS: mount option vers=%u does not support "
                         "minorversion=%u\n", mnt->version, mnt->minorversion);
        return 0;
+out_migration_misuse:
+       printk(KERN_INFO
+               "NFS: 'migration' not supported for this NFS version\n");
+       return 0;
 out_nomem:
        printk(KERN_INFO "NFS: not enough memory to parse option\n");
        return 0;
@@ -2494,7 +2563,7 @@ EXPORT_SYMBOL_GPL(nfs_kill_super);
 /*
  * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
  */
-struct dentry *
+static struct dentry *
 nfs_xdev_mount(struct file_system_type *fs_type, int flags,
                const char *dev_name, void *raw_data)
 {
@@ -2642,6 +2711,7 @@ unsigned int nfs_idmap_cache_timeout = 600;
 bool nfs4_disable_idmapping = true;
 unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
 unsigned short send_implementation_id = 1;
+char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
 
 EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
 EXPORT_SYMBOL_GPL(nfs_callback_tcpport);
@@ -2649,6 +2719,7 @@ EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout);
 EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
 EXPORT_SYMBOL_GPL(max_session_slots);
 EXPORT_SYMBOL_GPL(send_implementation_id);
+EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
 
 #define NFS_CALLBACK_MAXPORTNR (65535U)
 
@@ -2659,7 +2730,7 @@ static int param_set_portnr(const char *val, const struct kernel_param *kp)
 
        if (!val)
                return -EINVAL;
-       ret = strict_strtoul(val, 0, &num);
+       ret = kstrtoul(val, 0, &num);
        if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
                return -EINVAL;
        *((unsigned int *)kp->arg) = num;
@@ -2674,6 +2745,8 @@ static struct kernel_param_ops param_ops_portnr = {
 module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
 module_param(nfs_idmap_cache_timeout, int, 0644);
 module_param(nfs4_disable_idmapping, bool, 0644);
+module_param_string(nfs4_unique_id, nfs4_client_id_uniquifier,
+                       NFS4_CLIENT_ID_UNIQ_LEN, 0600);
 MODULE_PARM_DESC(nfs4_disable_idmapping,
                "Turn off NFSv4 idmapping when using 'sec=sys'");
 module_param(max_session_slots, ushort, 0644);
@@ -2682,6 +2755,7 @@ MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
 module_param(send_implementation_id, ushort, 0644);
 MODULE_PARM_DESC(send_implementation_id,
                "Send implementation ID with NFSv4.1 exchange_id");
+MODULE_PARM_DESC(nfs4_unique_id, "nfs_client_id4 uniquifier string");
 MODULE_ALIAS("nfs4");
 
 #endif /* CONFIG_NFS_V4 */