* [PATCH 01/12] SUNRPC: Remove obsolete comment
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
@ 2011-03-14 12:56 ` Chuck Lever
2011-03-14 12:56 ` [PATCH 02/12] SUNRPC: Add API to acquire source address Chuck Lever
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:56 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
The documenting comment at the top of net/sunrpc/clnt.c is out of
date. We adopted BSD's RTO estimation mechanism years ago.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/sunrpc/clnt.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 6111ea2..042e7da 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -13,10 +13,6 @@
* and need to be refreshed, or when a packet was damaged in transit.
* This may be have to be moved to the VFS layer.
*
- * NB: BSD uses a more intelligent approach to guessing when a request
- * or reply has been lost by keeping the RTO estimate for each procedure.
- * We currently make do with a constant timeout value.
- *
* Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
* Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
*/
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 02/12] SUNRPC: Add API to acquire source address
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
2011-03-14 12:56 ` [PATCH 01/12] SUNRPC: Remove obsolete comment Chuck Lever
@ 2011-03-14 12:56 ` Chuck Lever
2011-03-14 12:56 ` [PATCH 03/12] NFS: Add NFS4CLNT_UPDATE_CLIENTID Chuck Lever
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:56 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
NFSv4.0 clients have to send endpoint information for their callback
service to NFSv4.0 servers during SETCLIENTID negotiation.
Traditionally, user space provides the callback endpoint IP address
via the "clientaddr=" mount option.
During an NFSv4 migration event, it is possible that an FSID may be
migrated to a destination server that is accessible by a different NIC
than the source server. The client must update the callback endpoint
information on the destination server so that it can maintain leases
and allow delegation. Without a new "clientaddr=" option, though, the
kernel itself must construct an appropriate IP address for the
callback service.
Provide an API in the RPC client for upper layers to acquire a source
address for a remote. We copy the mechanism used by the mount
command: set up a connected UDP socket to the remote, then scrape the
source address off the socket.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
include/linux/sunrpc/clnt.h | 1
net/sunrpc/clnt.c | 132 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 0 deletions(-)
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 70acded..ca48893 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -169,6 +169,7 @@ size_t rpc_max_payload(struct rpc_clnt *);
void rpc_force_rebind(struct rpc_clnt *);
size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
+int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t *);
size_t rpc_ntop(const struct sockaddr *, char *, const size_t);
size_t rpc_pton(const char *, const size_t,
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 042e7da..49d8460 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -930,6 +930,138 @@ const char *rpc_peeraddr2str(struct rpc_clnt *clnt,
}
EXPORT_SYMBOL_GPL(rpc_peeraddr2str);
+/*
+ * Try a getsockname() on a connected datagram socket. Using a
+ * connected datagram socket prevents leaving a socket in TIME_WAIT.
+ * This conserves the ephemeral port number space.
+ *
+ * Returns zero and fills in "buf" and "bufsize" if successful;
+ * otherwise, a negative errno is returned.
+ */
+static int rpc_sockname(struct net *net, struct sockaddr *sap, size_t salen,
+ struct sockaddr *buf, size_t *bufsize)
+{
+ struct socket *sock;
+ int err, buflen;
+
+ err = __sock_create(net, sap->sa_family,
+ SOCK_DGRAM, IPPROTO_UDP, &sock, 1);
+ if (err < 0) {
+ dprintk("RPC: can't create UDP socket (%d)\n", -err);
+ goto out;
+ }
+
+ err = kernel_bind(sock, sap, salen);
+ if (err < 0) {
+ dprintk("RPC: can't bind UDP socket (%d)\n", -err);
+ goto out_release;
+ }
+
+ err = kernel_connect(sock, sap, salen, 0);
+ if (err < 0) {
+ dprintk("RPC: can't connect UDP socket (%d)\n", -err);
+ goto out_release;
+ }
+
+ err = kernel_getsockname(sock, buf, &buflen);
+ if (err < 0) {
+ dprintk("RPC: getsockname failed (%d)\n", -err);
+ goto out_release;
+ }
+
+ err = 0;
+ *bufsize = buflen;
+ if (buf->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
+ sin6->sin6_scope_id = 0;
+ }
+
+out_release:
+ sock_release(sock);
+out:
+ return err;
+}
+
+/*
+ * Scraping a connected socket failed, so we don't have a useable
+ * local address. Fallback: generate an address that will prevent
+ * the server from calling us back.
+ *
+ * Returns zero and fills in "buf" and "bufsize" if successful;
+ * otherwise, a negative errno is returned.
+ */
+static int rpc_anyaddr(int family, struct sockaddr *buf, size_t *bufsize)
+{
+ static const struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_ANY),
+ };
+ static const struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ };
+ size_t buflen = *bufsize;
+
+ switch (family) {
+ case AF_INET:
+ if (buflen < sizeof(sin))
+ return -EINVAL;
+ memcpy(buf, &sin, sizeof(sin));
+ *bufsize = sizeof(sin);
+ break;
+ case AF_INET6:
+ if (buflen < sizeof(sin6))
+ return -EINVAL;
+ memcpy(buf, &sin6, sizeof(sin6));
+ *bufsize = sizeof(sin);
+ default:
+ return -EAFNOSUPPORT;
+ }
+ return 0;
+}
+
+/**
+ * rpc_localaddr - discover local endpoint address for an RPC client
+ * @clnt: RPC client structure
+ * @buf: target buffer
+ * @bufsize: IN: length of target buffer; OUT: length of local address
+ *
+ * Returns zero and fills in "buf" and "bufsize" if successful;
+ * otherwise, a negative errno is returned.
+ *
+ * This works even if the underlying transport is not currently connected,
+ * or if the upper layer never previously provided a source address.
+ *
+ * The results of this function call are transient: multiple calls in
+ * succession may give different results, depending on how local
+ * networking configuration changes over time.
+ */
+int rpc_localaddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t *bufsize)
+{
+ struct sockaddr_storage address;
+ struct sockaddr *sap = (struct sockaddr *)&address;
+ struct rpc_xprt *xprt;
+ struct net *net;
+ size_t salen;
+ int err;
+
+ rcu_read_lock();
+ xprt = rcu_dereference(clnt->cl_xprt);
+ salen = xprt->addrlen;
+ memcpy(sap, &xprt->addr, salen);
+ net = get_net(xprt->xprt_net);
+ rcu_read_unlock();
+
+ err = rpc_sockname(net, sap, salen, buf, bufsize);
+ put_net(net);
+ if (err < 0) {
+ err = rpc_anyaddr(sap->sa_family, buf, bufsize);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
void
rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
{
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 03/12] NFS: Add NFS4CLNT_UPDATE_CLIENTID
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
2011-03-14 12:56 ` [PATCH 01/12] SUNRPC: Remove obsolete comment Chuck Lever
2011-03-14 12:56 ` [PATCH 02/12] SUNRPC: Add API to acquire source address Chuck Lever
@ 2011-03-14 12:56 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 04/12] NFS: Add a client-side function to display file handles Chuck Lever
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:56 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Introduce a mechanism for updating our client's callback endpoint
information on a server when handling NFS4ERR_CB_PATH_DOWN or during
migration recovery.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/nfs4_fs.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 7a74740..e266e2d 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -47,6 +47,7 @@ enum nfs4_client_state {
NFS4CLNT_LAYOUTRECALL,
NFS4CLNT_SESSION_RESET,
NFS4CLNT_RECALL_SLOT,
+ NFS4CLNT_UPDATE_CLIENTID,
};
enum nfs4_session_state {
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 04/12] NFS: Add a client-side function to display file handles
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (2 preceding siblings ...)
2011-03-14 12:56 ` [PATCH 03/12] NFS: Add NFS4CLNT_UPDATE_CLIENTID Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 05/12] NFS: Save root file handle in nfs_server Chuck Lever
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
For debugging, introduce a simplistic function to print file handles
on the system console. It's hooked into the dprintk debugging
facility, but you can call _nfs_display_fhandle() directly if you
always want to print a handle.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/inode.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/nfs_fs.h | 14 ++++++++++++++
2 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 1cc600e..552c47b 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1033,6 +1033,51 @@ struct nfs_fh *nfs_alloc_fhandle(void)
}
/**
+ * _nfs_display_fhandle - display an NFS file handle on the console
+ *
+ * @fh: file handle to display
+ * @caption: display caption
+ *
+ * For debugging only.
+ */
+#ifdef RPC_DEBUG
+void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption)
+{
+ unsigned short i;
+
+ if (fh->size == 0 || fh == NULL) {
+ printk(KERN_NOTICE "%s at %p is empty\n", caption, fh);
+ return;
+ }
+
+ printk(KERN_NOTICE "%s at %p is %u bytes:\n", caption, fh, fh->size);
+ for (i = 0; i < fh->size; i += 16) {
+ __be32 *pos = (__be32 *)&fh->data[i];
+
+ switch ((fh->size - i - 1) >> 2) {
+ case 0:
+ printk(KERN_NOTICE " %08x",
+ be32_to_cpup(pos));
+ break;
+ case 1:
+ printk(KERN_NOTICE " %08x %08x\n",
+ be32_to_cpup(pos), be32_to_cpup(pos + 1));
+ break;
+ case 2:
+ printk(KERN_NOTICE " %08x %08x %08x\n",
+ be32_to_cpup(pos), be32_to_cpup(pos + 1),
+ be32_to_cpup(pos + 2));
+ break;
+ default:
+ printk(KERN_NOTICE " %08x %08x %08x %08x\n",
+ be32_to_cpup(pos), be32_to_cpup(pos + 1),
+ be32_to_cpup(pos + 2), be32_to_cpup(pos + 3));
+ }
+ }
+}
+#endif
+
+/**
* nfs_inode_attrs_need_update - check if the inode attributes need updating
* @inode - pointer to inode
* @fattr - attributes
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 6023efa..a3ed01d 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -384,6 +384,20 @@ static inline void nfs_free_fhandle(const struct nfs_fh *fh)
kfree(fh);
}
+#ifdef RPC_DEBUG
+extern void _nfs_display_fhandle(const struct nfs_fh *fh, const char *caption);
+#define nfs_display_fhandle(fh, caption) \
+ do { \
+ if (unlikely(nfs_debug & NFSDBG_FACILITY)) \
+ _nfs_display_fhandle(fh, caption); \
+ } while(0)
+#else
+static inline void nfs_display_fhandle(const struct nfs_fh *fh,
+ const char *caption)
+{
+}
+#endif
+
/*
* linux/fs/nfs/nfsroot.c
*/
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 05/12] NFS: Save root file handle in nfs_server
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (3 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 04/12] NFS: Add a client-side function to display file handles Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 06/12] NFS: Introduce NFS_ATTR_FATTR_V4_LOCATIONS Chuck Lever
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Save each FSID's root directory file handle in the export's local
nfs_server structure on the client. This FH can later be used by the
migration recovery logic.
NB: Saving the root FH is done only for NFSv4 mounts.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/client.c | 1 +
fs/nfs/getroot.c | 5 +++++
include/linux/nfs_fs_sb.h | 1 +
3 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index f8c95e4..a2f016a 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -1081,6 +1081,7 @@ void nfs_free_server(struct nfs_server *server)
nfs_put_client(server->nfs_client);
nfs_free_iostats(server->io_stats);
+ nfs_free_fhandle(server->rootfh);
bdi_destroy(&server->backing_dev_info);
kfree(server);
nfs_release_automount_timer();
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
index b5ffe8f..437d1a1 100644
--- a/fs/nfs/getroot.c
+++ b/fs/nfs/getroot.c
@@ -205,6 +205,11 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
ret = ERR_CAST(inode);
goto out;
}
+ server->rootfh = nfs_alloc_fhandle();
+ if (server->rootfh != NULL) {
+ nfs_display_fhandle(mntfh, "nfs_get_root: new root FH");
+ nfs_copy_fh(server->rootfh, mntfh);
+ }
error = nfs_superblock_set_dummy_root(sb, inode);
if (error != 0) {
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index b197563..961ee85 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -153,6 +153,7 @@ struct nfs_server {
#endif
struct list_head delegations;
void (*destroy)(struct nfs_server *);
+ struct nfs_fh *rootfh;
atomic_t active; /* Keep trace of any activity to this server */
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 06/12] NFS: Introduce NFS_ATTR_FATTR_V4_LOCATIONS
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (4 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 05/12] NFS: Save root file handle in nfs_server Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 07/12] NFS: Introduce nfs4_proc_get_mig_status() Chuck Lever
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Our NFS client must distinguish between referral events (which it
currently supports) and migration events (which it does not yet
support).
In both types of events, an fs_locations array is returned, but the
upper layers should make the distinction between a referral and a
migration, not the XDR layer. There really isn't a way for an
XDR decoder to tell one from the other.
Slightly adjust the FATTR flags returned by decode_fs_locations()
to set NFS_ATTR_FATTR_V4_LOCATIONS, and then have logic in nfs4proc.c
distinguish whether the locations array is for a referral or
something else.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/nfs4proc.c | 5 +++--
fs/nfs/nfs4xdr.c | 2 +-
include/linux/nfs_xdr.h | 3 ++-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 491b068..1e72e5f 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2202,7 +2202,8 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct
}
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
- fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
+ if (fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)
+ fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
if (!fattr->mode)
fattr->mode = S_IFDIR;
memset(fhandle, 0, sizeof(struct nfs_fh));
@@ -4517,7 +4518,7 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
{
if (!((fattr->valid & NFS_ATTR_FATTR_FILEID) &&
(fattr->valid & NFS_ATTR_FATTR_FSID) &&
- (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)))
+ (fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)))
return;
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 4e2c168..9d2c9d2 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -3204,7 +3204,7 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
res->nlocations++;
}
if (res->nlocations != 0)
- status = NFS_ATTR_FATTR_V4_REFERRAL;
+ status = NFS_ATTR_FATTR_V4_LOCATIONS;
out:
dprintk("%s: fs_locations done, error = %d\n", __func__, status);
return status;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index b006857..2492487 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -77,7 +77,8 @@ struct nfs_fattr {
#define NFS_ATTR_FATTR_PRECTIME (1U << 16)
#define NFS_ATTR_FATTR_CHANGE (1U << 17)
#define NFS_ATTR_FATTR_PRECHANGE (1U << 18)
-#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 19) /* NFSv4 referral */
+#define NFS_ATTR_FATTR_V4_REFERRAL (1U << 19)
+#define NFS_ATTR_FATTR_V4_LOCATIONS (1U << 20)
#define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
| NFS_ATTR_FATTR_MODE \
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 07/12] NFS: Introduce nfs4_proc_get_mig_status()
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (5 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 06/12] NFS: Introduce NFS_ATTR_FATTR_V4_LOCATIONS Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 08/12] NFS: Add functions to swap transports during migration recovery Chuck Lever
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
The nfs4_proc_fs_locations() function is invoked during referral
processing to perform a GETATTR(fs_locations) on an object's parent
directory in order to discover the target of the referral. It
performs a LOOKUP, so the client needs to know the parent's file
handle a priori.
To handle migration recovery, we need to probe fs_locations
information on an FSID's root directory. The "parent" of the root
directory is not available via LOOKUP.
Additionally, a minor version zero server needs to know what client ID
is requesting fs_locations information, so it can clear the flag that
forces it to continue returning NFS4ERR_LEASE_MOVED. This flag is set
per client ID and per FSID. The client ID is not an argument of the
PUTFH or GETATTR operations.
Later minor versions have client ID information embedded in the
session ID. By convention, minor version zero clients send a RENEW
operation in the same compound as the GETATTR(fs_locations). (We may
need to visit this for referrals too, in case the LOOKUP operation is
actually seeing a migration).
Because of all of this, we need a new variant of
nfs4_proc_fs_locations() that can operate directly on a target file
handle, rather than taking a name and doing a LOOKUP as part of
retrieving fs_locations from the server. It also must properly append
a RENEW operation as needed. Introduce nfs4_proc_get_mig_status() to
fill this role, and add the requisite XDR encoding and decoding
paraphenalia.
Under the covers, both nfs4_proc_foo functions use the FS_LOCATIONS
XDR routines. This is a little awkward, but is necessary because it's
currently not easy to add new NFSv4 procedures due to a bug in
nfsstat.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/nfs4_fs.h | 2 +
fs/nfs/nfs4proc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-
fs/nfs/nfs4xdr.c | 48 +++++++++++++++++++++++++---------
include/linux/nfs_xdr.h | 7 ++++-
4 files changed, 108 insertions(+), 16 deletions(-)
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index e266e2d..b746a51 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -241,6 +241,8 @@ extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_
extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
+extern int nfs4_proc_get_mig_status(struct nfs_server *server,
+ struct nfs4_fs_locations *fs_locations, struct page *page);
extern void nfs4_release_lockowner(const struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 1e72e5f..bdd8aac 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4527,6 +4527,16 @@ static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
fattr->nlink = 2;
}
+/**
+ * nfs4_proc_fs_locations - retrieve locations array for a named object
+ *
+ * @dir: inode of parent directory
+ * @name: qstr containing name of object to query
+ * @locations: result of query
+ * @page: buffer
+ *
+ * Returns zero on success, or a negative errno code
+ */
int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page)
{
@@ -4551,13 +4561,66 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
};
int status;
- dprintk("%s: start\n", __func__);
+ dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
+ (unsigned long long)server->fsid.major,
+ (unsigned long long)server->fsid.minor,
+ server->nfs_client->cl_hostname);
nfs_fattr_init(&fs_locations->fattr);
fs_locations->server = server;
fs_locations->nlocations = 0;
status = nfs4_call_sync(server, &msg, &args, &res, 0);
nfs_fixup_referral_attributes(&fs_locations->fattr);
- dprintk("%s: returned status = %d\n", __func__, status);
+ dprintk("<-- %s status=%d\n", __func__, status);
+ return status;
+}
+
+/**
+ * nfs4_proc_get_mig_status - probe migration status of an export
+ *
+ * @server: local state for server
+ * @locations: result of query
+ * @page: buffer
+ *
+ * Returns zero on success, or a negative errno code
+ */
+int nfs4_proc_get_mig_status(struct nfs_server *server,
+ struct nfs4_fs_locations *locations,
+ struct page *page)
+{
+ u32 bitmask[2] = {
+ [0] = FATTR4_WORD0_FSID |
+ FATTR4_WORD0_FS_LOCATIONS,
+ };
+ struct nfs4_fs_locations_arg args = {
+ .client = server->nfs_client,
+ .fh = server->rootfh,
+ .page = page,
+ .bitmask = bitmask,
+ };
+ struct nfs4_fs_locations_res res = {
+ .fs_locations = locations,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
+ int status;
+
+ dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
+ (unsigned long long)server->fsid.major,
+ (unsigned long long)server->fsid.minor,
+ args.client->cl_hostname);
+ nfs_display_fhandle(args.fh, "Probing file handle");
+
+ args.mig_status = res.mig_status = 1;
+ if (args.client->cl_mvops->minor_version == 0)
+ args.renew = res.renew = 1;
+ nfs_fattr_init(&locations->fattr);
+ locations->server = server;
+ locations->nlocations = 0;
+ status = nfs4_call_sync(server, &msg, &args, &res, 0);
+ dprintk("<-- %s status=%d\n", __func__, status);
return status;
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 9d2c9d2..e4a8e50 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -669,13 +669,15 @@ static int nfs4_stat_to_errno(int);
encode_sequence_maxsz + \
encode_putfh_maxsz + \
encode_lookup_maxsz + \
- encode_fs_locations_maxsz)
+ encode_fs_locations_maxsz + \
+ encode_renew_maxsz)
#define NFS4_dec_fs_locations_sz \
(compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
decode_lookup_maxsz + \
- decode_fs_locations_maxsz)
+ decode_fs_locations_maxsz + \
+ encode_renew_maxsz)
#if defined(CONFIG_NFS_V4_1)
#define NFS4_enc_exchange_id_sz \
(compound_encode_hdr_maxsz + \
@@ -2450,11 +2452,20 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
- encode_putfh(xdr, args->dir_fh, &hdr);
- encode_lookup(xdr, args->name, &hdr);
- replen = hdr.replen; /* get the attribute into args->page */
- encode_fs_locations(xdr, args->bitmask, &hdr);
+ if (args->mig_status) {
+ encode_putfh(xdr, args->fh, &hdr);
+ replen = hdr.replen;
+ encode_fs_locations(xdr, args->bitmask, &hdr);
+ if (args->renew)
+ encode_renew(xdr, args->client, &hdr);
+ } else {
+ encode_putfh(xdr, args->dir_fh, &hdr);
+ encode_lookup(xdr, args->name, &hdr);
+ replen = hdr.replen;
+ encode_fs_locations(xdr, args->bitmask, &hdr);
+ }
+ /* Set up reply kvec to capture returned fs_locations array. */
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page,
0, PAGE_SIZE);
encode_nops(&hdr);
@@ -5904,13 +5915,24 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
status = decode_putfh(xdr);
if (status)
goto out;
- status = decode_lookup(xdr);
- if (status)
- goto out;
- xdr_enter_page(xdr, PAGE_SIZE);
- status = decode_getfattr(xdr, &res->fs_locations->fattr,
- res->fs_locations->server,
- !RPC_IS_ASYNC(req->rq_task));
+ if (res->mig_status) {
+ xdr_enter_page(xdr, PAGE_SIZE);
+ status = decode_getfattr(xdr, &res->fs_locations->fattr,
+ res->fs_locations->server,
+ !RPC_IS_ASYNC(req->rq_task));
+ if (status)
+ goto out;
+ if (res->renew)
+ status = decode_renew(xdr);
+ } else {
+ status = decode_lookup(xdr);
+ if (status)
+ goto out;
+ xdr_enter_page(xdr, PAGE_SIZE);
+ status = decode_getfattr(xdr, &res->fs_locations->fattr,
+ res->fs_locations->server,
+ !RPC_IS_ASYNC(req->rq_task));
+ }
out:
return status;
}
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 2492487..bcf823b 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -925,16 +925,21 @@ struct nfs4_fs_locations {
};
struct nfs4_fs_locations_arg {
+ const struct nfs_client *client;
const struct nfs_fh *dir_fh;
+ const struct nfs_fh *fh;
const struct qstr *name;
struct page *page;
const u32 *bitmask;
- struct nfs4_sequence_args seq_args;
+ struct nfs4_sequence_args seq_args;
+ unsigned char mig_status : 1, renew : 1;
};
struct nfs4_fs_locations_res {
struct nfs4_fs_locations *fs_locations;
struct nfs4_sequence_res seq_res;
+ unsigned char mig_status : 1,
+ renew : 1;
};
#endif /* CONFIG_NFS_V4 */
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 08/12] NFS: Add functions to swap transports during migration recovery
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (6 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 07/12] NFS: Introduce nfs4_proc_get_mig_status() Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 09/12] NFS: Add basic migration support to state manager thread Chuck Lever
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
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 minor version 1 introduces "fs_locations_info" which
extends the sorting information available to clients. This is
not supported yet.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/internal.h | 2 +
fs/nfs/nfs4namespace.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 164 insertions(+), 0 deletions(-)
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 58a9ad7..bf2a4e7 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -164,6 +164,8 @@ static inline void nfs_fs_proc_exit(void)
/* nfs4namespace.c */
#ifdef CONFIG_NFS_V4
extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, 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(const struct vfsmount *mnt_parent, struct dentry *dentry)
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 3c2a172..ea591ff 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -270,3 +270,165 @@ 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,
+ };
+ 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 active_tasks,
+ * so rpc_lock_client() can't make any progress.
+ */
+#if 0
+ error = rpc_lock_client(clnt, clnt->cl_timeout->to_maxval);
+ if (error != 0)
+ goto out;
+#endif
+
+ error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout);
+
+out:
+#if 0
+ rpc_unlock_client(clnt);
+#endif
+ dprintk("<-- %s() = %d\n", __func__, error);
+ 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;
+}
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 09/12] NFS: Add basic migration support to state manager thread
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (7 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 08/12] NFS: Add functions to swap transports during migration recovery Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:57 ` [PATCH 10/12] NFS: Remove "const" from "struct nfs_server *" fields Chuck Lever
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Migration recovery will be handled separately from the normal
synchronous and asynchronous NFS processes, much like the existing
state manager thread. In fact state and migration recovery will
have to be serialized.
The best approach, then, is to add migration recovery support to
the existing state manager infrastructure, reusing its rendevous
mechanism and finite state machine.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/nfs4_fs.h | 2 +
fs/nfs/nfs4state.c | 96 ++++++++++++++++++++++++++++++++++++++++++---
include/linux/nfs_fs_sb.h | 3 +
3 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b746a51..8049c62 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -48,6 +48,7 @@ enum nfs4_client_state {
NFS4CLNT_SESSION_RESET,
NFS4CLNT_RECALL_SLOT,
NFS4CLNT_UPDATE_CLIENTID,
+ NFS4CLNT_MOVED,
};
enum nfs4_session_state {
@@ -311,6 +312,7 @@ extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
extern void nfs4_schedule_state_recovery(struct nfs_client *);
+extern void nfs4_schedule_migration_recovery(struct nfs_server *);
extern void nfs4_schedule_state_manager(struct nfs_client *);
extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
extern int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state);
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index e6742b5..a4e69c3 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -56,6 +56,8 @@
#include "internal.h"
#include "pnfs.h"
+#define NFSDBG_FACILITY NFSDBG_CLIENT
+
#define OPENOWNER_POOL_SIZE 8
const nfs4_stateid zero_stateid;
@@ -1013,9 +1015,32 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp)
{
if (!clp)
return;
+ dprintk("--> %s: \"%s\" (client ID %llx)\n",
+ __func__, clp->cl_hostname, clp->cl_clientid);
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
nfs4_schedule_state_manager(clp);
+ dprintk("<-- %s\n", __func__);
+}
+
+/**
+ * nfs4_schedule_migration_recovery - start background migration recovery
+ *
+ * @server: nfs_server representing remote file system that is migrating
+ *
+ */
+void nfs4_schedule_migration_recovery(struct nfs_server *server)
+{
+ struct nfs_client *clp = server->nfs_client;
+
+ dprintk("--> %s(%llx:%llx)\n", __func__,
+ (unsigned long long)server->fsid.major,
+ (unsigned long long)server->fsid.minor);
+ if (test_and_set_bit(NFS4CLNT_MOVED, &clp->cl_state) == 0) {
+ clp->cl_moved_server = server;
+ nfs4_schedule_state_manager(clp);
+ }
+ dprintk("<-- %s\n", __func__);
}
int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
@@ -1435,6 +1460,67 @@ static int nfs4_reclaim_lease(struct nfs_client *clp)
return status;
}
+/*
+ * Try remote migration of one FSID from a source server to a
+ * destination server. The source server provides a list of
+ * potential destinations.
+ */
+static void nfs4_try_migration(struct nfs_server *server)
+{
+ struct nfs_client *clp = server->nfs_client;
+ struct nfs4_fs_locations *locations = NULL;
+ struct page *page;
+ int status;
+
+ dprintk("--> %s: FSID %llx:%llx on \"%s\"\n", __func__,
+ (unsigned long long)server->fsid.major,
+ (unsigned long long)server->fsid.minor,
+ clp->cl_hostname);
+
+ status = -ENOMEM;
+ page = alloc_page(GFP_KERNEL);
+ locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
+ if (page == NULL || locations == NULL) {
+ dprintk("<-- %s: no memory\n", __func__);
+ goto out;
+ }
+
+ status = nfs4_proc_get_mig_status(server, locations, page);
+ if (status != 0) {
+ dprintk("<-- %s: get migration status: %d\n",
+ __func__, status);
+ goto out;
+ }
+ if (!(locations->fattr.valid & NFS_ATTR_FATTR_V4_LOCATIONS)) {
+ dprintk("<-- %s: No fs_locations data available, "
+ "migration skipped\n", __func__);
+ goto out;
+ }
+
+ status = nfs4_replace_transport(server, locations);
+ if (status != 0) {
+ dprintk("<-- %s: failed to replace transport: %d\n",
+ __func__, status);
+ goto out;
+ }
+
+ /* XXX: Update pieces of our client ID. NB: The cl_ipaddr is still
+ * in the nfs_client... maybe it needs to be moved to the nfs_server? */
+
+ /* Force an update of our callback info on the destination server */
+ set_bit(NFS4CLNT_UPDATE_CLIENTID, &clp->cl_state);
+
+ /* XXX: would like to update /proc/mounts, but that's entirely
+ * optional. */
+
+ dprintk("<-- %s: migration succeeded\n", __func__);
+
+out:
+ if (page != NULL)
+ __free_page(page);
+ kfree(locations);
+}
+
#ifdef CONFIG_NFS_V4_1
void nfs41_handle_recall_slot(struct nfs_client *clp)
{
@@ -1624,6 +1710,9 @@ static void nfs4_state_manager(struct nfs_client *clp)
goto out_error;
}
+ if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state))
+ nfs4_try_migration(clp->cl_moved_server);
+
/* First recover reboot state... */
if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
status = nfs4_do_reclaim(clp,
@@ -1664,7 +1753,6 @@ static void nfs4_state_manager(struct nfs_client *clp)
continue;
}
-
nfs4_clear_state_manager_bit(clp);
/* Did we race with an attempt to give us more work? */
if (clp->cl_state == 0)
@@ -1690,9 +1778,3 @@ static int nfs4_run_state_manager(void *ptr)
module_put_and_exit(0);
return 0;
}
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 961ee85..3741928 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -55,6 +55,9 @@ struct nfs_client {
struct rpc_wait_queue cl_rpcwaitq;
+ /* accessed only when NFS4CLNT_MOVED bit is set */
+ struct nfs_server * cl_moved_server;
+
/* used for the setclientid verifier */
struct timespec cl_boot_time;
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 10/12] NFS: Remove "const" from "struct nfs_server *" fields
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (8 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 09/12] NFS: Add basic migration support to state manager thread Chuck Lever
@ 2011-03-14 12:57 ` Chuck Lever
2011-03-14 12:58 ` [PATCH 11/12] NFS: Add migration recovery callouts in nfs4proc.c Chuck Lever
2011-03-14 12:58 ` [PATCH 12/12] NFS: Implement support for NFS4ERR_LEASE_MOVED Chuck Lever
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:57 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
We're about to pass the nfs_server pointer in some NFSv4 argument and
result structures to functions that may change it, so it's no longer
"const".
The preference here is to maintain existing whitespace style rather
than answer all the nits called out by checkpatch.pl.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
include/linux/nfs_xdr.h | 28 ++++++++++++++--------------
1 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index bcf823b..b9ae049 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -256,7 +256,7 @@ struct nfs_openargs {
fmode_t delegation_type; /* CLAIM_PREVIOUS */
} u;
const struct qstr * name;
- const struct nfs_server *server; /* Needed for ID mapping */
+ struct nfs_server * server; /* Needed for ID mapping */
const u32 * bitmask;
__u32 claim;
struct nfs4_sequence_args seq_args;
@@ -270,7 +270,7 @@ struct nfs_openres {
struct nfs_fattr * f_attr;
struct nfs_fattr * dir_attr;
struct nfs_seqid * seqid;
- const struct nfs_server *server;
+ struct nfs_server * server;
fmode_t delegation_type;
nfs4_stateid delegation;
__u32 do_recall;
@@ -309,7 +309,7 @@ struct nfs_closeres {
nfs4_stateid stateid;
struct nfs_fattr * fattr;
struct nfs_seqid * seqid;
- const struct nfs_server *server;
+ struct nfs_server * server;
struct nfs4_sequence_res seq_res;
};
/*
@@ -381,7 +381,7 @@ struct nfs4_delegreturnargs {
struct nfs4_delegreturnres {
struct nfs_fattr * fattr;
- const struct nfs_server *server;
+ struct nfs_server * server;
struct nfs4_sequence_res seq_res;
};
@@ -431,7 +431,7 @@ struct nfs_writeres {
struct nfs_fattr * fattr;
struct nfs_writeverf * verf;
__u32 count;
- const struct nfs_server *server;
+ struct nfs_server * server;
struct nfs4_sequence_res seq_res;
};
@@ -446,7 +446,7 @@ struct nfs_removeargs {
};
struct nfs_removeres {
- const struct nfs_server *server;
+ struct nfs_server *server;
struct nfs_fattr *dir_attr;
struct nfs4_change_info cinfo;
struct nfs4_sequence_res seq_res;
@@ -465,7 +465,7 @@ struct nfs_renameargs {
};
struct nfs_renameres {
- const struct nfs_server *server;
+ struct nfs_server *server;
struct nfs4_change_info old_cinfo;
struct nfs_fattr *old_fattr;
struct nfs4_change_info new_cinfo;
@@ -546,7 +546,7 @@ struct nfs_getaclres {
struct nfs_setattrres {
struct nfs_fattr * fattr;
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs4_sequence_res seq_res;
};
@@ -719,7 +719,7 @@ struct nfs4_accessargs {
};
struct nfs4_accessres {
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs_fattr * fattr;
u32 supported;
u32 access;
@@ -747,7 +747,7 @@ struct nfs4_create_arg {
};
struct nfs4_create_res {
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs_fh * fh;
struct nfs_fattr * fattr;
struct nfs4_change_info dir_cinfo;
@@ -773,7 +773,7 @@ struct nfs4_getattr_arg {
};
struct nfs4_getattr_res {
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs_fattr * fattr;
struct nfs4_sequence_res seq_res;
};
@@ -787,7 +787,7 @@ struct nfs4_link_arg {
};
struct nfs4_link_res {
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs_fattr * fattr;
struct nfs4_change_info cinfo;
struct nfs_fattr * dir_attr;
@@ -803,7 +803,7 @@ struct nfs4_lookup_arg {
};
struct nfs4_lookup_res {
- const struct nfs_server * server;
+ struct nfs_server * server;
struct nfs_fattr * fattr;
struct nfs_fh * fh;
struct nfs4_sequence_res seq_res;
@@ -918,7 +918,7 @@ struct nfs4_fs_location {
#define NFS4_FS_LOCATIONS_MAXENTRIES 10
struct nfs4_fs_locations {
struct nfs_fattr fattr;
- const struct nfs_server *server;
+ struct nfs_server *server;
struct nfs4_pathname fs_path;
int nlocations;
struct nfs4_fs_location locations[NFS4_FS_LOCATIONS_MAXENTRIES];
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 11/12] NFS: Add migration recovery callouts in nfs4proc.c
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (9 preceding siblings ...)
2011-03-14 12:57 ` [PATCH 10/12] NFS: Remove "const" from "struct nfs_server *" fields Chuck Lever
@ 2011-03-14 12:58 ` Chuck Lever
2011-03-14 12:58 ` [PATCH 12/12] NFS: Implement support for NFS4ERR_LEASE_MOVED Chuck Lever
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:58 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
Finally, to enable support for migration, insert migration recovery
callouts in the synchronous and asynchronous error handling paths for
NFSv4 procedures.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/nfs4proc.c | 61 ++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 46 insertions(+), 15 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index bdd8aac..2537a18 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -71,7 +71,8 @@ struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data);
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
+static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *,
+ struct nfs4_state *);
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
@@ -239,10 +240,17 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
return res;
}
-/* This is the error handling routine for processes that are allowed
- * to sleep.
+/**
+ * nfs4_handle_exception - Common error handling for callers allowed to sleep
+ *
+ * @server: local state context for the server
+ * @errorcode: NFS4ERR value returned from the server
+ * @exception: exception handling state
+ *
+ * Returns zero on success, or a negative errno value.
*/
-static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+static int nfs4_handle_exception(struct nfs_server *server, int errorcode,
+ struct nfs4_exception *exception)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_state *state = exception->state;
@@ -277,6 +285,9 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
exception->retry = 1;
break;
#endif /* defined(CONFIG_NFS_V4_1) */
+ case -NFS4ERR_MOVED:
+ nfs4_schedule_migration_recovery(server);
+ goto recovery_wait;
case -NFS4ERR_FILE_OPEN:
if (exception->timeout > HZ) {
/* We have retried a decent amount, time to
@@ -298,6 +309,7 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
return nfs4_map_errors(ret);
do_state_recovery:
nfs4_schedule_state_recovery(clp);
+recovery_wait:
ret = nfs4_wait_clnt_recover(clp);
if (ret == 0)
exception->retry = 1;
@@ -2194,10 +2206,14 @@ static int nfs4_get_referral(struct inode *dir, const struct qstr *name, struct
status = nfs4_proc_fs_locations(dir, name, locations, page);
if (status != 0)
goto out;
- /* Make sure server returned a different fsid for the referral */
+
+ /*
+ * If the fsid didn't change, this is a migration event, not a
+ * referral. Cause us to drop into the exception handler, which
+ * will kick off migration recovery.
+ */
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
- dprintk("%s: server did not return a different fsid for a referral at %s\n", __func__, name->name);
- status = -EIO;
+ status = -NFS4ERR_MOVED;
goto out;
}
@@ -3492,8 +3508,18 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
return err;
}
-static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
+/**
+ * nfs4_async_handle_error - Common error handling for callers who cannot sleep
+ *
+ * @task: active RPC task
+ * @server: local state for the server
+ * @state: NFSv4 state for active operation
+ *
+ * Returns zero on success, or a negative errno value.
+ */
+static int nfs4_async_handle_error(struct rpc_task *task,
+ struct nfs_server *server,
+ struct nfs4_state *state)
{
struct nfs_client *clp = server->nfs_client;
@@ -3522,19 +3548,23 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
dprintk("%s ERROR %d, Reset session\n", __func__,
task->tk_status);
nfs4_schedule_state_recovery(clp);
- task->tk_status = 0;
- return -EAGAIN;
+ goto restart_call;
#endif /* CONFIG_NFS_V4_1 */
+ case -NFS4ERR_MOVED:
+ rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
+ nfs4_schedule_migration_recovery(server);
+ if (test_bit(NFS4CLNT_MANAGER_RUNNING,
+ &clp->cl_state) == 0)
+ rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
+ goto restart_call;
case -NFS4ERR_DELAY:
nfs_inc_server_stats(server, NFSIOS_DELAY);
case -NFS4ERR_GRACE:
case -EKEYEXPIRED:
rpc_delay(task, NFS4_POLL_RETRY_MAX);
- task->tk_status = 0;
- return -EAGAIN;
+ goto restart_call;
case -NFS4ERR_OLD_STATEID:
- task->tk_status = 0;
- return -EAGAIN;
+ goto restart_call;
}
task->tk_status = nfs4_map_errors(task->tk_status);
return 0;
@@ -3543,6 +3573,7 @@ do_state_recovery:
nfs4_schedule_state_recovery(clp);
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
+restart_call:
task->tk_status = 0;
return -EAGAIN;
}
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH 12/12] NFS: Implement support for NFS4ERR_LEASE_MOVED
2011-03-14 12:56 [PATCH 00/12] Snapshot of client-side NFSv4 migration support (v2) Chuck Lever
` (10 preceding siblings ...)
2011-03-14 12:58 ` [PATCH 11/12] NFS: Add migration recovery callouts in nfs4proc.c Chuck Lever
@ 2011-03-14 12:58 ` Chuck Lever
11 siblings, 0 replies; 13+ messages in thread
From: Chuck Lever @ 2011-03-14 12:58 UTC (permalink / raw)
To: trond.myklebust; +Cc: linux-nfs
To recover from NFS4ERR_LEASE_MOVED, walk the cl_superblocks list and
invoke nfs4_try_migration() on each server's root file handle.
nfs4_try_migration() should automatically determine whether that file
system has migrated, and then perform recovery for it.
The per-filesystem migration probe also informs minor version zero
servers that this client should no longer receive NFS4ERR_LEASE_MOVED.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
fs/nfs/client.c | 1 +
fs/nfs/nfs4_fs.h | 2 ++
fs/nfs/nfs4proc.c | 10 ++++++++
fs/nfs/nfs4state.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
include/linux/nfs_fs_sb.h | 2 ++
5 files changed, 68 insertions(+), 1 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index a2f016a..f628526 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -179,6 +179,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion;
clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
+ clp->cl_mig_counter = 1;
#endif
cred = rpc_lookup_machine_cred();
if (!IS_ERR(cred))
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 8049c62..dc696c6 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -49,6 +49,7 @@ enum nfs4_client_state {
NFS4CLNT_RECALL_SLOT,
NFS4CLNT_UPDATE_CLIENTID,
NFS4CLNT_MOVED,
+ NFS4CLNT_LEASE_MOVED,
};
enum nfs4_session_state {
@@ -313,6 +314,7 @@ extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
extern void nfs4_schedule_state_recovery(struct nfs_client *);
extern void nfs4_schedule_migration_recovery(struct nfs_server *);
+extern void nfs4_schedule_lease_moved_recovery(struct nfs_client *);
extern void nfs4_schedule_state_manager(struct nfs_client *);
extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
extern int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 2537a18..3e8a2e3 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -288,6 +288,9 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode,
case -NFS4ERR_MOVED:
nfs4_schedule_migration_recovery(server);
goto recovery_wait;
+ case -NFS4ERR_LEASE_MOVED:
+ nfs4_schedule_lease_moved_recovery(clp);
+ goto recovery_wait;
case -NFS4ERR_FILE_OPEN:
if (exception->timeout > HZ) {
/* We have retried a decent amount, time to
@@ -3557,6 +3560,13 @@ static int nfs4_async_handle_error(struct rpc_task *task,
&clp->cl_state) == 0)
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
goto restart_call;
+ case -NFS4ERR_LEASE_MOVED:
+ rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
+ nfs4_schedule_lease_moved_recovery(clp);
+ if (test_bit(NFS4CLNT_MANAGER_RUNNING,
+ &clp->cl_state) == 0)
+ rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
+ goto restart_call;
case -NFS4ERR_DELAY:
nfs_inc_server_stats(server, NFSIOS_DELAY);
case -NFS4ERR_GRACE:
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index a4e69c3..1b521eb 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -60,6 +60,8 @@
#define OPENOWNER_POOL_SIZE 8
+static void nfs4_handle_lease_moved(struct nfs_client *clp);
+
const nfs4_stateid zero_stateid;
static LIST_HEAD(nfs4_clientid_list);
@@ -1043,6 +1045,23 @@ void nfs4_schedule_migration_recovery(struct nfs_server *server)
dprintk("<-- %s\n", __func__);
}
+/**
+ * nfs4_schedule_lease_moved_recovery - start lease moved recovery
+ *
+ * @clp: nfs_client of server that may have migrated file systems
+ *
+ */
+void nfs4_schedule_lease_moved_recovery(struct nfs_client *clp)
+{
+ dprintk("--> %s: \"%s\" (client ID %llx)\n",
+ __func__, clp->cl_hostname, clp->cl_clientid);
+
+ if (test_and_set_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state) == 0)
+ nfs4_schedule_state_manager(clp);
+
+ dprintk("<-- %s\n", __func__);
+}
+
int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
{
@@ -1348,11 +1367,13 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
nfs4_state_end_reclaim_reboot(clp);
return 0;
case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_LEASE_MOVED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_clear_reclaim_reboot(clp);
nfs4_state_start_reclaim_reboot(clp);
break;
+ case -NFS4ERR_LEASE_MOVED:
+ nfs4_handle_lease_moved(clp);
+ return 0;
case -NFS4ERR_EXPIRED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
@@ -1521,6 +1542,34 @@ out:
kfree(locations);
}
+static void nfs4_handle_lease_moved(struct nfs_client *clp)
+{
+ struct nfs_server *server;
+
+ dprintk("--> %s: \"%s\" (client ID %llx)\n",
+ __func__, clp->cl_hostname, clp->cl_clientid);
+
+ /*
+ * rcu_read_lock() must be dropped before trying each individual
+ * migration. cl_mig_counter is used to skip servers that have
+ * already been visited for this lease_moved event when the list
+ * walk is restarted.
+ */
+ clp->cl_mig_counter++;
+restart:
+ rcu_read_lock();
+ list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+ if (server->mig_counter != clp->cl_mig_counter) {
+ server->mig_counter = clp->cl_mig_counter;
+ rcu_read_unlock();
+ nfs4_try_migration(server);
+ goto restart;
+ }
+ rcu_read_unlock();
+
+ dprintk("<-- %s\n", __func__);
+}
+
#ifdef CONFIG_NFS_V4_1
void nfs41_handle_recall_slot(struct nfs_client *clp)
{
@@ -1710,6 +1759,9 @@ static void nfs4_state_manager(struct nfs_client *clp)
goto out_error;
}
+ if (test_and_clear_bit(NFS4CLNT_LEASE_MOVED, &clp->cl_state))
+ nfs4_handle_lease_moved(clp);
+
if (test_and_clear_bit(NFS4CLNT_MOVED, &clp->cl_state))
nfs4_try_migration(clp->cl_moved_server);
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 3741928..0ce4aad 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -57,6 +57,7 @@ struct nfs_client {
/* accessed only when NFS4CLNT_MOVED bit is set */
struct nfs_server * cl_moved_server;
+ unsigned long cl_mig_counter;
/* used for the setclientid verifier */
struct timespec cl_boot_time;
@@ -157,6 +158,7 @@ struct nfs_server {
struct list_head delegations;
void (*destroy)(struct nfs_server *);
struct nfs_fh *rootfh;
+ unsigned long mig_counter;
atomic_t active; /* Keep trace of any activity to this server */
^ permalink raw reply related [flat|nested] 13+ messages in thread