Skip to content

Commit

Permalink
NFS: Add functions to swap transports during migration recovery
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
chucklever authored and Trond Myklebust committed Oct 28, 2013
1 parent 32e62b7 commit 800c06a
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
2 changes: 2 additions & 0 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
101 changes: 101 additions & 0 deletions fs/nfs/nfs4namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,104 @@ struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
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;
}

0 comments on commit 800c06a

Please sign in to comment.