Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 800c06a5 authored by Chuck Lever's avatar Chuck Lever Committed by Trond Myklebust
Browse files

NFS: Add functions to swap transports during migration recovery



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: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 32e62b7c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 *);
+101 −0
Original line number Diff line number Diff line
@@ -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;
}