aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2013-10-17 14:12:50 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2013-10-28 15:24:00 -0400
commitb03d735b4ca2375d2251195cd848713bc55e7d79 (patch)
tree5c351a0fbdc47022c74c7db5a9f80235f9105f6a /fs/nfs
parent9e6ee76dfb7cd747fac5679542a9b45930173eec (diff)
NFS: Add method to retrieve fs_locations during migration recovery
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 in the compound, so the client needs to know the parent's file handle a priori. Unfortunately this function is not adequate for handling migration recovery. We need to probe fs_locations information on an FSID, but there's no parent directory available for many operations that can return NFS4ERR_MOVED. Another subtlety: recovering from NFS4ERR_LEASE_MOVED is a process of walking over a list of known FSIDs that reside on the server, and probing whether they have migrated. Once the server has detected that the client has probed all migrated file systems, it stops returning NFS4ERR_LEASE_MOVED. 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. However, the client ID is not an argument of either the PUTFH or GETATTR operations. Later minor versions have client ID information embedded in the compound's SEQUENCE operation. Therefore, by convention, minor version zero clients send a RENEW operation in the same compound as the GETATTR(fs_locations), since RENEW's one argument is a clientid4. This allows a minor version zero server to identify correctly the client that is probing for a migration. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/nfs4_fs.h4
-rw-r--r--fs/nfs/nfs4proc.c153
-rw-r--r--fs/nfs/nfs4xdr.c46
3 files changed, 192 insertions, 11 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 4f48000b2b97..e59d3b4c7944 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -205,6 +205,8 @@ struct nfs4_state_maintenance_ops {
205}; 205};
206 206
207struct nfs4_mig_recovery_ops { 207struct nfs4_mig_recovery_ops {
208 int (*get_locations)(struct inode *, struct nfs4_fs_locations *,
209 struct page *, struct rpc_cred *);
208}; 210};
209 211
210extern const struct dentry_operations nfs4_dentry_operations; 212extern const struct dentry_operations nfs4_dentry_operations;
@@ -237,6 +239,8 @@ extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait);
237extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); 239extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
238extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *, 240extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struct qstr *,
239 struct nfs4_fs_locations *, struct page *); 241 struct nfs4_fs_locations *, struct page *);
242extern int nfs4_proc_get_locations(struct inode *, struct nfs4_fs_locations *,
243 struct page *page, struct rpc_cred *);
240extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *, 244extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
241 struct nfs_fh *, struct nfs_fattr *); 245 struct nfs_fh *, struct nfs_fattr *);
242extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); 246extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 2aacd1357dc5..c71c16e0ac22 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5984,6 +5984,157 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
5984 return err; 5984 return err;
5985} 5985}
5986 5986
5987/*
5988 * This operation also signals the server that this client is
5989 * performing migration recovery. The server can stop returning
5990 * NFS4ERR_LEASE_MOVED to this client. A RENEW operation is
5991 * appended to this compound to identify the client ID which is
5992 * performing recovery.
5993 */
5994static int _nfs40_proc_get_locations(struct inode *inode,
5995 struct nfs4_fs_locations *locations,
5996 struct page *page, struct rpc_cred *cred)
5997{
5998 struct nfs_server *server = NFS_SERVER(inode);
5999 struct rpc_clnt *clnt = server->client;
6000 u32 bitmask[2] = {
6001 [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
6002 };
6003 struct nfs4_fs_locations_arg args = {
6004 .clientid = server->nfs_client->cl_clientid,
6005 .fh = NFS_FH(inode),
6006 .page = page,
6007 .bitmask = bitmask,
6008 .migration = 1, /* skip LOOKUP */
6009 .renew = 1, /* append RENEW */
6010 };
6011 struct nfs4_fs_locations_res res = {
6012 .fs_locations = locations,
6013 .migration = 1,
6014 .renew = 1,
6015 };
6016 struct rpc_message msg = {
6017 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
6018 .rpc_argp = &args,
6019 .rpc_resp = &res,
6020 .rpc_cred = cred,
6021 };
6022 unsigned long now = jiffies;
6023 int status;
6024
6025 nfs_fattr_init(&locations->fattr);
6026 locations->server = server;
6027 locations->nlocations = 0;
6028
6029 nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
6030 nfs4_set_sequence_privileged(&args.seq_args);
6031 status = nfs4_call_sync_sequence(clnt, server, &msg,
6032 &args.seq_args, &res.seq_res);
6033 if (status)
6034 return status;
6035
6036 renew_lease(server, now);
6037 return 0;
6038}
6039
6040#ifdef CONFIG_NFS_V4_1
6041
6042/*
6043 * This operation also signals the server that this client is
6044 * performing migration recovery. The server can stop asserting
6045 * SEQ4_STATUS_LEASE_MOVED for this client. The client ID
6046 * performing this operation is identified in the SEQUENCE
6047 * operation in this compound.
6048 *
6049 * When the client supports GETATTR(fs_locations_info), it can
6050 * be plumbed in here.
6051 */
6052static int _nfs41_proc_get_locations(struct inode *inode,
6053 struct nfs4_fs_locations *locations,
6054 struct page *page, struct rpc_cred *cred)
6055{
6056 struct nfs_server *server = NFS_SERVER(inode);
6057 struct rpc_clnt *clnt = server->client;
6058 u32 bitmask[2] = {
6059 [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
6060 };
6061 struct nfs4_fs_locations_arg args = {
6062 .fh = NFS_FH(inode),
6063 .page = page,
6064 .bitmask = bitmask,
6065 .migration = 1, /* skip LOOKUP */
6066 };
6067 struct nfs4_fs_locations_res res = {
6068 .fs_locations = locations,
6069 .migration = 1,
6070 };
6071 struct rpc_message msg = {
6072 .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
6073 .rpc_argp = &args,
6074 .rpc_resp = &res,
6075 .rpc_cred = cred,
6076 };
6077 int status;
6078
6079 nfs_fattr_init(&locations->fattr);
6080 locations->server = server;
6081 locations->nlocations = 0;
6082
6083 nfs4_init_sequence(&args.seq_args, &res.seq_res, 0);
6084 nfs4_set_sequence_privileged(&args.seq_args);
6085 status = nfs4_call_sync_sequence(clnt, server, &msg,
6086 &args.seq_args, &res.seq_res);
6087 if (status == NFS4_OK &&
6088 res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED)
6089 status = -NFS4ERR_LEASE_MOVED;
6090 return status;
6091}
6092
6093#endif /* CONFIG_NFS_V4_1 */
6094
6095/**
6096 * nfs4_proc_get_locations - discover locations for a migrated FSID
6097 * @inode: inode on FSID that is migrating
6098 * @locations: result of query
6099 * @page: buffer
6100 * @cred: credential to use for this operation
6101 *
6102 * Returns NFS4_OK on success, a negative NFS4ERR status code if the
6103 * operation failed, or a negative errno if a local error occurred.
6104 *
6105 * On success, "locations" is filled in, but if the server has
6106 * no locations information, NFS_ATTR_FATTR_V4_LOCATIONS is not
6107 * asserted.
6108 *
6109 * -NFS4ERR_LEASE_MOVED is returned if the server still has leases
6110 * from this client that require migration recovery.
6111 */
6112int nfs4_proc_get_locations(struct inode *inode,
6113 struct nfs4_fs_locations *locations,
6114 struct page *page, struct rpc_cred *cred)
6115{
6116 struct nfs_server *server = NFS_SERVER(inode);
6117 struct nfs_client *clp = server->nfs_client;
6118 const struct nfs4_mig_recovery_ops *ops =
6119 clp->cl_mvops->mig_recovery_ops;
6120 struct nfs4_exception exception = { };
6121 int status;
6122
6123 dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__,
6124 (unsigned long long)server->fsid.major,
6125 (unsigned long long)server->fsid.minor,
6126 clp->cl_hostname);
6127 nfs_display_fhandle(NFS_FH(inode), __func__);
6128
6129 do {
6130 status = ops->get_locations(inode, locations, page, cred);
6131 if (status != -NFS4ERR_DELAY)
6132 break;
6133 nfs4_handle_exception(server, status, &exception);
6134 } while (exception.retry);
6135 return status;
6136}
6137
5987/** 6138/**
5988 * If 'use_integrity' is true and the state managment nfs_client 6139 * If 'use_integrity' is true and the state managment nfs_client
5989 * cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient 6140 * cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient
@@ -7882,10 +8033,12 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
7882#endif 8033#endif
7883 8034
7884static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = { 8035static const struct nfs4_mig_recovery_ops nfs40_mig_recovery_ops = {
8036 .get_locations = _nfs40_proc_get_locations,
7885}; 8037};
7886 8038
7887#if defined(CONFIG_NFS_V4_1) 8039#if defined(CONFIG_NFS_V4_1)
7888static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = { 8040static const struct nfs4_mig_recovery_ops nfs41_mig_recovery_ops = {
8041 .get_locations = _nfs41_proc_get_locations,
7889}; 8042};
7890#endif /* CONFIG_NFS_V4_1 */ 8043#endif /* CONFIG_NFS_V4_1 */
7891 8044
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 79210d23f607..1854b04f828f 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -736,13 +736,15 @@ static int nfs4_stat_to_errno(int);
736 encode_sequence_maxsz + \ 736 encode_sequence_maxsz + \
737 encode_putfh_maxsz + \ 737 encode_putfh_maxsz + \
738 encode_lookup_maxsz + \ 738 encode_lookup_maxsz + \
739 encode_fs_locations_maxsz) 739 encode_fs_locations_maxsz + \
740 encode_renew_maxsz)
740#define NFS4_dec_fs_locations_sz \ 741#define NFS4_dec_fs_locations_sz \
741 (compound_decode_hdr_maxsz + \ 742 (compound_decode_hdr_maxsz + \
742 decode_sequence_maxsz + \ 743 decode_sequence_maxsz + \
743 decode_putfh_maxsz + \ 744 decode_putfh_maxsz + \
744 decode_lookup_maxsz + \ 745 decode_lookup_maxsz + \
745 decode_fs_locations_maxsz) 746 decode_fs_locations_maxsz + \
747 decode_renew_maxsz)
746#define NFS4_enc_secinfo_sz (compound_encode_hdr_maxsz + \ 748#define NFS4_enc_secinfo_sz (compound_encode_hdr_maxsz + \
747 encode_sequence_maxsz + \ 749 encode_sequence_maxsz + \
748 encode_putfh_maxsz + \ 750 encode_putfh_maxsz + \
@@ -2687,11 +2689,20 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
2687 2689
2688 encode_compound_hdr(xdr, req, &hdr); 2690 encode_compound_hdr(xdr, req, &hdr);
2689 encode_sequence(xdr, &args->seq_args, &hdr); 2691 encode_sequence(xdr, &args->seq_args, &hdr);
2690 encode_putfh(xdr, args->dir_fh, &hdr); 2692 if (args->migration) {
2691 encode_lookup(xdr, args->name, &hdr); 2693 encode_putfh(xdr, args->fh, &hdr);
2692 replen = hdr.replen; /* get the attribute into args->page */ 2694 replen = hdr.replen;
2693 encode_fs_locations(xdr, args->bitmask, &hdr); 2695 encode_fs_locations(xdr, args->bitmask, &hdr);
2696 if (args->renew)
2697 encode_renew(xdr, args->clientid, &hdr);
2698 } else {
2699 encode_putfh(xdr, args->dir_fh, &hdr);
2700 encode_lookup(xdr, args->name, &hdr);
2701 replen = hdr.replen;
2702 encode_fs_locations(xdr, args->bitmask, &hdr);
2703 }
2694 2704
2705 /* Set up reply kvec to capture returned fs_locations array. */
2695 xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page, 2706 xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page,
2696 0, PAGE_SIZE); 2707 0, PAGE_SIZE);
2697 encode_nops(&hdr); 2708 encode_nops(&hdr);
@@ -6824,13 +6835,26 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
6824 status = decode_putfh(xdr); 6835 status = decode_putfh(xdr);
6825 if (status) 6836 if (status)
6826 goto out; 6837 goto out;
6827 status = decode_lookup(xdr); 6838 if (res->migration) {
6828 if (status) 6839 xdr_enter_page(xdr, PAGE_SIZE);
6829 goto out; 6840 status = decode_getfattr_generic(xdr,
6830 xdr_enter_page(xdr, PAGE_SIZE); 6841 &res->fs_locations->fattr,
6831 status = decode_getfattr_generic(xdr, &res->fs_locations->fattr,
6832 NULL, res->fs_locations, 6842 NULL, res->fs_locations,
6833 NULL, res->fs_locations->server); 6843 NULL, res->fs_locations->server);
6844 if (status)
6845 goto out;
6846 if (res->renew)
6847 status = decode_renew(xdr);
6848 } else {
6849 status = decode_lookup(xdr);
6850 if (status)
6851 goto out;
6852 xdr_enter_page(xdr, PAGE_SIZE);
6853 status = decode_getfattr_generic(xdr,
6854 &res->fs_locations->fattr,
6855 NULL, res->fs_locations,
6856 NULL, res->fs_locations->server);
6857 }
6834out: 6858out:
6835 return status; 6859 return status;
6836} 6860}