From patchwork Mon May 9 19:38:13 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuck Lever X-Patchwork-Id: 770472 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p49JcVb2010810 for ; Mon, 9 May 2011 19:38:32 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754282Ab1EITiS (ORCPT ); Mon, 9 May 2011 15:38:18 -0400 Received: from mail-yi0-f46.google.com ([209.85.218.46]:55808 "EHLO mail-yi0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754272Ab1EITiQ (ORCPT ); Mon, 9 May 2011 15:38:16 -0400 Received: by yia27 with SMTP id 27so1915854yia.19 for ; Mon, 09 May 2011 12:38:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:sender:from:subject:to:cc:date:message-id :in-reply-to:references:user-agent:mime-version:content-type :content-transfer-encoding; bh=DxkpTeE9crQ9NgODn+ABeZBOerntgm88bu7h8j9sIvE=; b=meus46fEQ9ooMRWG7eZEJUUoVYtUAy05A4iB8Hr/xO0O00R9TywTZupve4s7raRJG/ bX7u3rApOKUZFYLRUE7Dr40WtU/mnOUtQh4JUO1wwSiQSkm80TLaDXbKFtAXpa9ZpIkb H9HcfnMK9HXFPjP26RRE9+1k6NrKKQ/bsIbK0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=sender:from:subject:to:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-type:content-transfer-encoding; b=jknSACW0oeak90wsaavKEJKA6SSpF0Gp319Y1CLBpr1mdN74BNZeUAlBTVCkSJhGQl +4IT83A2SVzIsmepL0/VcV25wCDEN/Z8xog6P3AtNWgYnU8zo0d/csd9aT4w/XChU7Nn pxvDL9ZNaw4pdK4bYMfUJ8Lxemh3LUWQo61Zw= Received: by 10.236.180.232 with SMTP id j68mr2814429yhm.115.1304969895894; Mon, 09 May 2011 12:38:15 -0700 (PDT) Received: from matisse.1015granger.net (adsl-99-26-161-222.dsl.sfldmi.sbcglobal.net [99.26.161.222]) by mx.google.com with ESMTPS id p9sm2755010yhm.7.2011.05.09.12.38.14 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 09 May 2011 12:38:15 -0700 (PDT) From: Chuck Lever Subject: [PATCH 12/16] NFS: Add functions to swap transports during migration recovery To: trond.myklebust@netapp.com Cc: linux-nfs@vger.kernel.org Date: Mon, 09 May 2011 15:38:13 -0400 Message-ID: <20110509193813.16568.4549.stgit@matisse.1015granger.net> In-Reply-To: <20110509192522.16568.59082.stgit@matisse.1015granger.net> References: <20110509192522.16568.59082.stgit@matisse.1015granger.net> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 09 May 2011 19:38:32 +0000 (UTC) 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 --- fs/nfs/internal.h | 2 fs/nfs/nfs4namespace.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 0 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 0bf4e67..191c5b4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -177,6 +177,8 @@ static inline void nfs_fs_proc_exit(void) /* nfs4namespace.c */ #ifdef CONFIG_NFS_V4 extern struct vfsmount *nfs_do_refmount(struct dentry *dentry); +extern int nfs4_replace_transport(struct nfs_server *server, + const struct nfs4_fs_locations *locations); #else static inline struct vfsmount *nfs_do_refmount(struct dentry *dentry) diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index bb80c49..2fa024c 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -22,6 +22,8 @@ #define NFSDBG_FACILITY NFSDBG_VFS +#undef USE_RPC_LOCK_CLIENT + /* * Convert the NFSv4 pathname components into a standard posix path. * @@ -263,3 +265,203 @@ out: dprintk("%s: done\n", __func__); return mnt; } + +#undef NFSDBG_FACILITY +#define NFSDBG_FACILITY NFSDBG_CLIENT + +/* + * Returns zero on success, or a negative errno value. + */ +static int nfs4_update_server(struct nfs_server *server, const char *hostname, + struct sockaddr *sap, size_t salen) +{ + struct nfs_client *clp = server->nfs_client; + struct rpc_clnt *clnt = server->client; + struct xprt_create xargs = { + .ident = clp->cl_proto, + .net = &init_net, + .dstaddr = sap, + .addrlen = salen, + .servername = hostname, + }; + char buf[INET6_ADDRSTRLEN + 1]; + struct sockaddr_storage address; + struct sockaddr *localaddr = (struct sockaddr *)&address; + int error; + + dprintk("--> %s: move FSID %llx:%llx to \"%s\")\n", __func__, + (unsigned long long)server->fsid.major, + (unsigned long long)server->fsid.minor, + hostname); + + /* + * rpc_lock_client() deadlocks here. This is because the tasks + * that received NFS4ERR_MOVED are waiting for us to wake them + * when we are done recovering. But they have bumped + * cl_active_tasks for this clnt, so rpc_lock_client() can't make + * any progress. + */ +#ifdef USE_RPC_LOCK_CLIENT + error = rpc_lock_client(clnt, clnt->cl_timeout->to_maxval); + if (error != 0) { + dprintk("<-- %s(): rpc_lock_client returned %d\n", + __func__, error); + goto out; + } +#endif /* USE_RPC_LOCK_CLIENT */ + + error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout); + if (error != 0) { + dprintk("<-- %s(): rpc_switch_client_transport returned %d\n", + __func__, error); + goto out; + } + + /* + * If we were able to contact the server at @sap, set up a new + * nfs_client and move @server to it. + */ + error = rpc_localaddr(clnt, localaddr, sizeof(address)); + if (error != 0) { + dprintk("<-- %s(): rpc_localaddr returned %d\n", + __func__, error); + goto out; + } + error = -EAFNOSUPPORT; + if (rpc_ntop(localaddr, buf, sizeof(buf)) == 0) { + dprintk("<-- %s(): rpc_ntop returned %d\n", + __func__, error); + goto out; + } + error = nfs4_clone_client(clp, sap, salen, buf, server); + if (error != 0) { + dprintk("<-- %s(): nfs4_clone_client returned %d\n", + __func__, error); + goto out; + } + if (server->nfs_client->cl_hostname == NULL) + server->nfs_client->cl_hostname = kstrdup(hostname, GFP_KERNEL); + + dprintk("<-- %s() succeeded\n", __func__); + +out: +#ifdef USE_RPC_LOCK_CLIENT + rpc_unlock_client(clnt); +#endif /* USE_RPC_LOCK_CLIENT */ + return error; +} + +/* + * 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; + + dprintk("--> %s(%llx:%llx)\n", __func__, + (unsigned long long)server->fsid.major, + (unsigned long long)server->fsid.minor); + + error = -ENOMEM; + sap = kmalloc(addr_bufsize, GFP_KERNEL); + if (sap == NULL) + goto out; + + 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; + + /* XXX: IPv6 not supported? */ + if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL) + continue; + + salen = nfs_parse_server_name(buf->data, buf->len, + sap, addr_bufsize); + 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); +out: + dprintk("<-- %s() = %d\n", __func__, error); + 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; + + dprintk("--> %s(%llx:%llx)\n", __func__, + (unsigned long long)server->fsid.major, + (unsigned long long)server->fsid.minor); + + 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); + + dprintk("<-- %s() = %d\n", __func__, error); + return error; +}