NFS: Add functions to swap transports during migration recovery
authorChuck Lever <chuck.lever@oracle.com>
Thu, 17 Oct 2013 18:12:34 +0000 (14:12 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 28 Oct 2013 19:23:07 +0000 (15:23 -0400)
Introduce functions that can walk through an array of returned
fs_locations information and connect a transport to one of the
destination servers listed therein.

Note that NFS minor version 1 introduces "fs_locations_info" which
extends the locations array sorting criteria available to clients.
This is not supported yet.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/nfs4_fs.h
fs/nfs/nfs4namespace.c

index 28842ab..fcae728 100644 (file)
@@ -217,6 +217,8 @@ rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
 struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);
 struct vfsmount *nfs4_submount(struct nfs_server *, struct dentry *,
                               struct nfs_fh *, struct nfs_fattr *);
+int nfs4_replace_transport(struct nfs_server *server,
+                               const struct nfs4_fs_locations *locations);
 
 /* nfs4proc.c */
 extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
index 2288cd3..ebd8b06 100644 (file)
@@ -400,3 +400,104 @@ out:
        rpc_shutdown_client(client);
        return mnt;
 }
+
+/*
+ * Try one location from the fs_locations array.
+ *
+ * Returns zero on success, or a negative errno value.
+ */
+static int nfs4_try_replacing_one_location(struct nfs_server *server,
+               char *page, char *page2,
+               const struct nfs4_fs_location *location)
+{
+       const size_t addr_bufsize = sizeof(struct sockaddr_storage);
+       struct sockaddr *sap;
+       unsigned int s;
+       size_t salen;
+       int error;
+
+       sap = kmalloc(addr_bufsize, GFP_KERNEL);
+       if (sap == NULL)
+               return -ENOMEM;
+
+       error = -ENOENT;
+       for (s = 0; s < location->nservers; s++) {
+               const struct nfs4_string *buf = &location->servers[s];
+               char *hostname;
+
+               if (buf->len <= 0 || buf->len > PAGE_SIZE)
+                       continue;
+
+               if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL)
+                       continue;
+
+               salen = nfs_parse_server_name(buf->data, buf->len,
+                                               sap, addr_bufsize, server);
+               if (salen == 0)
+                       continue;
+               rpc_set_port(sap, NFS_PORT);
+
+               error = -ENOMEM;
+               hostname = kstrndup(buf->data, buf->len, GFP_KERNEL);
+               if (hostname == NULL)
+                       break;
+
+               error = nfs4_update_server(server, hostname, sap, salen);
+               kfree(hostname);
+               if (error == 0)
+                       break;
+       }
+
+       kfree(sap);
+       return error;
+}
+
+/**
+ * nfs4_replace_transport - set up transport to destination server
+ *
+ * @server: export being migrated
+ * @locations: fs_locations array
+ *
+ * Returns zero on success, or a negative errno value.
+ *
+ * The client tries all the entries in the "locations" array, in the
+ * order returned by the server, until one works or the end of the
+ * array is reached.
+ */
+int nfs4_replace_transport(struct nfs_server *server,
+                          const struct nfs4_fs_locations *locations)
+{
+       char *page = NULL, *page2 = NULL;
+       int loc, error;
+
+       error = -ENOENT;
+       if (locations == NULL || locations->nlocations <= 0)
+               goto out;
+
+       error = -ENOMEM;
+       page = (char *) __get_free_page(GFP_USER);
+       if (!page)
+               goto out;
+       page2 = (char *) __get_free_page(GFP_USER);
+       if (!page2)
+               goto out;
+
+       for (loc = 0; loc < locations->nlocations; loc++) {
+               const struct nfs4_fs_location *location =
+                                               &locations->locations[loc];
+
+               if (location == NULL || location->nservers <= 0 ||
+                   location->rootpath.ncomponents == 0)
+                       continue;
+
+               error = nfs4_try_replacing_one_location(server, page,
+                                                       page2, location);
+               if (error == 0)
+                       break;
+       }
+
+out:
+       free_page((unsigned long)page);
+       free_page((unsigned long)page2);
+       return error;
+}