Commit 1fbf3e48 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfs-for-5.1-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable fixes:
   - Fixes for NFS I/O request leakages
   - Fix error handling paths in the NFS I/O recoalescing code
   - Reinitialise NFSv4.1 sequence results before retransmitting a
     request
   - Fix a soft lockup in the delegation recovery code
   - Bulk destroy of layouts needs to be safe w.r.t. umount
   - Prevent thundering herd issues when the SUNRPC socket is not
     connected
   - Respect RPC call timeouts when retrying transmission

  Features:
   - Convert rpc auth layer to use xdr_streams
   - Config option to disable insecure RPCSEC_GSS crypto types
   - Reduce size of RPC receive buffers
   - Readdirplus optimization by cache mechanism
   - Convert SUNRPC socket send code to use iov_iter()
   - SUNRPC micro-optimisations to avoid indirect calls
   - Add support for the pNFS LAYOUTERROR operation and use it with the
     pNFS/flexfiles driver
   - Add trace events to report non-zero NFS status codes
   - Various removals of unnecessary dprintks

  Bugfixes and cleanups:
   - Fix a number of sparse warnings and documentation format warnings
   - Fix nfs_parse_devname to not modify it's argument
   - Fix potential corruption of page being written through pNFS/blocks
   - fix xfstest generic/099 failures on nfsv3
   - Avoid NFSv4.1 "false retries" when RPC calls are interrupted
   - Abort I/O early if the pNFS/flexfiles layout segment was
     invalidated
   - Avoid unnecessary pNFS/flexfiles layout invalidations"

* tag 'nfs-for-5.1-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (90 commits)
  SUNRPC: Take the transport send lock before binding+connecting
  SUNRPC: Micro-optimise when the task is known not to be sleeping
  SUNRPC: Check whether the task was transmitted before rebind/reconnect
  SUNRPC: Remove redundant calls to RPC_IS_QUEUED()
  SUNRPC: Clean up
  SUNRPC: Respect RPC call timeouts when retrying transmission
  SUNRPC: Fix up RPC back channel transmission
  SUNRPC: Prevent thundering herd when the socket is not connected
  SUNRPC: Allow dynamic allocation of back channel slots
  NFSv4.1: Bump the default callback session slot count to 16
  SUNRPC: Convert remaining GFP_NOIO, and GFP_NOWAIT sites in sunrpc
  NFS/flexfiles: Clean up mirror DS initialisation
  NFS/flexfiles: Remove dead code in ff_layout_mirror_valid()
  NFS/flexfile: Simplify nfs4_ff_layout_select_ds_stateid()
  NFS/flexfile: Simplify nfs4_ff_layout_ds_version()
  NFS/flexfiles: Simplify ff_layout_get_ds_cred()
  NFS/flexfiles: Simplify nfs4_ff_find_or_create_ds_client()
  NFS/flexfiles: Simplify nfs4_ff_layout_select_ds_fh()
  NFS/flexfiles: Speed up read failover when DSes are down
  NFS/flexfiles: Don't invalidate DS deviceids for being unresponsive
  ...
parents f88c5942 4d6c671a
...@@ -74,17 +74,6 @@ static void nlm4_compute_offsets(const struct nlm_lock *lock, ...@@ -74,17 +74,6 @@ static void nlm4_compute_offsets(const struct nlm_lock *lock,
*l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1); *l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
} }
/*
* Handle decode buffer overflows out-of-line.
*/
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("lockd: %s prematurely hit the end of our receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/* /*
* Encode/decode NLMv4 basic data types * Encode/decode NLMv4 basic data types
* *
...@@ -176,7 +165,6 @@ static int decode_cookie(struct xdr_stream *xdr, ...@@ -176,7 +165,6 @@ static int decode_cookie(struct xdr_stream *xdr,
dprintk("NFS: returned cookie was too long: %u\n", length); dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO; return -EIO;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
...@@ -236,7 +224,6 @@ static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat) ...@@ -236,7 +224,6 @@ static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat)
__func__, be32_to_cpup(p)); __func__, be32_to_cpup(p));
return -EIO; return -EIO;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
...@@ -309,7 +296,6 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result) ...@@ -309,7 +296,6 @@ static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
out: out:
return error; return error;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
......
...@@ -70,17 +70,6 @@ static void nlm_compute_offsets(const struct nlm_lock *lock, ...@@ -70,17 +70,6 @@ static void nlm_compute_offsets(const struct nlm_lock *lock,
*l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1); *l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
} }
/*
* Handle decode buffer overflows out-of-line.
*/
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("lockd: %s prematurely hit the end of our receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/* /*
* Encode/decode NLMv3 basic data types * Encode/decode NLMv3 basic data types
* *
...@@ -173,7 +162,6 @@ static int decode_cookie(struct xdr_stream *xdr, ...@@ -173,7 +162,6 @@ static int decode_cookie(struct xdr_stream *xdr,
dprintk("NFS: returned cookie was too long: %u\n", length); dprintk("NFS: returned cookie was too long: %u\n", length);
return -EIO; return -EIO;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
...@@ -231,7 +219,6 @@ static int decode_nlm_stat(struct xdr_stream *xdr, ...@@ -231,7 +219,6 @@ static int decode_nlm_stat(struct xdr_stream *xdr,
__func__, be32_to_cpup(p)); __func__, be32_to_cpup(p));
return -EIO; return -EIO;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
...@@ -303,7 +290,6 @@ static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result) ...@@ -303,7 +290,6 @@ static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result)
out: out:
return error; return error;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
......
...@@ -72,16 +72,6 @@ static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p) ...@@ -72,16 +72,6 @@ static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p)
return xdr_ressize_check(rqstp, p); return xdr_ressize_check(rqstp, p);
} }
static __be32 *read_buf(struct xdr_stream *xdr, size_t nbytes)
{
__be32 *p;
p = xdr_inline_decode(xdr, nbytes);
if (unlikely(p == NULL))
printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n");
return p;
}
static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len,
const char **str, size_t maxlen) const char **str, size_t maxlen)
{ {
...@@ -98,13 +88,13 @@ static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh) ...@@ -98,13 +88,13 @@ static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
{ {
__be32 *p; __be32 *p;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
fh->size = ntohl(*p); fh->size = ntohl(*p);
if (fh->size > NFS4_FHSIZE) if (fh->size > NFS4_FHSIZE)
return htonl(NFS4ERR_BADHANDLE); return htonl(NFS4ERR_BADHANDLE);
p = read_buf(xdr, fh->size); p = xdr_inline_decode(xdr, fh->size);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
memcpy(&fh->data[0], p, fh->size); memcpy(&fh->data[0], p, fh->size);
...@@ -117,11 +107,11 @@ static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) ...@@ -117,11 +107,11 @@ static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
__be32 *p; __be32 *p;
unsigned int attrlen; unsigned int attrlen;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
attrlen = ntohl(*p); attrlen = ntohl(*p);
p = read_buf(xdr, attrlen << 2); p = xdr_inline_decode(xdr, attrlen << 2);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
if (likely(attrlen > 0)) if (likely(attrlen > 0))
...@@ -135,7 +125,7 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid) ...@@ -135,7 +125,7 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{ {
__be32 *p; __be32 *p;
p = read_buf(xdr, NFS4_STATEID_SIZE); p = xdr_inline_decode(xdr, NFS4_STATEID_SIZE);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
memcpy(stateid->data, p, NFS4_STATEID_SIZE); memcpy(stateid->data, p, NFS4_STATEID_SIZE);
...@@ -156,7 +146,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound ...@@ -156,7 +146,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
status = decode_string(xdr, &hdr->taglen, &hdr->tag, CB_OP_TAGLEN_MAXSZ); status = decode_string(xdr, &hdr->taglen, &hdr->tag, CB_OP_TAGLEN_MAXSZ);
if (unlikely(status != 0)) if (unlikely(status != 0))
return status; return status;
p = read_buf(xdr, 12); p = xdr_inline_decode(xdr, 12);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
hdr->minorversion = ntohl(*p++); hdr->minorversion = ntohl(*p++);
...@@ -176,7 +166,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound ...@@ -176,7 +166,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op) static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
{ {
__be32 *p; __be32 *p;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE_HDR); return htonl(NFS4ERR_RESOURCE_HDR);
*op = ntohl(*p); *op = ntohl(*p);
...@@ -205,7 +195,7 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, ...@@ -205,7 +195,7 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp,
status = decode_delegation_stateid(xdr, &args->stateid); status = decode_delegation_stateid(xdr, &args->stateid);
if (unlikely(status != 0)) if (unlikely(status != 0))
return status; return status;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
args->truncate = ntohl(*p); args->truncate = ntohl(*p);
...@@ -227,7 +217,7 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, ...@@ -227,7 +217,7 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
__be32 status = 0; __be32 status = 0;
uint32_t iomode; uint32_t iomode;
p = read_buf(xdr, 4 * sizeof(uint32_t)); p = xdr_inline_decode(xdr, 4 * sizeof(uint32_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
...@@ -245,14 +235,14 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, ...@@ -245,14 +235,14 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
if (unlikely(status != 0)) if (unlikely(status != 0))
return status; return status;
p = read_buf(xdr, 2 * sizeof(uint64_t)); p = xdr_inline_decode(xdr, 2 * sizeof(uint64_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
p = xdr_decode_hyper(p, &args->cbl_range.offset); p = xdr_decode_hyper(p, &args->cbl_range.offset);
p = xdr_decode_hyper(p, &args->cbl_range.length); p = xdr_decode_hyper(p, &args->cbl_range.length);
return decode_layout_stateid(xdr, &args->cbl_stateid); return decode_layout_stateid(xdr, &args->cbl_stateid);
} else if (args->cbl_recall_type == RETURN_FSID) { } else if (args->cbl_recall_type == RETURN_FSID) {
p = read_buf(xdr, 2 * sizeof(uint64_t)); p = xdr_inline_decode(xdr, 2 * sizeof(uint64_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
p = xdr_decode_hyper(p, &args->cbl_fsid.major); p = xdr_decode_hyper(p, &args->cbl_fsid.major);
...@@ -275,7 +265,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, ...@@ -275,7 +265,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
args->ndevs = 0; args->ndevs = 0;
/* Num of device notifications */ /* Num of device notifications */
p = read_buf(xdr, sizeof(uint32_t)); p = xdr_inline_decode(xdr, sizeof(uint32_t));
if (unlikely(p == NULL)) { if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR); status = htonl(NFS4ERR_BADXDR);
goto out; goto out;
...@@ -298,7 +288,8 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, ...@@ -298,7 +288,8 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
struct cb_devicenotifyitem *dev = &args->devs[i]; struct cb_devicenotifyitem *dev = &args->devs[i];
p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE); p = xdr_inline_decode(xdr, (4 * sizeof(uint32_t)) +
NFS4_DEVICEID4_SIZE);
if (unlikely(p == NULL)) { if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR); status = htonl(NFS4ERR_BADXDR);
goto err; goto err;
...@@ -329,7 +320,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp, ...@@ -329,7 +320,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) { if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) {
p = read_buf(xdr, sizeof(uint32_t)); p = xdr_inline_decode(xdr, sizeof(uint32_t));
if (unlikely(p == NULL)) { if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_BADXDR); status = htonl(NFS4ERR_BADXDR);
goto err; goto err;
...@@ -359,7 +350,7 @@ static __be32 decode_sessionid(struct xdr_stream *xdr, ...@@ -359,7 +350,7 @@ static __be32 decode_sessionid(struct xdr_stream *xdr,
{ {
__be32 *p; __be32 *p;
p = read_buf(xdr, NFS4_MAX_SESSIONID_LEN); p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
...@@ -379,13 +370,13 @@ static __be32 decode_rc_list(struct xdr_stream *xdr, ...@@ -379,13 +370,13 @@ static __be32 decode_rc_list(struct xdr_stream *xdr,
goto out; goto out;
status = htonl(NFS4ERR_RESOURCE); status = htonl(NFS4ERR_RESOURCE);
p = read_buf(xdr, sizeof(uint32_t)); p = xdr_inline_decode(xdr, sizeof(uint32_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
goto out; goto out;
rc_list->rcl_nrefcalls = ntohl(*p++); rc_list->rcl_nrefcalls = ntohl(*p++);
if (rc_list->rcl_nrefcalls) { if (rc_list->rcl_nrefcalls) {
p = read_buf(xdr, p = xdr_inline_decode(xdr,
rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t)); rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
goto out; goto out;
...@@ -418,7 +409,7 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp, ...@@ -418,7 +409,7 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp,
if (status) if (status)
return status; return status;
p = read_buf(xdr, 5 * sizeof(uint32_t)); p = xdr_inline_decode(xdr, 5 * sizeof(uint32_t));
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE); return htonl(NFS4ERR_RESOURCE);
...@@ -461,7 +452,7 @@ static __be32 decode_recallany_args(struct svc_rqst *rqstp, ...@@ -461,7 +452,7 @@ static __be32 decode_recallany_args(struct svc_rqst *rqstp,
uint32_t bitmap[2]; uint32_t bitmap[2];
__be32 *p, status; __be32 *p, status;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
args->craa_objs_to_keep = ntohl(*p++); args->craa_objs_to_keep = ntohl(*p++);
...@@ -480,7 +471,7 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp, ...@@ -480,7 +471,7 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
struct cb_recallslotargs *args = argp; struct cb_recallslotargs *args = argp;
__be32 *p; __be32 *p;
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
args->crsa_target_highest_slotid = ntohl(*p++); args->crsa_target_highest_slotid = ntohl(*p++);
...@@ -492,14 +483,14 @@ static __be32 decode_lockowner(struct xdr_stream *xdr, struct cb_notify_lock_arg ...@@ -492,14 +483,14 @@ static __be32 decode_lockowner(struct xdr_stream *xdr, struct cb_notify_lock_arg
__be32 *p; __be32 *p;
unsigned int len; unsigned int len;
p = read_buf(xdr, 12); p = xdr_inline_decode(xdr, 12);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
p = xdr_decode_hyper(p, &args->cbnl_owner.clientid); p = xdr_decode_hyper(p, &args->cbnl_owner.clientid);
len = be32_to_cpu(*p); len = be32_to_cpu(*p);
p = read_buf(xdr, len); p = xdr_inline_decode(xdr, len);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
return htonl(NFS4ERR_BADXDR); return htonl(NFS4ERR_BADXDR);
...@@ -537,7 +528,7 @@ static __be32 decode_write_response(struct xdr_stream *xdr, ...@@ -537,7 +528,7 @@ static __be32 decode_write_response(struct xdr_stream *xdr,
__be32 *p; __be32 *p;
/* skip the always zero field */ /* skip the always zero field */
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
goto out; goto out;
p++; p++;
...@@ -577,7 +568,7 @@ static __be32 decode_offload_args(struct svc_rqst *rqstp, ...@@ -577,7 +568,7 @@ static __be32 decode_offload_args(struct svc_rqst *rqstp,
return status; return status;
/* decode status */ /* decode status */
p = read_buf(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
goto out; goto out;
args->error = ntohl(*p++); args->error = ntohl(*p++);
...@@ -943,10 +934,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) ...@@ -943,10 +934,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
}; };
unsigned int nops = 0; unsigned int nops = 0;
xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); xdr_init_decode(&xdr_in, &rqstp->rq_arg,
rqstp->rq_arg.head[0].iov_base, NULL);
p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
xdr_init_encode(&xdr_out, &rqstp->rq_res, p); xdr_init_encode(&xdr_out, &rqstp->rq_res, p, NULL);
status = decode_compound_hdr_arg(&xdr_in, &hdr_arg); status = decode_compound_hdr_arg(&xdr_in, &hdr_arg);
if (status == htonl(NFS4ERR_RESOURCE)) if (status == htonl(NFS4ERR_RESOURCE))
......
...@@ -229,6 +229,8 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation ...@@ -229,6 +229,8 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
if (delegation->inode != NULL) if (delegation->inode != NULL)
inode = igrab(delegation->inode); inode = igrab(delegation->inode);
if (!inode)
set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
return inode; return inode;
} }
...@@ -681,7 +683,7 @@ void nfs_expire_all_delegations(struct nfs_client *clp) ...@@ -681,7 +683,7 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
/** /**
* nfs_super_return_all_delegations - return delegations for one superblock * nfs_super_return_all_delegations - return delegations for one superblock
* @sb: sb to process * @server: pointer to nfs_server to process
* *
*/ */
void nfs_server_return_all_delegations(struct nfs_server *server) void nfs_server_return_all_delegations(struct nfs_server *server)
...@@ -944,10 +946,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) ...@@ -944,10 +946,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations, list_for_each_entry_rcu(delegation, &server->delegations,
super_list) { super_list) {
if (test_bit(NFS_DELEGATION_RETURNING, if (test_bit(NFS_DELEGATION_INODE_FREEING,
&delegation->flags)) &delegation->flags) ||
continue; test_bit(NFS_DELEGATION_RETURNING,
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) ||
test_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags) == 0) &delegation->flags) == 0)
continue; continue;
if (!nfs_sb_active(server->super)) if (!nfs_sb_active(server->super))
...@@ -1053,10 +1056,11 @@ void nfs_reap_expired_delegations(struct nfs_client *clp) ...@@ -1053,10 +1056,11 @@ void nfs_reap_expired_delegations(struct nfs_client *clp)
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations, list_for_each_entry_rcu(delegation, &server->delegations,
super_list) { super_list) {
if (test_bit(NFS_DELEGATION_RETURNING, if (test_bit(NFS_DELEGATION_INODE_FREEING,
&delegation->flags)) &delegation->flags) ||
continue; test_bit(NFS_DELEGATION_RETURNING,
if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags) ||
test_bit(NFS_DELEGATION_TEST_EXPIRED,
&delegation->flags) == 0) &delegation->flags) == 0)
continue; continue;
if (!nfs_sb_active(server->super)) if (!nfs_sb_active(server->super))
......
...@@ -34,6 +34,7 @@ enum { ...@@ -34,6 +34,7 @@ enum {
NFS_DELEGATION_RETURNING, NFS_DELEGATION_RETURNING,
NFS_DELEGATION_REVOKED, NFS_DELEGATION_REVOKED,
NFS_DELEGATION_TEST_EXPIRED, NFS_DELEGATION_TEST_EXPIRED,
NFS_DELEGATION_INODE_FREEING,
}; };
int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
......
...@@ -139,12 +139,19 @@ struct nfs_cache_array { ...@@ -139,12 +139,19 @@ struct nfs_cache_array {
struct nfs_cache_array_entry array[0]; struct nfs_cache_array_entry array[0];
}; };
struct readdirvec {
unsigned long nr;
unsigned long index;
struct page *pages[NFS_MAX_READDIR_RAPAGES];
};
typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, bool); typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, bool);
typedef struct { typedef struct {
struct file *file; struct file *file;
struct page *page; struct page *page;
struct dir_context *ctx; struct dir_context *ctx;
unsigned long page_index; unsigned long page_index;
struct readdirvec pvec;
u64 *dir_cookie; u64 *dir_cookie;
u64 last_cookie; u64 last_cookie;
loff_t current_index; loff_t current_index;
...@@ -524,6 +531,10 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en ...@@ -524,6 +531,10 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
struct nfs_cache_array *array; struct nfs_cache_array *array;
unsigned int count = 0; unsigned int count = 0;
int status; int status;
int max_rapages = NFS_MAX_READDIR_RAPAGES;
desc->pvec.index = desc->page_index;
desc->pvec.nr = 0;
scratch = alloc_page(GFP_KERNEL); scratch = alloc_page(GFP_KERNEL);
if (scratch == NULL) if (scratch == NULL)
...@@ -548,20 +559,40 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en ...@@ -548,20 +559,40 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
if (desc->plus) if (desc->plus)
nfs_prime_dcache(file_dentry(desc->file), entry); nfs_prime_dcache(file_dentry(desc->file), entry);
status = nfs_readdir_add_to_array(entry, page); status = nfs_readdir_add_to_array(entry, desc->pvec.pages[desc->pvec.nr]);
if (status == -ENOSPC) {
desc->pvec.nr++;
if (desc->pvec.nr == max_rapages)
break;
status = nfs_readdir_add_to_array(entry, desc->pvec.pages[desc->pvec.nr]);
}
if (status != 0) if (status != 0)
break; break;
} while (!entry->eof); } while (!entry->eof);
/*
* page and desc->pvec.pages[0] are valid, don't need to check
* whether or not to be NULL.
*/
copy_highpage(page, desc->pvec.pages[0]);
out_nopages: out_nopages:
if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) { if (count == 0 || (status == -EBADCOOKIE && entry->eof != 0)) {
array = kmap(page); array = kmap_atomic(desc->pvec.pages[desc->pvec.nr]);
array->eof_index = array->size; array->eof_index = array->size;
status = 0; status = 0;
kunmap(page); kunmap_atomic(array);
} }
put_page(scratch); put_page(scratch);
/*
* desc->pvec.nr > 0 means at least one page was completely filled,
* we should return -ENOSPC. Otherwise function
* nfs_readdir_xdr_to_array will enter infinite loop.
*/
if (desc->pvec.nr > 0)
return -ENOSPC;
return status; return status;
} }
...@@ -574,8 +605,8 @@ void nfs_readdir_free_pages(struct page **pages, unsigned int npages) ...@@ -574,8 +605,8 @@ void nfs_readdir_free_pages(struct page **pages, unsigned int npages)
} }
/* /*
* nfs_readdir_large_page will allocate pages that must be freed with a call * nfs_readdir_alloc_pages() will allocate pages that must be freed with a call
* to nfs_readdir_free_pagearray * to nfs_readdir_free_pages()
*/ */
static static
int nfs_readdir_alloc_pages(struct page **pages, unsigned int npages) int nfs_readdir_alloc_pages(struct page **pages, unsigned int npages)
...@@ -595,6 +626,24 @@ int nfs_readdir_alloc_pages(struct page **pages, unsigned int npages) ...@@ -595,6 +626,24 @@ int nfs_readdir_alloc_pages(struct page **pages, unsigned int npages)
return -ENOMEM; return -ENOMEM;
} }
/*
* nfs_readdir_rapages_init initialize rapages by nfs_cache_array structure.
*/
static
void nfs_readdir_rapages_init(nfs_readdir_descriptor_t *desc)
{
struct nfs_cache_array *array;
int max_rapages = NFS_MAX_READDIR_RAPAGES;
int index;
for (index = 0; index < max_rapages; index++) {
array = kmap_atomic(desc->pvec.pages[index]);
memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1;
kunmap_atomic(array);
}
}
static static
int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode)
{ {
...@@ -605,6 +654,12 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, ...@@ -605,6 +654,12 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
int status = -ENOMEM; int status = -ENOMEM;
unsigned int array_size = ARRAY_SIZE(pages); unsigned int array_size = ARRAY_SIZE(pages);
/*
* This means we hit readdir rdpages miss, the preallocated rdpages
* are useless, the preallocate rdpages should be reinitialized.
*/
nfs_readdir_rapages_init(desc);
entry.prev_cookie = 0; entry.prev_cookie = 0;
entry.cookie = desc->last_cookie; entry.cookie = desc->last_cookie;
entry.eof = 0; entry.eof = 0;
...@@ -664,9 +719,24 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) ...@@ -664,9 +719,24 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
struct inode *inode = file_inode(desc->file); struct inode *inode = file_inode(desc->file);
int ret; int ret;
ret = nfs_readdir_xdr_to_array(desc, page, inode); /*
if (ret < 0) * If desc->page_index in range desc->pvec.index and
goto error; * desc->pvec.index + desc->pvec.nr, we get readdir cache hit.
*/
if (desc->page_index >= desc->pvec.index &&
desc->page_index < (desc->pvec.index + desc->pvec.nr)) {
/*
* page and desc->pvec.pages[x] are valid, don't need to check
* whether or not to be NULL.
*/
copy_highpage(page, desc->pvec.pages[desc->page_index - desc->pvec.index]);
ret = 0;
} else {
ret = nfs_readdir_xdr_to_array(desc, page, inode);
if (ret < 0)
goto error;
}
SetPageUptodate(page); SetPageUptodate(page);
if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) {
...@@ -831,6 +901,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) ...@@ -831,6 +901,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
*desc = &my_desc; *desc = &my_desc;
struct nfs_open_dir_context *dir_ctx = file->private_data; struct nfs_open_dir_context *dir_ctx = file->private_data;
int res = 0; int res = 0;
int max_rapages = NFS_MAX_READDIR_RAPAGES;
dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n", dfprintk(FILE, "NFS: readdir(%pD2) starting at cookie %llu\n",
file, (long long)ctx->pos); file, (long long)ctx->pos);
...@@ -850,6 +921,12 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) ...@@ -850,6 +921,12 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
desc->decode = NFS_PROTO(inode)->decode_dirent; desc->decode = NFS_PROTO(inode)->decode_dirent;
desc->plus = nfs_use_readdirplus(inode, ctx); desc->plus = nfs_use_readdirplus(inode, ctx);
res = nfs_readdir_alloc_pages(desc->pvec.pages, max_rapages);
if (res < 0)
return -ENOMEM;
nfs_readdir_rapages_init(desc);
if (ctx->pos == 0 || nfs_attribute_cache_expired(inode)) if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
res = nfs_revalidate_mapping(inode, file->f_mapping); res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0) if (res < 0)
...@@ -885,6 +962,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) ...@@ -885,6 +962,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
break; break;
} while (!desc->eof); } while (!desc->eof);
out: out:
nfs_readdir_free_pages(desc->pvec.pages, max_rapages);
if (res > 0) if (res > 0)
res = 0; res = 0;
dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res); dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res);
...@@ -945,7 +1023,7 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, ...@@ -945,7 +1023,7 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
/** /**
* nfs_force_lookup_revalidate - Mark the directory as having changed * nfs_force_lookup_revalidate - Mark the directory as having changed
* @dir - pointer to directory inode * @dir: pointer to directory inode
* *
* This forces the revalidation code in nfs_lookup_revalidate() to do a * This forces the revalidation code in nfs_lookup_revalidate() to do a
* full lookup on all child dentries of 'dir' whenever a change occurs * full lookup on all child dentries of 'dir' whenever a change occurs
...@@ -1649,7 +1727,7 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, ...@@ -1649,7 +1727,7 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
reval_dentry: reval_dentry:
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
return nfs_lookup_revalidate_dentry(dir, dentry, inode);; return nfs_lookup_revalidate_dentry(dir, dentry, inode);
full_reval: full_reval:
return nfs_do_lookup_revalidate(dir, dentry, flags); return nfs_do_lookup_revalidate(dir, dentry, flags);
......
...@@ -428,7 +428,7 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr) ...@@ -428,7 +428,7 @@ static void nfs_direct_read_completion(struct nfs_pgio_header *hdr)
hdr->release(hdr); hdr->release(hdr);
} }
static void nfs_read_sync_pgio_error(struct list_head *head) static void nfs_read_sync_pgio_error(struct list_head *head, int error)
{ {
struct nfs_page *req; struct nfs_page *req;
...@@ -664,8 +664,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) ...@@ -664,8 +664,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
list_for_each_entry_safe(req, tmp, &reqs, wb_list) { list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
if (!nfs_pageio_add_request(&desc, req)) { if (!nfs_pageio_add_request(&desc, req)) {
nfs_list_remove_request(req); nfs_list_move_request(req, &failed);
nfs_list_add_request(req, &failed);
spin_lock(&cinfo.inode->i_lock); spin_lock(&cinfo.inode->i_lock);
dreq->flags = 0; dreq->flags = 0;
if (desc.pg_error < 0) if (desc.pg_error < 0)
...@@ -821,7 +820,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) ...@@ -821,7 +820,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)
hdr->release(hdr); hdr->release(hdr);
} }
static void nfs_write_sync_pgio_error(struct list_head *head) static void nfs_write_sync_pgio_error(struct list_head *head, int error)
{ {
struct nfs_page *req; struct nfs_page *req;
......
...@@ -89,8 +89,8 @@ EXPORT_SYMBOL_GPL(nfs_file_release); ...@@ -89,8 +89,8 @@ EXPORT_SYMBOL_GPL(nfs_file_release);
/** /**
* nfs_revalidate_size - Revalidate the file size * nfs_revalidate_size - Revalidate the file size
* @inode - pointer to inode struct * @inode: pointer to inode struct
* @file - pointer to struct file * @filp: pointer to struct file
* *
* Revalidates the file length. This is basically a wrapper around * Revalidates the file length. This is basically a wrapper around
* nfs_revalidate_inode() that takes into account the fact that we may * nfs_revalidate_inode() that takes into account the fact that we may
...@@ -276,6 +276,12 @@ EXPORT_SYMBOL_GPL(nfs_file_fsync); ...@@ -276,6 +276,12 @@ EXPORT_SYMBOL_GPL(nfs_file_fsync);
* then a modify/write/read cycle when writing to a page in the * then a modify/write/read cycle when writing to a page in the
* page cache. * page cache.
* *
* Some pNFS layout drivers can only read/write at a certain block
* granularity like all block devices and therefore we must perform
* read/modify/write whenever a page hasn't read yet and the data
* to be written there is not aligned to a block boundary and/or
* smaller than the block size.
*
* The modify/write/read cycle may occur if a page is read before * The modify/write/read cycle may occur if a page is read before
* being completely filled by the writer. In this situation, the * being completely filled by the writer. In this situation, the
* page must be completely written to stable storage on the server * page must be completely written to stable storage on the server
...@@ -291,26 +297,32 @@ EXPORT_SYMBOL_GPL(nfs_file_fsync); ...@@ -291,26 +297,32 @@ EXPORT_SYMBOL_GPL(nfs_file_fsync);
* and that the new data won't completely replace the old data in * and that the new data won't completely replace the old data in
* that range of the file. * that range of the file.
*/ */
static int nfs_want_read_modify_write(struct file *file, struct page *page, static bool nfs_full_page_write(struct page *page, loff_t pos, unsigned int len)
loff_t pos, unsigned len)
{ {
unsigned int pglen = nfs_page_length(page); unsigned int pglen = nfs_page_length(page);
unsigned int offset = pos & (PAGE_SIZE - 1); unsigned int offset = pos & (PAGE_SIZE - 1);
unsigned int end = offset + len; unsigned int end = offset + len;
if (pnfs_ld_read_whole_page(file->f_mapping->host)) { return !pglen || (end >= pglen && !offset);
if (!PageUptodate(page)) }
return 1;
return 0;
}
if ((file->f_mode & FMODE_READ) && /* open for read? */ static bool nfs_want_read_modify_write(struct file *file, struct page *page,
!PageUptodate(page) && /* Uptodate? */ loff_t pos, unsigned int len)
!PagePrivate(page) && /* i/o request already? */ {
pglen && /* valid bytes of file? */ /*
(end < pglen || offset)) /* replace all valid bytes? */ * Up-to-date pages, those with ongoing or full-page write
return 1; * don't need read/modify/write
return 0; */
if (PageUptodate(page) || PagePrivate(page) ||
nfs_full_page_write(page, pos, len))
return false;
if (pnfs_ld_read_whole_page(file->f_mapping->host))
return true;
/* Open for reading too? */
if (file->f_mode & FMODE_READ)
return true;
return false;
} }
/* /*
......
This diff is collapsed.
...@@ -132,16 +132,6 @@ FF_LAYOUT_LSEG(struct pnfs_layout_segment *lseg) ...@@ -132,16 +132,6 @@ FF_LAYOUT_LSEG(struct pnfs_layout_segment *lseg)
generic_hdr); generic_hdr);
} }
static inline struct nfs4_deviceid_node *
FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx)
{
if (idx >= FF_LAYOUT_LSEG(lseg)->mirror_array_cnt ||
FF_LAYOUT_LSEG(lseg)->mirror_array[idx] == NULL ||
FF_LAYOUT_LSEG(lseg)->mirror_array[idx]->mirror_ds == NULL)
return NULL;
return &FF_LAYOUT_LSEG(lseg)->mirror_array[idx]->mirror_ds->id_node;
}
static inline struct nfs4_ff_layout_ds * static inline struct nfs4_ff_layout_ds *
FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node) FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node)
{ {
...@@ -151,9 +141,25 @@ FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node) ...@@ -151,9 +141,25 @@ FF_LAYOUT_MIRROR_DS(struct nfs4_deviceid_node *node)
static inline struct nfs4_ff_layout_mirror * static inline struct nfs4_ff_layout_mirror *
FF_LAYOUT_COMP(struct pnfs_layout_segment *lseg, u32 idx) FF_LAYOUT_COMP(struct pnfs_layout_segment *lseg, u32 idx)
{ {
if (idx >= FF_LAYOUT_LSEG(lseg)->mirror_array_cnt) struct nfs4_ff_layout_segment *fls = FF_LAYOUT_LSEG(lseg);
return NULL;
return FF_LAYOUT_LSEG(lseg)->mirror_array[idx]; if (idx < fls->mirror_array_cnt)
return fls->mirror_array[idx];
return NULL;
}
static inline struct nfs4_deviceid_node *
FF_LAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg, u32 idx)
{
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, idx);
if (mirror != NULL) {
struct nfs4_ff_layout_ds *mirror_ds = mirror->mirror_ds;
if (!IS_ERR_OR_NULL(mirror_ds))
return &mirror_ds->id_node;
}
return NULL;
} }
static inline u32 static inline u32
...@@ -174,28 +180,10 @@ ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg) ...@@ -174,28 +180,10 @@ ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg)
return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_READ_IO; return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_READ_IO;
} }
static inline bool
ff_layout_test_devid_unavailable(struct nfs4_deviceid_node *node)
{
/*
* Flexfiles should never mark a DS unavailable, but if it does
* print a (ratelimited) warning as this can affect performance.
*/
if (nfs4_test_deviceid_unavailable(node)) {
u32 *p = (u32 *)node->deviceid.data;
pr_warn_ratelimited("NFS: flexfiles layout referencing an "
"unavailable device [%x%x%x%x]\n",
p[0], p[1], p[2], p[3]);
return true;
}
return false;
}
static inline int static inline int
nfs4_ff_layout_ds_version(struct pnfs_layout_segment *lseg, u32 ds_idx) nfs4_ff_layout_ds_version(const struct nfs4_ff_layout_mirror *mirror)
{ {
return FF_LAYOUT_COMP(lseg, ds_idx)->mirror_ds->ds_versions[0].version; return mirror->mirror_ds->ds_versions[0].version;
} }
struct nfs4_ff_layout_ds * struct nfs4_ff_layout_ds *
...@@ -207,6 +195,7 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo, ...@@ -207,6 +195,7 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
struct nfs4_ff_layout_mirror *mirror, u64 offset, struct nfs4_ff_layout_mirror *mirror, u64 offset,
u64 length, int status, enum nfs_opnum4 opnum, u64 length, int status, enum nfs_opnum4 opnum,
gfp_t gfp_flags); gfp_t gfp_flags);
void ff_layout_send_layouterror(struct pnfs_layout_segment *lseg);
int ff_layout_encode_ds_ioerr(struct xdr_stream *xdr, const struct list_head *head); int ff_layout_encode_ds_ioerr(struct xdr_stream *xdr, const struct list_head *head);
void ff_layout_free_ds_ioerr(struct list_head *head); void ff_layout_free_ds_ioerr(struct list_head *head);
unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo, unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
...@@ -214,23 +203,23 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo, ...@@ -214,23 +203,23 @@ unsigned int ff_layout_fetch_ds_ioerr(struct pnfs_layout_hdr *lo,
struct list_head *head, struct list_head *head,
unsigned int maxnum); unsigned int maxnum);
struct nfs_fh * struct nfs_fh *
nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx); nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror);
int void
nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg, nfs4_ff_layout_select_ds_stateid(const struct nfs4_ff_layout_mirror *mirror,
u32 mirror_idx, nfs4_stateid *stateid);
nfs4_stateid *stateid);
struct nfs4_pnfs_ds * struct nfs4_pnfs_ds *
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
bool fail_return); bool fail_return);
struct rpc_clnt * struct rpc_clnt *
nfs4_ff_find_or_create_ds_client(struct pnfs_layout_segment *lseg, nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
u32 ds_idx,
struct nfs_client *ds_clp, struct nfs_client *ds_clp,
struct inode *inode); struct inode *inode);
const struct cred *ff_layout_get_ds_cred(struct pnfs_layout_segment *lseg, const struct cred *ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
u32 ds_idx, const struct cred *mdscred); const struct pnfs_layout_range *range,
const struct cred *mdscred);
bool ff_layout_avoid_mds_available_ds(struct pnfs_layout_segment *lseg); bool ff_layout_avoid_mds_available_ds(struct pnfs_layout_segment *lseg);
bool ff_layout_avoid_read_on_rw(struct pnfs_layout_segment *lseg); bool ff_layout_avoid_read_on_rw(struct pnfs_layout_segment *lseg);
......
...@@ -183,56 +183,6 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev, ...@@ -183,56 +183,6 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
return NULL; return NULL;
} }
static void ff_layout_mark_devid_invalid(struct pnfs_layout_segment *lseg,
struct nfs4_deviceid_node *devid)
{
nfs4_delete_deviceid(devid->ld, devid->nfs_client, &devid->deviceid);
if (!ff_layout_has_available_ds(lseg))
pnfs_error_mark_layout_for_return(lseg->pls_layout->plh_inode,
lseg);
}
static bool ff_layout_mirror_valid(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
bool create)
{
if (mirror == NULL || IS_ERR(mirror->mirror_ds))
goto outerr;
if (mirror->mirror_ds == NULL) {
if (create) {
struct nfs4_deviceid_node *node;
struct pnfs_layout_hdr *lh = lseg->pls_layout;
struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
node = nfs4_find_get_deviceid(NFS_SERVER(lh->plh_inode),
&mirror->devid, lh->plh_lc_cred,
GFP_KERNEL);
if (node)
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
/* check for race with another call to this function */
if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) &&
mirror_ds != ERR_PTR(-ENODEV))
nfs4_put_deviceid_node(node);
} else
goto outerr;
}
if (IS_ERR(mirror->mirror_ds))
goto outerr;
if (mirror->mirror_ds->ds == NULL) {
struct nfs4_deviceid_node *devid;
devid = &mirror->mirror_ds->id_node;
ff_layout_mark_devid_invalid(lseg, devid);
return false;
}
return true;
outerr:
pnfs_error_mark_layout_for_return(lseg->pls_layout->plh_inode, lseg);
return false;
}
static void extend_ds_error(struct nfs4_ff_layout_ds_err *err, static void extend_ds_error(struct nfs4_ff_layout_ds_err *err,
u64 offset, u64 length) u64 offset, u64 length)
{ {
...@@ -326,7 +276,6 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo, ...@@ -326,7 +276,6 @@ int ff_layout_track_ds_error(struct nfs4_flexfile_layout *flo,
spin_lock(&flo->generic_hdr.plh_inode->i_lock); spin_lock(&flo->generic_hdr.plh_inode->i_lock);
ff_layout_add_ds_error_locked(flo, dserr); ff_layout_add_ds_error_locked(flo, dserr);
spin_unlock(&flo->generic_hdr.plh_inode->i_lock); spin_unlock(&flo->generic_hdr.plh_inode->i_lock);
return 0; return 0;
} }
...@@ -353,46 +302,54 @@ ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode) ...@@ -353,46 +302,54 @@ ff_layout_get_mirror_cred(struct nfs4_ff_layout_mirror *mirror, u32 iomode)
} }
struct nfs_fh * struct nfs_fh *
nfs4_ff_layout_select_ds_fh(struct pnfs_layout_segment *lseg, u32 mirror_idx) nfs4_ff_layout_select_ds_fh(struct nfs4_ff_layout_mirror *mirror)
{ {
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, mirror_idx);
struct nfs_fh *fh = NULL;
if (!ff_layout_mirror_valid(lseg, mirror, false)) {
pr_err_ratelimited("NFS: %s: No data server for mirror offset index %d\n",
__func__, mirror_idx);
goto out;
}
/* FIXME: For now assume there is only 1 version available for the DS */ /* FIXME: For now assume there is only 1 version available for the DS */
fh = &mirror->fh_versions[0]; return &mirror->fh_versions[0];
out:
return fh;
} }
int void
nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg, nfs4_ff_layout_select_ds_stateid(const struct nfs4_ff_layout_mirror *mirror,
u32 mirror_idx, nfs4_stateid *stateid)
nfs4_stateid *stateid)
{ {
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, mirror_idx); if (nfs4_ff_layout_ds_version(mirror) == 4)
nfs4_stateid_copy(stateid, &mirror->stateid);
}
if (!ff_layout_mirror_valid(lseg, mirror, false)) { static bool
pr_err_ratelimited("NFS: %s: No data server for mirror offset index %d\n", ff_layout_init_mirror_ds(struct pnfs_layout_hdr *lo,
__func__, mirror_idx); struct nfs4_ff_layout_mirror *mirror)
goto out; {
if (mirror == NULL)
goto outerr;
if (mirror->mirror_ds == NULL) {
struct nfs4_deviceid_node *node;
struct nfs4_ff_layout_ds *mirror_ds = ERR_PTR(-ENODEV);
node = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode),
&mirror->devid, lo->plh_lc_cred,
GFP_KERNEL);
if (node)
mirror_ds = FF_LAYOUT_MIRROR_DS(node);
/* check for race with another call to this function */
if (cmpxchg(&mirror->mirror_ds, NULL, mirror_ds) &&
mirror_ds != ERR_PTR(-ENODEV))
nfs4_put_deviceid_node(node);
} }
nfs4_stateid_copy(stateid, &mirror->stateid); if (IS_ERR(mirror->mirror_ds))
return 1; goto outerr;
out:
return 0; return true;
outerr:
return false;
} }
/** /**
* nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call * nfs4_ff_layout_prepare_ds - prepare a DS connection for an RPC call
* @lseg: the layout segment we're operating on * @lseg: the layout segment we're operating on
* @ds_idx: index of the DS to use * @mirror: layout mirror describing the DS to use
* @fail_return: return layout on connect failure? * @fail_return: return layout on connect failure?
* *
* Try to prepare a DS connection to accept an RPC call. This involves * Try to prepare a DS connection to accept an RPC call. This involves
...@@ -407,26 +364,18 @@ nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg, ...@@ -407,26 +364,18 @@ nfs4_ff_layout_select_ds_stateid(struct pnfs_layout_segment *lseg,
* Returns a pointer to a connected DS object on success or NULL on failure. * Returns a pointer to a connected DS object on success or NULL on failure.
*/ */
struct nfs4_pnfs_ds * struct nfs4_pnfs_ds *
nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg,
struct nfs4_ff_layout_mirror *mirror,
bool fail_return) bool fail_return)
{ {
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
struct nfs4_pnfs_ds *ds = NULL; struct nfs4_pnfs_ds *ds = NULL;
struct nfs4_deviceid_node *devid;
struct inode *ino = lseg->pls_layout->plh_inode; struct inode *ino = lseg->pls_layout->plh_inode;
struct nfs_server *s = NFS_SERVER(ino); struct nfs_server *s = NFS_SERVER(ino);
unsigned int max_payload; unsigned int max_payload;
int status; int status;
if (!ff_layout_mirror_valid(lseg, mirror, true)) { if (!ff_layout_init_mirror_ds(lseg->pls_layout, mirror))
pr_err_ratelimited("NFS: %s: No data server for offset index %d\n", goto noconnect;
__func__, ds_idx);
goto out;
}
devid = &mirror->mirror_ds->id_node;
if (ff_layout_test_devid_unavailable(devid))
goto out_fail;
ds = mirror->mirror_ds->ds; ds = mirror->mirror_ds->ds;
/* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */ /* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
...@@ -437,8 +386,8 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, ...@@ -437,8 +386,8 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
/* FIXME: For now we assume the server sent only one version of NFS /* FIXME: For now we assume the server sent only one version of NFS
* to use for the DS. * to use for the DS.
*/ */
status = nfs4_pnfs_ds_connect(s, ds, devid, dataserver_timeo, status = nfs4_pnfs_ds_connect(s, ds, &mirror->mirror_ds->id_node,
dataserver_retrans, dataserver_timeo, dataserver_retrans,
mirror->mirror_ds->ds_versions[0].version, mirror->mirror_ds->ds_versions[0].version,
mirror->mirror_ds->ds_versions[0].minor_version); mirror->mirror_ds->ds_versions[0].minor_version);
...@@ -453,11 +402,12 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, ...@@ -453,11 +402,12 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
mirror->mirror_ds->ds_versions[0].wsize = max_payload; mirror->mirror_ds->ds_versions[0].wsize = max_payload;
goto out; goto out;
} }
out_fail: noconnect:
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout), ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
mirror, lseg->pls_range.offset, mirror, lseg->pls_range.offset,
lseg->pls_range.length, NFS4ERR_NXIO, lseg->pls_range.length, NFS4ERR_NXIO,
OP_ILLEGAL, GFP_NOIO); OP_ILLEGAL, GFP_NOIO);
ff_layout_send_layouterror(lseg);
if (fail_return || !ff_layout_has_available_ds(lseg)) if (fail_return || !ff_layout_has_available_ds(lseg))
pnfs_error_mark_layout_for_return(ino, lseg); pnfs_error_mark_layout_for_return(ino, lseg);
ds = NULL; ds = NULL;
...@@ -466,14 +416,14 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx, ...@@ -466,14 +416,14 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
} }
const struct cred * const struct cred *
ff_layout_get_ds_cred(struct pnfs_layout_segment *lseg, u32 ds_idx, ff_layout_get_ds_cred(struct nfs4_ff_layout_mirror *mirror,
const struct pnfs_layout_range *range,
const struct cred *mdscred) const struct cred *mdscred)
{ {
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
const struct cred *cred; const struct cred *cred;
if (mirror && !mirror->mirror_ds->ds_versions[0].tightly_coupled) { if (mirror && !mirror->mirror_ds->ds_versions[0].tightly_coupled) {
cred = ff_layout_get_mirror_cred(mirror, lseg->pls_range.iomode); cred = ff_layout_get_mirror_cred(mirror, range->iomode);
if (!cred) if (!cred)
cred = get_cred(mdscred); cred = get_cred(mdscred);
} else { } else {
...@@ -483,15 +433,18 @@ ff_layout_get_ds_cred(struct pnfs_layout_segment *lseg, u32 ds_idx, ...@@ -483,15 +433,18 @@ ff_layout_get_ds_cred(struct pnfs_layout_segment *lseg, u32 ds_idx,
} }
/** /**
* Find or create a DS rpc client with th MDS server rpc client auth flavor * nfs4_ff_find_or_create_ds_client - Find or create a DS rpc client
* in the nfs_client cl_ds_clients list. * @mirror: pointer to the mirror
*/ * @ds_clp: nfs_client for the DS
* @inode: pointer to inode
*
* Find or create a DS rpc client with th MDS server rpc client auth flavor
* in the nfs_client cl_ds_clients list.
*/
struct rpc_clnt * struct rpc_clnt *
nfs4_ff_find_or_create_ds_client(struct pnfs_layout_segment *lseg, u32 ds_idx, nfs4_ff_find_or_create_ds_client(struct nfs4_ff_layout_mirror *mirror,
struct nfs_client *ds_clp, struct inode *inode) struct nfs_client *ds_clp, struct inode *inode)
{ {
struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
switch (mirror->mirror_ds->ds_versions[0].version) { switch (mirror->mirror_ds->ds_versions[0].version) {
case 3: case 3:
/* For NFSv3 DS, flavor is set when creating DS connections */ /* For NFSv3 DS, flavor is set when creating DS connections */
...@@ -608,7 +561,7 @@ static bool ff_read_layout_has_available_ds(struct pnfs_layout_segment *lseg) ...@@ -608,7 +561,7 @@ static bool ff_read_layout_has_available_ds(struct pnfs_layout_segment *lseg)
if (IS_ERR(mirror->mirror_ds)) if (IS_ERR(mirror->mirror_ds))
continue; continue;
devid = &mirror->mirror_ds->id_node; devid = &mirror->mirror_ds->id_node;
if (!ff_layout_test_devid_unavailable(devid)) if (!nfs4_test_deviceid_unavailable(devid))
return true; return true;
} }
} }
...@@ -629,7 +582,7 @@ static bool ff_rw_layout_has_available_ds(struct pnfs_layout_segment *lseg) ...@@ -629,7 +582,7 @@ static bool ff_rw_layout_has_available_ds(struct pnfs_layout_segment *lseg)
if (!mirror->mirror_ds) if (!mirror->mirror_ds)
continue; continue;
devid = &mirror->mirror_ds->id_node; devid = &mirror->mirror_ds->id_node;
if (ff_layout_test_devid_unavailable(devid)) if (nfs4_test_deviceid_unavailable(devid))
return false; return false;
} }
......
...@@ -143,6 +143,7 @@ EXPORT_SYMBOL_GPL(nfs_sync_inode); ...@@ -143,6 +143,7 @@ EXPORT_SYMBOL_GPL(nfs_sync_inode);
/** /**
* nfs_sync_mapping - helper to flush all mmapped dirty data to disk * nfs_sync_mapping - helper to flush all mmapped dirty data to disk
* @mapping: pointer to struct address_space
*/ */
int nfs_sync_mapping(struct address_space *mapping) int nfs_sync_mapping(struct address_space *mapping)
{ {
...@@ -1184,8 +1185,8 @@ int nfs_attribute_cache_expired(struct inode *inode) ...@@ -1184,8 +1185,8 @@ int nfs_attribute_cache_expired(struct inode *inode)
/** /**
* nfs_revalidate_inode - Revalidate the inode attributes * nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct * @server: pointer to nfs_server struct
* @inode - pointer to inode struct * @inode: pointer to inode struct
* *
* Updates inode attribute information by retrieving the data from the server. * Updates inode attribute information by retrieving the data from the server.
*/ */
...@@ -1255,8 +1256,8 @@ int nfs_revalidate_mapping_rcu(struct inode *inode) ...@@ -1255,8 +1256,8 @@ int nfs_revalidate_mapping_rcu(struct inode *inode)
/** /**
* nfs_revalidate_mapping - Revalidate the pagecache * nfs_revalidate_mapping - Revalidate the pagecache
* @inode - pointer to host inode * @inode: pointer to host inode
* @mapping - pointer to mapping * @mapping: pointer to mapping
*/ */
int nfs_revalidate_mapping(struct inode *inode, int nfs_revalidate_mapping(struct inode *inode,
struct address_space *mapping) struct address_space *mapping)
...@@ -1371,8 +1372,8 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1371,8 +1372,8 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/** /**
* nfs_check_inode_attributes - verify consistency of the inode attribute cache * nfs_check_inode_attributes - verify consistency of the inode attribute cache
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - updated attributes * @fattr: updated attributes
* *
* Verifies the attribute cache. If we have just changed the attributes, * Verifies the attribute cache. If we have just changed the attributes,
* so that fattr carries weak cache consistency data, then it may * so that fattr carries weak cache consistency data, then it may
...@@ -1572,8 +1573,8 @@ EXPORT_SYMBOL_GPL(_nfs_display_fhandle); ...@@ -1572,8 +1573,8 @@ EXPORT_SYMBOL_GPL(_nfs_display_fhandle);
/** /**
* nfs_inode_attrs_need_update - check if the inode attributes need updating * nfs_inode_attrs_need_update - check if the inode attributes need updating
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - attributes * @fattr: attributes
* *
* Attempt to divine whether or not an RPC call reply carrying stale * Attempt to divine whether or not an RPC call reply carrying stale
* attributes got scheduled after another call carrying updated ones. * attributes got scheduled after another call carrying updated ones.
...@@ -1614,8 +1615,8 @@ static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr ...@@ -1614,8 +1615,8 @@ static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr
/** /**
* nfs_refresh_inode - try to update the inode attribute cache * nfs_refresh_inode - try to update the inode attribute cache
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - updated attributes * @fattr: updated attributes
* *
* Check that an RPC call that returned attributes has not overlapped with * Check that an RPC call that returned attributes has not overlapped with
* other recent updates of the inode metadata, then decide whether it is * other recent updates of the inode metadata, then decide whether it is
...@@ -1649,8 +1650,8 @@ static int nfs_post_op_update_inode_locked(struct inode *inode, ...@@ -1649,8 +1650,8 @@ static int nfs_post_op_update_inode_locked(struct inode *inode,
/** /**
* nfs_post_op_update_inode - try to update the inode attribute cache * nfs_post_op_update_inode - try to update the inode attribute cache
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - updated attributes * @fattr: updated attributes
* *
* After an operation that has changed the inode metadata, mark the * After an operation that has changed the inode metadata, mark the
* attribute cache as being invalid, then try to update it. * attribute cache as being invalid, then try to update it.
...@@ -1679,8 +1680,8 @@ EXPORT_SYMBOL_GPL(nfs_post_op_update_inode); ...@@ -1679,8 +1680,8 @@ EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
/** /**
* nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache * nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - updated attributes * @fattr: updated attributes
* *
* After an operation that has changed the inode metadata, mark the * After an operation that has changed the inode metadata, mark the
* attribute cache as being invalid, then try to update it. Fake up * attribute cache as being invalid, then try to update it. Fake up
...@@ -1731,8 +1732,8 @@ int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fa ...@@ -1731,8 +1732,8 @@ int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fa
/** /**
* nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
* @inode - pointer to inode * @inode: pointer to inode
* @fattr - updated attributes * @fattr: updated attributes
* *
* After an operation that has changed the inode metadata, mark the * After an operation that has changed the inode metadata, mark the
* attribute cache as being invalid, then try to update it. Fake up * attribute cache as being invalid, then try to update it. Fake up
......
...@@ -69,7 +69,8 @@ struct nfs_clone_mount { ...@@ -69,7 +69,8 @@ struct nfs_clone_mount {
* Maximum number of pages that readdir can use for creating * Maximum number of pages that readdir can use for creating
* a vmapped array of pages. * a vmapped array of pages.
*/ */
#define NFS_MAX_READDIR_PAGES 8 #define NFS_MAX_READDIR_PAGES 64
#define NFS_MAX_READDIR_RAPAGES 8
struct nfs_client_initdata { struct nfs_client_initdata {
unsigned long init_flags; unsigned long init_flags;
...@@ -755,6 +756,7 @@ static inline bool nfs_error_is_fatal(int err) ...@@ -755,6 +756,7 @@ static inline bool nfs_error_is_fatal(int err)
{ {
switch (err) { switch (err) {
case -ERESTARTSYS: case -ERESTARTSYS:
case -EINTR:
case -EACCES: case -EACCES:
case -EDQUOT: case -EDQUOT:
case -EFBIG: case -EFBIG:
...@@ -763,6 +765,7 @@ static inline bool nfs_error_is_fatal(int err) ...@@ -763,6 +765,7 @@ static inline bool nfs_error_is_fatal(int err)
case -EROFS: case -EROFS:
case -ESTALE: case -ESTALE:
case -E2BIG: case -E2BIG:
case -ENOMEM:
return true; return true;
default: default:
return false; return false;
......
...@@ -25,7 +25,7 @@ static void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode) ...@@ -25,7 +25,7 @@ static void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode)
/** /**
* nfs_start_io_read - declare the file is being used for buffered reads * nfs_start_io_read - declare the file is being used for buffered reads
* @inode - file inode * @inode: file inode
* *
* Declare that a buffered read operation is about to start, and ensure * Declare that a buffered read operation is about to start, and ensure
* that we block all direct I/O. * that we block all direct I/O.
...@@ -56,7 +56,7 @@ nfs_start_io_read(struct inode *inode) ...@@ -56,7 +56,7 @@ nfs_start_io_read(struct inode *inode)
/** /**
* nfs_end_io_read - declare that the buffered read operation is done * nfs_end_io_read - declare that the buffered read operation is done
* @inode - file inode * @inode: file inode
* *
* Declare that a buffered read operation is done, and release the shared * Declare that a buffered read operation is done, and release the shared
* lock on inode->i_rwsem. * lock on inode->i_rwsem.
...@@ -69,7 +69,7 @@ nfs_end_io_read(struct inode *inode) ...@@ -69,7 +69,7 @@ nfs_end_io_read(struct inode *inode)
/** /**
* nfs_start_io_write - declare the file is being used for buffered writes * nfs_start_io_write - declare the file is being used for buffered writes
* @inode - file inode * @inode: file inode
* *
* Declare that a buffered read operation is about to start, and ensure * Declare that a buffered read operation is about to start, and ensure
* that we block all direct I/O. * that we block all direct I/O.
...@@ -83,7 +83,7 @@ nfs_start_io_write(struct inode *inode) ...@@ -83,7 +83,7 @@ nfs_start_io_write(struct inode *inode)
/** /**
* nfs_end_io_write - declare that the buffered write operation is done * nfs_end_io_write - declare that the buffered write operation is done
* @inode - file inode * @inode: file inode
* *
* Declare that a buffered write operation is done, and release the * Declare that a buffered write operation is done, and release the
* lock on inode->i_rwsem. * lock on inode->i_rwsem.
...@@ -105,7 +105,7 @@ static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode) ...@@ -105,7 +105,7 @@ static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode)
/** /**
* nfs_end_io_direct - declare the file is being used for direct i/o * nfs_end_io_direct - declare the file is being used for direct i/o
* @inode - file inode * @inode: file inode
* *
* Declare that a direct I/O operation is about to start, and ensure * Declare that a direct I/O operation is about to start, and ensure
* that we block all buffered I/O. * that we block all buffered I/O.
...@@ -136,7 +136,7 @@ nfs_start_io_direct(struct inode *inode) ...@@ -136,7 +136,7 @@ nfs_start_io_direct(struct inode *inode)
/** /**
* nfs_end_io_direct - declare that the direct i/o operation is done * nfs_end_io_direct - declare that the direct i/o operation is done
* @inode - file inode * @inode: file inode
* *
* Declare that a direct I/O operation is done, and release the shared * Declare that a direct I/O operation is done, and release the shared
* lock on inode->i_rwsem. * lock on inode->i_rwsem.
......
...@@ -221,10 +221,10 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, ...@@ -221,10 +221,10 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
/** /**
* nfs_do_submount - set up mountpoint when crossing a filesystem boundary * nfs_do_submount - set up mountpoint when crossing a filesystem boundary
* @dentry - parent directory * @dentry: parent directory
* @fh - filehandle for new root dentry * @fh: filehandle for new root dentry
* @fattr - attributes for new root inode * @fattr: attributes for new root inode
* @authflavor - security flavor to use when performing the mount * @authflavor: security flavor to use when performing the mount
* *
*/ */
struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh, struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfs2.h> #include <linux/nfs2.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include "nfstrace.h"
#include "internal.h" #include "internal.h"
#define NFSDBG_FACILITY NFSDBG_XDR #define NFSDBG_FACILITY NFSDBG_XDR
...@@ -55,41 +56,15 @@ ...@@ -55,41 +56,15 @@
#define NFS_attrstat_sz (1+NFS_fattr_sz) #define NFS_attrstat_sz (1+NFS_fattr_sz)
#define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) #define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
#define NFS_readlinkres_sz (2) #define NFS_readlinkres_sz (2+1)
#define NFS_readres_sz (1+NFS_fattr_sz+1) #define NFS_readres_sz (1+NFS_fattr_sz+1+1)
#define NFS_writeres_sz (NFS_attrstat_sz) #define NFS_writeres_sz (NFS_attrstat_sz)
#define NFS_stat_sz (1) #define NFS_stat_sz (1)
#define NFS_readdirres_sz (1) #define NFS_readdirres_sz (1+1)
#define NFS_statfsres_sz (1+NFS_info_sz) #define NFS_statfsres_sz (1+NFS_info_sz)
static int nfs_stat_to_errno(enum nfs_stat); static int nfs_stat_to_errno(enum nfs_stat);
/*
* While encoding arguments, set up the reply buffer in advance to
* receive reply data directly into the page cache.
*/
static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
unsigned int base, unsigned int len,
unsigned int bufsize)
{
struct rpc_auth *auth = req->rq_cred->cr_auth;
unsigned int replen;
replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
}
/*
* Handle decode buffer overflows out-of-line.
*/
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("NFS: %s prematurely hit the end of our receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/* /*
* Encode/decode NFSv2 basic data types * Encode/decode NFSv2 basic data types
* *
...@@ -110,8 +85,8 @@ static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result) ...@@ -110,8 +85,8 @@ static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
count = be32_to_cpup(p); count = be32_to_cpup(p);
recvd = xdr_read_pages(xdr, count); recvd = xdr_read_pages(xdr, count);
if (unlikely(count > recvd)) if (unlikely(count > recvd))
...@@ -125,9 +100,6 @@ static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result) ...@@ -125,9 +100,6 @@ static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
"count %u > recvd %u\n", count, recvd); "count %u > recvd %u\n", count, recvd);
count = recvd; count = recvd;
goto out; goto out;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -157,13 +129,16 @@ static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) ...@@ -157,13 +129,16 @@ static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
if (unlikely(*p != cpu_to_be32(NFS_OK)))
goto out_status;
*status = 0;
return 0;
out_status:
*status = be32_to_cpup(p); *status = be32_to_cpup(p);
trace_nfs_xdr_status((int)*status);
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -205,14 +180,11 @@ static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) ...@@ -205,14 +180,11 @@ static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, NFS2_FHSIZE); p = xdr_inline_decode(xdr, NFS2_FHSIZE);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
fh->size = NFS2_FHSIZE; fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE); memcpy(fh->data, p, NFS2_FHSIZE);
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -282,8 +254,8 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) ...@@ -282,8 +254,8 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
fattr->valid |= NFS_ATTR_FATTR_V2; fattr->valid |= NFS_ATTR_FATTR_V2;
...@@ -325,9 +297,6 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) ...@@ -325,9 +297,6 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
out_gid: out_gid:
dprintk("NFS: returned invalid gid\n"); dprintk("NFS: returned invalid gid\n");
return -EINVAL; return -EINVAL;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -416,23 +385,20 @@ static int decode_filename_inline(struct xdr_stream *xdr, ...@@ -416,23 +385,20 @@ static int decode_filename_inline(struct xdr_stream *xdr,
u32 count; u32 count;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
count = be32_to_cpup(p); count = be32_to_cpup(p);
if (count > NFS3_MAXNAMLEN) if (count > NFS3_MAXNAMLEN)
goto out_nametoolong; goto out_nametoolong;
p = xdr_inline_decode(xdr, count); p = xdr_inline_decode(xdr, count);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
*name = (const char *)p; *name = (const char *)p;
*length = count; *length = count;
return 0; return 0;
out_nametoolong: out_nametoolong:
dprintk("NFS: returned filename too long: %u\n", count); dprintk("NFS: returned filename too long: %u\n", count);
return -ENAMETOOLONG; return -ENAMETOOLONG;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -455,8 +421,8 @@ static int decode_path(struct xdr_stream *xdr) ...@@ -455,8 +421,8 @@ static int decode_path(struct xdr_stream *xdr)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
length = be32_to_cpup(p); length = be32_to_cpup(p);
if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
goto out_size; goto out_size;
...@@ -472,9 +438,6 @@ static int decode_path(struct xdr_stream *xdr) ...@@ -472,9 +438,6 @@ static int decode_path(struct xdr_stream *xdr)
dprintk("NFS: server cheating in pathname result: " dprintk("NFS: server cheating in pathname result: "
"length %u > received %u\n", length, recvd); "length %u > received %u\n", length, recvd);
return -EIO; return -EIO;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
/* /*
...@@ -615,8 +578,8 @@ static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, ...@@ -615,8 +578,8 @@ static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
const struct nfs_readlinkargs *args = data; const struct nfs_readlinkargs *args = data;
encode_fhandle(xdr, args->fh); encode_fhandle(xdr, args->fh);
prepare_reply_buffer(req, args->pages, args->pgbase, rpc_prepare_reply_pages(req, args->pages, args->pgbase,
args->pglen, NFS_readlinkres_sz); args->pglen, NFS_readlinkres_sz);
} }
/* /*
...@@ -651,8 +614,8 @@ static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, ...@@ -651,8 +614,8 @@ static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
const struct nfs_pgio_args *args = data; const struct nfs_pgio_args *args = data;
encode_readargs(xdr, args); encode_readargs(xdr, args);
prepare_reply_buffer(req, args->pages, args->pgbase, rpc_prepare_reply_pages(req, args->pages, args->pgbase,
args->count, NFS_readres_sz); args->count, NFS_readres_sz);
req->rq_rcv_buf.flags |= XDRBUF_READ; req->rq_rcv_buf.flags |= XDRBUF_READ;
} }
...@@ -809,8 +772,8 @@ static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, ...@@ -809,8 +772,8 @@ static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
const struct nfs_readdirargs *args = data; const struct nfs_readdirargs *args = data;
encode_readdirargs(xdr, args); encode_readdirargs(xdr, args);
prepare_reply_buffer(req, args->pages, 0, rpc_prepare_reply_pages(req, args->pages, 0,
args->count, NFS_readdirres_sz); args->count, NFS_readdirres_sz);
} }
/* /*
...@@ -951,12 +914,12 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, ...@@ -951,12 +914,12 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
int error; int error;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EAGAIN;
if (*p++ == xdr_zero) { if (*p++ == xdr_zero) {
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EAGAIN;
if (*p++ == xdr_zero) if (*p++ == xdr_zero)
return -EAGAIN; return -EAGAIN;
entry->eof = 1; entry->eof = 1;
...@@ -964,8 +927,8 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, ...@@ -964,8 +927,8 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
} }
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EAGAIN;
entry->ino = be32_to_cpup(p); entry->ino = be32_to_cpup(p);
error = decode_filename_inline(xdr, &entry->name, &entry->len); error = decode_filename_inline(xdr, &entry->name, &entry->len);
...@@ -978,17 +941,13 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, ...@@ -978,17 +941,13 @@ int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
*/ */
entry->prev_cookie = entry->cookie; entry->prev_cookie = entry->cookie;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EAGAIN;
entry->cookie = be32_to_cpup(p); entry->cookie = be32_to_cpup(p);
entry->d_type = DT_UNKNOWN; entry->d_type = DT_UNKNOWN;
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EAGAIN;
} }
/* /*
...@@ -1052,17 +1011,14 @@ static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) ...@@ -1052,17 +1011,14 @@ static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, NFS_info_sz << 2); p = xdr_inline_decode(xdr, NFS_info_sz << 2);
if (unlikely(p == NULL)) if (unlikely(!p))
goto out_overflow; return -EIO;
result->tsize = be32_to_cpup(p++); result->tsize = be32_to_cpup(p++);
result->bsize = be32_to_cpup(p++); result->bsize = be32_to_cpup(p++);
result->blocks = be32_to_cpup(p++); result->blocks = be32_to_cpup(p++);
result->bfree = be32_to_cpup(p++); result->bfree = be32_to_cpup(p++);
result->bavail = be32_to_cpup(p); result->bavail = be32_to_cpup(p);
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
......
...@@ -222,8 +222,6 @@ static int __nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, ...@@ -222,8 +222,6 @@ static int __nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
switch (status) { switch (status) {
case 0: case 0:
status = nfs_refresh_inode(inode, fattr); status = nfs_refresh_inode(inode, fattr);
set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
set_cached_acl(inode, ACL_TYPE_DEFAULT, dfacl);
break; break;
case -EPFNOSUPPORT: case -EPFNOSUPPORT:
case -EPROTONOSUPPORT: case -EPROTONOSUPPORT:
......
This diff is collapsed.
...@@ -20,5 +20,8 @@ loff_t nfs42_proc_llseek(struct file *, loff_t, int); ...@@ -20,5 +20,8 @@ loff_t nfs42_proc_llseek(struct file *, loff_t, int);
int nfs42_proc_layoutstats_generic(struct nfs_server *, int nfs42_proc_layoutstats_generic(struct nfs_server *,
struct nfs42_layoutstat_data *); struct nfs42_layoutstat_data *);
int nfs42_proc_clone(struct file *, struct file *, loff_t, loff_t, loff_t); int nfs42_proc_clone(struct file *, struct file *, loff_t, loff_t, loff_t);
int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
const struct nfs42_layout_error *errors,
size_t n);
#endif /* __LINUX_FS_NFS_NFS4_2_H */ #endif /* __LINUX_FS_NFS_NFS4_2_H */
...@@ -672,6 +672,170 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server, ...@@ -672,6 +672,170 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
return 0; return 0;
} }
static struct nfs42_layouterror_data *
nfs42_alloc_layouterror_data(struct pnfs_layout_segment *lseg, gfp_t gfp_flags)
{
struct nfs42_layouterror_data *data;
struct inode *inode = lseg->pls_layout->plh_inode;
data = kzalloc(sizeof(*data), gfp_flags);
if (data) {
data->args.inode = data->inode = nfs_igrab_and_active(inode);
if (data->inode) {
data->lseg = pnfs_get_lseg(lseg);
if (data->lseg)
return data;
nfs_iput_and_deactive(data->inode);
}
kfree(data);
}
return NULL;
}
static void
nfs42_free_layouterror_data(struct nfs42_layouterror_data *data)
{
pnfs_put_lseg(data->lseg);
nfs_iput_and_deactive(data->inode);
kfree(data);
}
static void
nfs42_layouterror_prepare(struct rpc_task *task, void *calldata)
{
struct nfs42_layouterror_data *data = calldata;
struct inode *inode = data->inode;
struct nfs_server *server = NFS_SERVER(inode);
struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
unsigned i;
spin_lock(&inode->i_lock);
if (!pnfs_layout_is_valid(lo)) {
spin_unlock(&inode->i_lock);
rpc_exit(task, 0);
return;
}
for (i = 0; i < data->args.num_errors; i++)
nfs4_stateid_copy(&data->args.errors[i].stateid,
&lo->plh_stateid);
spin_unlock(&inode->i_lock);
nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
&data->res.seq_res, task);
}
static void
nfs42_layouterror_done(struct rpc_task *task, void *calldata)
{
struct nfs42_layouterror_data *data = calldata;
struct inode *inode = data->inode;
struct pnfs_layout_hdr *lo = data->lseg->pls_layout;
if (!nfs4_sequence_done(task, &data->res.seq_res))
return;
switch (task->tk_status) {
case 0:
break;
case -NFS4ERR_BADHANDLE:
case -ESTALE:
pnfs_destroy_layout(NFS_I(inode));
break;
case -NFS4ERR_EXPIRED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
spin_lock(&inode->i_lock);
if (pnfs_layout_is_valid(lo) &&
nfs4_stateid_match(&data->args.errors[0].stateid,
&lo->plh_stateid)) {
LIST_HEAD(head);
/*
* Mark the bad layout state as invalid, then retry
* with the current stateid.
*/
pnfs_mark_layout_stateid_invalid(lo, &head);
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&head);
nfs_commit_inode(inode, 0);
} else
spin_unlock(&inode->i_lock);
break;
case -NFS4ERR_OLD_STATEID:
spin_lock(&inode->i_lock);
if (pnfs_layout_is_valid(lo) &&
nfs4_stateid_match_other(&data->args.errors[0].stateid,
&lo->plh_stateid)) {
/* Do we need to delay before resending? */
if (!nfs4_stateid_is_newer(&lo->plh_stateid,
&data->args.errors[0].stateid))
rpc_delay(task, HZ);
rpc_restart_call_prepare(task);
}
spin_unlock(&inode->i_lock);
break;
case -ENOTSUPP:
case -EOPNOTSUPP:
NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTERROR;
}
}
static void
nfs42_layouterror_release(void *calldata)
{
struct nfs42_layouterror_data *data = calldata;
nfs42_free_layouterror_data(data);
}
static const struct rpc_call_ops nfs42_layouterror_ops = {
.rpc_call_prepare = nfs42_layouterror_prepare,
.rpc_call_done = nfs42_layouterror_done,
.rpc_release = nfs42_layouterror_release,
};
int nfs42_proc_layouterror(struct pnfs_layout_segment *lseg,
const struct nfs42_layout_error *errors, size_t n)
{
struct inode *inode = lseg->pls_layout->plh_inode;
struct nfs42_layouterror_data *data;
struct rpc_task *task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTERROR],
};
struct rpc_task_setup task_setup = {
.rpc_message = &msg,
.callback_ops = &nfs42_layouterror_ops,
.flags = RPC_TASK_ASYNC,
};
unsigned int i;
if (!nfs_server_capable(inode, NFS_CAP_LAYOUTERROR))
return -EOPNOTSUPP;
if (n > NFS42_LAYOUTERROR_MAX)
return -EINVAL;
data = nfs42_alloc_layouterror_data(lseg, GFP_NOFS);
if (!data)
return -ENOMEM;
for (i = 0; i < n; i++) {
data->args.errors[i] = errors[i];
data->args.num_errors++;
data->res.num_errors++;
}
msg.rpc_argp = &data->args;
msg.rpc_resp = &data->res;
task_setup.callback_data = data;
task_setup.rpc_client = NFS_SERVER(inode)->client;
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
task = rpc_run_task(&task_setup);
if (IS_ERR(task))
return PTR_ERR(task);
rpc_put_task(task);
return 0;
}
EXPORT_SYMBOL_GPL(nfs42_proc_layouterror);
static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
struct file *dst_f, struct nfs_lock_context *src_lock, struct file *dst_f, struct nfs_lock_context *src_lock,
struct nfs_lock_context *dst_lock, loff_t src_offset, struct nfs_lock_context *dst_lock, loff_t src_offset,
......
...@@ -51,6 +51,15 @@ ...@@ -51,6 +51,15 @@
1 /* opaque devaddr4 length */ + \ 1 /* opaque devaddr4 length */ + \
XDR_QUADLEN(PNFS_LAYOUTSTATS_MAXSIZE)) XDR_QUADLEN(PNFS_LAYOUTSTATS_MAXSIZE))
#define decode_layoutstats_maxsz (op_decode_hdr_maxsz) #define decode_layoutstats_maxsz (op_decode_hdr_maxsz)
#define encode_device_error_maxsz (XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
1 /* status */ + 1 /* opnum */)
#define encode_layouterror_maxsz (op_decode_hdr_maxsz + \
2 /* offset */ + \
2 /* length */ + \
encode_stateid_maxsz + \
1 /* Array size */ + \
encode_device_error_maxsz)
#define decode_layouterror_maxsz (op_decode_hdr_maxsz)
#define encode_clone_maxsz (encode_stateid_maxsz + \ #define encode_clone_maxsz (encode_stateid_maxsz + \
encode_stateid_maxsz + \ encode_stateid_maxsz + \
2 /* src offset */ + \ 2 /* src offset */ + \
...@@ -59,43 +68,53 @@ ...@@ -59,43 +68,53 @@
#define decode_clone_maxsz (op_decode_hdr_maxsz) #define decode_clone_maxsz (op_decode_hdr_maxsz)
#define NFS4_enc_allocate_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_allocate_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_allocate_maxsz + \ encode_allocate_maxsz + \
encode_getattr_maxsz) encode_getattr_maxsz)
#define NFS4_dec_allocate_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_allocate_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_allocate_maxsz + \ decode_allocate_maxsz + \
decode_getattr_maxsz) decode_getattr_maxsz)
#define NFS4_enc_copy_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_copy_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_savefh_maxsz + \ encode_savefh_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_copy_maxsz + \ encode_copy_maxsz + \
encode_commit_maxsz) encode_commit_maxsz)
#define NFS4_dec_copy_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_copy_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_savefh_maxsz + \ decode_savefh_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_copy_maxsz + \ decode_copy_maxsz + \
decode_commit_maxsz) decode_commit_maxsz)
#define NFS4_enc_offload_cancel_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_offload_cancel_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_offload_cancel_maxsz) encode_offload_cancel_maxsz)
#define NFS4_dec_offload_cancel_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_offload_cancel_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_offload_cancel_maxsz) decode_offload_cancel_maxsz)
#define NFS4_enc_deallocate_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_deallocate_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_deallocate_maxsz + \ encode_deallocate_maxsz + \
encode_getattr_maxsz) encode_getattr_maxsz)
#define NFS4_dec_deallocate_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_deallocate_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_deallocate_maxsz + \ decode_deallocate_maxsz + \
decode_getattr_maxsz) decode_getattr_maxsz)
#define NFS4_enc_seek_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_seek_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_seek_maxsz) encode_seek_maxsz)
#define NFS4_dec_seek_sz (compound_decode_hdr_maxsz + \ #define NFS4_dec_seek_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_seek_maxsz) decode_seek_maxsz)
#define NFS4_enc_layoutstats_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_layoutstats_sz (compound_encode_hdr_maxsz + \
...@@ -106,6 +125,16 @@ ...@@ -106,6 +125,16 @@
decode_sequence_maxsz + \ decode_sequence_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
PNFS_LAYOUTSTATS_MAXDEV * decode_layoutstats_maxsz) PNFS_LAYOUTSTATS_MAXDEV * decode_layoutstats_maxsz)
#define NFS4_enc_layouterror_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \
encode_putfh_maxsz + \
NFS42_LAYOUTERROR_MAX * \
encode_layouterror_maxsz)
#define NFS4_dec_layouterror_sz (compound_decode_hdr_maxsz + \
decode_sequence_maxsz + \
decode_putfh_maxsz + \
NFS42_LAYOUTERROR_MAX * \
decode_layouterror_maxsz)
#define NFS4_enc_clone_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_clone_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \ encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
...@@ -223,6 +252,34 @@ static void encode_clone(struct xdr_stream *xdr, ...@@ -223,6 +252,34 @@ static void encode_clone(struct xdr_stream *xdr,
xdr_encode_hyper(p, args->count); xdr_encode_hyper(p, args->count);
} }
static void encode_device_error(struct xdr_stream *xdr,
const struct nfs42_device_error *error)
{
__be32 *p;
p = reserve_space(xdr, NFS4_DEVICEID4_SIZE + 2*4);
p = xdr_encode_opaque_fixed(p, error->dev_id.data,
NFS4_DEVICEID4_SIZE);
*p++ = cpu_to_be32(error->status);
*p = cpu_to_be32(error->opnum);
}
static void encode_layouterror(struct xdr_stream *xdr,
const struct nfs42_layout_error *args,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_LAYOUTERROR, decode_layouterror_maxsz, hdr);
p = reserve_space(xdr, 8 + 8);
p = xdr_encode_hyper(p, args->offset);
p = xdr_encode_hyper(p, args->length);
encode_nfs4_stateid(xdr, &args->stateid);
p = reserve_space(xdr, 4);
*p = cpu_to_be32(1);
encode_device_error(xdr, &args->errors[0]);
}
/* /*
* Encode ALLOCATE request * Encode ALLOCATE request
*/ */
...@@ -381,6 +438,27 @@ static void nfs4_xdr_enc_clone(struct rpc_rqst *req, ...@@ -381,6 +438,27 @@ static void nfs4_xdr_enc_clone(struct rpc_rqst *req,
encode_nops(&hdr); encode_nops(&hdr);
} }
/*
* Encode LAYOUTERROR request
*/
static void nfs4_xdr_enc_layouterror(struct rpc_rqst *req,
struct xdr_stream *xdr,
const void *data)
{
const struct nfs42_layouterror_args *args = data;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
int i;
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, NFS_FH(args->inode), &hdr);
for (i = 0; i < args->num_errors; i++)
encode_layouterror(xdr, &args->errors[i], &hdr);
encode_nops(&hdr);
}
static int decode_allocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res) static int decode_allocate(struct xdr_stream *xdr, struct nfs42_falloc_res *res)
{ {
return decode_op_hdr(xdr, OP_ALLOCATE); return decode_op_hdr(xdr, OP_ALLOCATE);
...@@ -394,7 +472,7 @@ static int decode_write_response(struct xdr_stream *xdr, ...@@ -394,7 +472,7 @@ static int decode_write_response(struct xdr_stream *xdr,
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; return -EIO;
count = be32_to_cpup(p); count = be32_to_cpup(p);
if (count > 1) if (count > 1)
return -EREMOTEIO; return -EREMOTEIO;
...@@ -402,18 +480,14 @@ static int decode_write_response(struct xdr_stream *xdr, ...@@ -402,18 +480,14 @@ static int decode_write_response(struct xdr_stream *xdr,
status = decode_opaque_fixed(xdr, &res->stateid, status = decode_opaque_fixed(xdr, &res->stateid,
NFS4_STATEID_SIZE); NFS4_STATEID_SIZE);
if (unlikely(status)) if (unlikely(status))
goto out_overflow; return -EIO;
} }
p = xdr_inline_decode(xdr, 8 + 4); p = xdr_inline_decode(xdr, 8 + 4);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; return -EIO;
p = xdr_decode_hyper(p, &res->count); p = xdr_decode_hyper(p, &res->count);
res->verifier.committed = be32_to_cpup(p); res->verifier.committed = be32_to_cpup(p);
return decode_verifier(xdr, &res->verifier.verifier); return decode_verifier(xdr, &res->verifier.verifier);
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
static int decode_copy_requirements(struct xdr_stream *xdr, static int decode_copy_requirements(struct xdr_stream *xdr,
...@@ -422,14 +496,11 @@ static int decode_copy_requirements(struct xdr_stream *xdr, ...@@ -422,14 +496,11 @@ static int decode_copy_requirements(struct xdr_stream *xdr,
p = xdr_inline_decode(xdr, 4 + 4); p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; return -EIO;
res->consecutive = be32_to_cpup(p++); res->consecutive = be32_to_cpup(p++);
res->synchronous = be32_to_cpup(p++); res->synchronous = be32_to_cpup(p++);
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res) static int decode_copy(struct xdr_stream *xdr, struct nfs42_copy_res *res)
...@@ -474,15 +545,11 @@ static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res) ...@@ -474,15 +545,11 @@ static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)
p = xdr_inline_decode(xdr, 4 + 8); p = xdr_inline_decode(xdr, 4 + 8);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; return -EIO;
res->sr_eof = be32_to_cpup(p++); res->sr_eof = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &res->sr_offset); p = xdr_decode_hyper(p, &res->sr_offset);
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr);
return -EIO;
} }
static int decode_layoutstats(struct xdr_stream *xdr) static int decode_layoutstats(struct xdr_stream *xdr)
...@@ -495,6 +562,11 @@ static int decode_clone(struct xdr_stream *xdr) ...@@ -495,6 +562,11 @@ static int decode_clone(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_CLONE); return decode_op_hdr(xdr, OP_CLONE);
} }
static int decode_layouterror(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_LAYOUTERROR);
}
/* /*
* Decode ALLOCATE request * Decode ALLOCATE request
*/ */
...@@ -704,4 +776,30 @@ static int nfs4_xdr_dec_clone(struct rpc_rqst *rqstp, ...@@ -704,4 +776,30 @@ static int nfs4_xdr_dec_clone(struct rpc_rqst *rqstp,
return status; return status;
} }
/*
* Decode LAYOUTERROR request
*/
static int nfs4_xdr_dec_layouterror(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfs42_layouterror_res *res = data;
struct compound_hdr hdr;
int status, i;
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
status = decode_sequence(xdr, &res->seq_res, rqstp);
if (status)
goto out;
status = decode_putfh(xdr);
for (i = 0; i < res->num_errors && status == 0; i++)
status = decode_layouterror(xdr);
out:
res->rpc_status = status;
return status;
}
#endif /* __LINUX_FS_NFS_NFS4_2XDR_H */ #endif /* __LINUX_FS_NFS_NFS4_2XDR_H */
...@@ -42,7 +42,7 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) ...@@ -42,7 +42,7 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
} }
#ifdef CONFIG_NFS_V4_1 #ifdef CONFIG_NFS_V4_1
/** /*
* Per auth flavor data server rpc clients * Per auth flavor data server rpc clients
*/ */
struct nfs4_ds_server { struct nfs4_ds_server {
...@@ -51,7 +51,9 @@ struct nfs4_ds_server { ...@@ -51,7 +51,9 @@ struct nfs4_ds_server {
}; };
/** /**
* Common lookup case for DS I/O * nfs4_find_ds_client - Common lookup case for DS I/O
* @ds_clp: pointer to the DS's nfs_client
* @flavor: rpc auth flavour to match
*/ */
static struct nfs4_ds_server * static struct nfs4_ds_server *
nfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor) nfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor)
...@@ -118,9 +120,13 @@ nfs4_free_ds_server(struct nfs4_ds_server *dss) ...@@ -118,9 +120,13 @@ nfs4_free_ds_server(struct nfs4_ds_server *dss)
} }
/** /**
* Find or create a DS rpc client with th MDS server rpc client auth flavor * nfs4_find_or_create_ds_client - Find or create a DS rpc client
* in the nfs_client cl_ds_clients list. * @ds_clp: pointer to the DS's nfs_client
*/ * @inode: pointer to the inode
*
* Find or create a DS rpc client with th MDS server rpc client auth flavor
* in the nfs_client cl_ds_clients list.
*/
struct rpc_clnt * struct rpc_clnt *
nfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode) nfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode)
{ {
...@@ -145,7 +151,6 @@ static void ...@@ -145,7 +151,6 @@ static void
nfs4_shutdown_ds_clients(struct nfs_client *clp) nfs4_shutdown_ds_clients(struct nfs_client *clp)
{ {
struct nfs4_ds_server *dss; struct nfs4_ds_server *dss;
LIST_HEAD(shutdown_list);
while (!list_empty(&clp->cl_ds_clients)) { while (!list_empty(&clp->cl_ds_clients)) {
dss = list_entry(clp->cl_ds_clients.next, dss = list_entry(clp->cl_ds_clients.next,
...@@ -284,7 +289,7 @@ static int nfs4_init_callback(struct nfs_client *clp) ...@@ -284,7 +289,7 @@ static int nfs4_init_callback(struct nfs_client *clp)
/** /**
* nfs40_init_client - nfs_client initialization tasks for NFSv4.0 * nfs40_init_client - nfs_client initialization tasks for NFSv4.0
* @clp - nfs_client to initialize * @clp: nfs_client to initialize
* *
* Returns zero on success, or a negative errno if some error occurred. * Returns zero on success, or a negative errno if some error occurred.
*/ */
...@@ -312,7 +317,7 @@ int nfs40_init_client(struct nfs_client *clp) ...@@ -312,7 +317,7 @@ int nfs40_init_client(struct nfs_client *clp)
/** /**
* nfs41_init_client - nfs_client initialization tasks for NFSv4.1+ * nfs41_init_client - nfs_client initialization tasks for NFSv4.1+
* @clp - nfs_client to initialize * @clp: nfs_client to initialize
* *
* Returns zero on success, or a negative errno if some error occurred. * Returns zero on success, or a negative errno if some error occurred.
*/ */
...@@ -360,9 +365,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) ...@@ -360,9 +365,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
* nfs4_init_client - Initialise an NFS4 client record * nfs4_init_client - Initialise an NFS4 client record
* *
* @clp: nfs_client to initialise * @clp: nfs_client to initialise
* @timeparms: timeout parameters for underlying RPC transport * @cl_init: pointer to nfs_client_initdata
* @ip_addr: callback IP address in presentation format
* @authflavor: authentication flavor for underlying RPC transport
* *
* Returns pointer to an NFS client, or an ERR_PTR value. * Returns pointer to an NFS client, or an ERR_PTR value.
*/ */
...@@ -649,13 +652,13 @@ nfs4_check_server_scope(struct nfs41_server_scope *s1, ...@@ -649,13 +652,13 @@ nfs4_check_server_scope(struct nfs41_server_scope *s1,
/** /**
* nfs4_detect_session_trunking - Checks for session trunking. * nfs4_detect_session_trunking - Checks for session trunking.
*
* Called after a successful EXCHANGE_ID on a multi-addr connection.
* Upon success, add the transport.
*
* @clp: original mount nfs_client * @clp: original mount nfs_client
* @res: result structure from an exchange_id using the original mount * @res: result structure from an exchange_id using the original mount
* nfs_client with a new multi_addr transport * nfs_client with a new multi_addr transport
* @xprt: pointer to the transport to add.
*
* Called after a successful EXCHANGE_ID on a multi-addr connection.
* Upon success, add the transport.
* *
* Returns zero on success, otherwise -EINVAL * Returns zero on success, otherwise -EINVAL
* *
......
...@@ -137,6 +137,7 @@ static size_t nfs_parse_server_name(char *string, size_t len, ...@@ -137,6 +137,7 @@ static size_t nfs_parse_server_name(char *string, size_t len,
/** /**
* nfs_find_best_sec - Find a security mechanism supported locally * nfs_find_best_sec - Find a security mechanism supported locally
* @clnt: pointer to rpc_clnt
* @server: NFS server struct * @server: NFS server struct
* @flavors: List of security tuples returned by SECINFO procedure * @flavors: List of security tuples returned by SECINFO procedure
* *
...@@ -288,8 +289,8 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, ...@@ -288,8 +289,8 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
/** /**
* nfs_follow_referral - set up mountpoint when hitting a referral on moved error * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
* @dentry - parent directory * @dentry: parent directory
* @locations - array of NFSv4 server location information * @locations: array of NFSv4 server location information
* *
*/ */
static struct vfsmount *nfs_follow_referral(struct dentry *dentry, static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
......
...@@ -730,33 +730,41 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) ...@@ -730,33 +730,41 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
res->sr_slot = NULL; res->sr_slot = NULL;
} }
static void nfs4_slot_sequence_record_sent(struct nfs4_slot *slot,
u32 seqnr)
{
if ((s32)(seqnr - slot->seq_nr_highest_sent) > 0)
slot->seq_nr_highest_sent = seqnr;
}
static void nfs4_slot_sequence_acked(struct nfs4_slot *slot,
u32 seqnr)
{
slot->seq_nr_highest_sent = seqnr;
slot->seq_nr_last_acked = seqnr;
}
static int nfs41_sequence_process(struct rpc_task *task, static int nfs41_sequence_process(struct rpc_task *task,
struct nfs4_sequence_res *res) struct nfs4_sequence_res *res)
{ {
struct nfs4_session *session; struct nfs4_session *session;
struct nfs4_slot *slot = res->sr_slot; struct nfs4_slot *slot = res->sr_slot;
struct nfs_client *clp; struct nfs_client *clp;
bool interrupted = false;
int ret = 1; int ret = 1;
if (slot == NULL) if (slot == NULL)
goto out_noaction; goto out_noaction;
/* don't increment the sequence number if the task wasn't sent */ /* don't increment the sequence number if the task wasn't sent */
if (!RPC_WAS_SENT(task)) if (!RPC_WAS_SENT(task) || slot->seq_done)
goto out; goto out;
session = slot->table->session; session = slot->table->session;
if (slot->interrupted) {
if (res->sr_status != -NFS4ERR_DELAY)
slot->interrupted = 0;
interrupted = true;
}
trace_nfs4_sequence_done(session, res); trace_nfs4_sequence_done(session, res);
/* Check the SEQUENCE operation status */ /* Check the SEQUENCE operation status */
switch (res->sr_status) { switch (res->sr_status) {
case 0: case 0:
/* Mark this sequence number as having been acked */
nfs4_slot_sequence_acked(slot, slot->seq_nr);
/* Update the slot's sequence and clientid lease timer */ /* Update the slot's sequence and clientid lease timer */
slot->seq_done = 1; slot->seq_done = 1;
clp = session->clp; clp = session->clp;
...@@ -771,9 +779,9 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -771,9 +779,9 @@ static int nfs41_sequence_process(struct rpc_task *task,
* sr_status remains 1 if an RPC level error occurred. * sr_status remains 1 if an RPC level error occurred.
* The server may or may not have processed the sequence * The server may or may not have processed the sequence
* operation.. * operation..
* Mark the slot as having hosted an interrupted RPC call.
*/ */
slot->interrupted = 1; nfs4_slot_sequence_record_sent(slot, slot->seq_nr);
slot->seq_done = 1;
goto out; goto out;
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
/* The server detected a resend of the RPC call and /* The server detected a resend of the RPC call and
...@@ -784,6 +792,7 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -784,6 +792,7 @@ static int nfs41_sequence_process(struct rpc_task *task,
__func__, __func__,
slot->slot_nr, slot->slot_nr,
slot->seq_nr); slot->seq_nr);
nfs4_slot_sequence_acked(slot, slot->seq_nr);
goto out_retry; goto out_retry;
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
case -NFS4ERR_SEQ_FALSE_RETRY: case -NFS4ERR_SEQ_FALSE_RETRY:
...@@ -791,6 +800,7 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -791,6 +800,7 @@ static int nfs41_sequence_process(struct rpc_task *task,
* The server thinks we tried to replay a request. * The server thinks we tried to replay a request.
* Retry the call after bumping the sequence ID. * Retry the call after bumping the sequence ID.
*/ */
nfs4_slot_sequence_acked(slot, slot->seq_nr);
goto retry_new_seq; goto retry_new_seq;
case -NFS4ERR_BADSLOT: case -NFS4ERR_BADSLOT:
/* /*
...@@ -801,21 +811,28 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -801,21 +811,28 @@ static int nfs41_sequence_process(struct rpc_task *task,
goto session_recover; goto session_recover;
goto retry_nowait; goto retry_nowait;
case -NFS4ERR_SEQ_MISORDERED: case -NFS4ERR_SEQ_MISORDERED:
nfs4_slot_sequence_record_sent(slot, slot->seq_nr);
/* /*
* Was the last operation on this sequence interrupted? * Were one or more calls using this slot interrupted?
* If so, retry after bumping the sequence number. * If the server never received the request, then our
*/ * transmitted slot sequence number may be too high.
if (interrupted)
goto retry_new_seq;
/*
* Could this slot have been previously retired?
* If so, then the server may be expecting seq_nr = 1!
*/ */
if (slot->seq_nr != 1) { if ((s32)(slot->seq_nr - slot->seq_nr_last_acked) > 1) {
slot->seq_nr = 1; slot->seq_nr--;
goto retry_nowait; goto retry_nowait;
} }
goto session_recover; /*
* RFC5661:
* A retry might be sent while the original request is
* still in progress on the replier. The replier SHOULD
* deal with the issue by returning NFS4ERR_DELAY as the
* reply to SEQUENCE or CB_SEQUENCE operation, but
* implementations MAY return NFS4ERR_SEQ_MISORDERED.
*
* Restart the search after a delay.
*/
slot->seq_nr = slot->seq_nr_highest_sent;
goto out_retry;
default: default:
/* Just update the slot sequence no. */ /* Just update the slot sequence no. */
slot->seq_done = 1; slot->seq_done = 1;
...@@ -906,17 +923,6 @@ static const struct rpc_call_ops nfs41_call_sync_ops = { ...@@ -906,17 +923,6 @@ static const struct rpc_call_ops nfs41_call_sync_ops = {
.rpc_call_done = nfs41_call_sync_done, .rpc_call_done = nfs41_call_sync_done,
}; };
static void
nfs4_sequence_process_interrupted(struct nfs_client *client,
struct nfs4_slot *slot, const struct cred *cred)
{
struct rpc_task *task;
task = _nfs41_proc_sequence(client, cred, slot, true);
if (!IS_ERR(task))
rpc_put_task_async(task);
}
#else /* !CONFIG_NFS_V4_1 */ #else /* !CONFIG_NFS_V4_1 */
static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res) static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
...@@ -937,16 +943,15 @@ int nfs4_sequence_done(struct rpc_task *task, ...@@ -937,16 +943,15 @@ int nfs4_sequence_done(struct rpc_task *task,
} }
EXPORT_SYMBOL_GPL(nfs4_sequence_done); EXPORT_SYMBOL_GPL(nfs4_sequence_done);
static void #endif /* !CONFIG_NFS_V4_1 */
nfs4_sequence_process_interrupted(struct nfs_client *client,
struct nfs4_slot *slot, const struct cred *cred) static void nfs41_sequence_res_init(struct nfs4_sequence_res *res)
{ {
WARN_ON_ONCE(1); res->sr_timestamp = jiffies;
slot->interrupted = 0; res->sr_status_flags = 0;
res->sr_status = 1;
} }
#endif /* !CONFIG_NFS_V4_1 */
static static
void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args, void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, struct nfs4_sequence_res *res,
...@@ -958,10 +963,6 @@ void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args, ...@@ -958,10 +963,6 @@ void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args,
args->sa_slot = slot; args->sa_slot = slot;
res->sr_slot = slot; res->sr_slot = slot;
res->sr_timestamp = jiffies;
res->sr_status_flags = 0;
res->sr_status = 1;
} }
int nfs4_setup_sequence(struct nfs_client *client, int nfs4_setup_sequence(struct nfs_client *client,
...@@ -982,31 +983,25 @@ int nfs4_setup_sequence(struct nfs_client *client, ...@@ -982,31 +983,25 @@ int nfs4_setup_sequence(struct nfs_client *client,
task->tk_timeout = 0; task->tk_timeout = 0;
} }
for (;;) { spin_lock(&tbl->slot_tbl_lock);
spin_lock(&tbl->slot_tbl_lock); /* The state manager will wait until the slot table is empty */
/* The state manager will wait until the slot table is empty */ if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged)
if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged) goto out_sleep;
goto out_sleep;
slot = nfs4_alloc_slot(tbl);
if (IS_ERR(slot)) {
/* Try again in 1/4 second */
if (slot == ERR_PTR(-ENOMEM))
task->tk_timeout = HZ >> 2;
goto out_sleep;
}
spin_unlock(&tbl->slot_tbl_lock);
if (likely(!slot->interrupted)) slot = nfs4_alloc_slot(tbl);
break; if (IS_ERR(slot)) {
nfs4_sequence_process_interrupted(client, /* Try again in 1/4 second */
slot, task->tk_msg.rpc_cred); if (slot == ERR_PTR(-ENOMEM))
task->tk_timeout = HZ >> 2;
goto out_sleep;
} }
spin_unlock(&tbl->slot_tbl_lock);
nfs4_sequence_attach_slot(args, res, slot); nfs4_sequence_attach_slot(args, res, slot);
trace_nfs4_setup_sequence(session, args); trace_nfs4_setup_sequence(session, args);
out_start: out_start:
nfs41_sequence_res_init(res);
rpc_call_start(task); rpc_call_start(task);
return 0; return 0;
...@@ -1555,6 +1550,10 @@ static void nfs_clear_open_stateid(struct nfs4_state *state, ...@@ -1555,6 +1550,10 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,
static void nfs_set_open_stateid_locked(struct nfs4_state *state, static void nfs_set_open_stateid_locked(struct nfs4_state *state,
const nfs4_stateid *stateid, nfs4_stateid *freeme) const nfs4_stateid *stateid, nfs4_stateid *freeme)
__must_hold(&state->owner->so_lock)
__must_hold(&state->seqlock)
__must_hold(RCU)
{ {
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int status = 0; int status = 0;
...@@ -5963,7 +5962,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, ...@@ -5963,7 +5962,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
/** /**
* nfs4_proc_setclientid_confirm - Confirm client ID * nfs4_proc_setclientid_confirm - Confirm client ID
* @clp: state data structure * @clp: state data structure
* @res: result of a previous SETCLIENTID * @arg: result of a previous SETCLIENTID
* @cred: credential to use for this call * @cred: credential to use for this call
* *
* Returns zero, a negative errno, or a negative NFS4ERR status code. * Returns zero, a negative errno, or a negative NFS4ERR status code.
...@@ -7527,7 +7526,7 @@ int nfs4_proc_fsid_present(struct inode *inode, const struct cred *cred) ...@@ -7527,7 +7526,7 @@ int nfs4_proc_fsid_present(struct inode *inode, const struct cred *cred)
return status; return status;
} }
/** /*
* If 'use_integrity' is true and the state managment nfs_client * If 'use_integrity' is true and the state managment nfs_client
* cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient * cl_rpcclient is using krb5i/p, use the integrity protected cl_rpcclient
* and the machine credential as per RFC3530bis and RFC5661 Security * and the machine credential as per RFC3530bis and RFC5661 Security
...@@ -8937,10 +8936,12 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout) ...@@ -8937,10 +8936,12 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
if (status != 0) if (status != 0)
goto out; goto out;
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */ if (task->tk_status < 0) {
if (task->tk_status < 0 || lgp->res.layoutp->len == 0) {
status = nfs4_layoutget_handle_exception(task, lgp, &exception); status = nfs4_layoutget_handle_exception(task, lgp, &exception);
*timeout = exception.timeout; *timeout = exception.timeout;
} else if (lgp->res.layoutp->len == 0) {
status = -EAGAIN;
*timeout = nfs4_update_delay(&exception.timeout);
} else } else
lseg = pnfs_layout_process(lgp); lseg = pnfs_layout_process(lgp);
out: out:
...@@ -9219,7 +9220,7 @@ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync) ...@@ -9219,7 +9220,7 @@ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
return status; return status;
} }
/** /*
* Use the state managment nfs_client cl_rpcclient, which uses krb5i (if * Use the state managment nfs_client cl_rpcclient, which uses krb5i (if
* possible) as per RFC3530bis and RFC5661 Security Considerations sections * possible) as per RFC3530bis and RFC5661 Security Considerations sections
*/ */
...@@ -9484,7 +9485,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = { ...@@ -9484,7 +9485,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = {
* @server: server / transport on which to perform the operation * @server: server / transport on which to perform the operation
* @stateid: state ID to release * @stateid: state ID to release
* @cred: credential * @cred: credential
* @is_recovery: set to true if this call needs to be privileged * @privileged: set to true if this call needs to be privileged
* *
* Note: this function is always asynchronous. * Note: this function is always asynchronous.
*/ */
...@@ -9691,7 +9692,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { ...@@ -9691,7 +9692,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
| NFS_CAP_DEALLOCATE | NFS_CAP_DEALLOCATE
| NFS_CAP_SEEK | NFS_CAP_SEEK
| NFS_CAP_LAYOUTSTATS | NFS_CAP_LAYOUTSTATS
| NFS_CAP_CLONE, | NFS_CAP_CLONE
| NFS_CAP_LAYOUTERROR,
.init_client = nfs41_init_client, .init_client = nfs41_init_client,
.shutdown_client = nfs41_shutdown_client, .shutdown_client = nfs41_shutdown_client,
.match_stateid = nfs41_match_stateid, .match_stateid = nfs41_match_stateid,
......
...@@ -55,7 +55,7 @@ static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize) ...@@ -55,7 +55,7 @@ static void nfs4_shrink_slot_table(struct nfs4_slot_table *tbl, u32 newsize)
/** /**
* nfs4_slot_tbl_drain_complete - wake waiters when drain is complete * nfs4_slot_tbl_drain_complete - wake waiters when drain is complete
* @tbl - controlling slot table * @tbl: controlling slot table
* *
*/ */
void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl) void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl)
...@@ -110,6 +110,8 @@ static struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl, ...@@ -110,6 +110,8 @@ static struct nfs4_slot *nfs4_new_slot(struct nfs4_slot_table *tbl,
slot->table = tbl; slot->table = tbl;
slot->slot_nr = slotid; slot->slot_nr = slotid;
slot->seq_nr = seq_init; slot->seq_nr = seq_init;
slot->seq_nr_highest_sent = seq_init;
slot->seq_nr_last_acked = seq_init - 1;
} }
return slot; return slot;
} }
...@@ -276,7 +278,8 @@ static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl, ...@@ -276,7 +278,8 @@ static void nfs4_reset_slot_table(struct nfs4_slot_table *tbl,
p = &tbl->slots; p = &tbl->slots;
while (*p) { while (*p) {
(*p)->seq_nr = ivalue; (*p)->seq_nr = ivalue;
(*p)->interrupted = 0; (*p)->seq_nr_highest_sent = ivalue;
(*p)->seq_nr_last_acked = ivalue - 1;
p = &(*p)->next; p = &(*p)->next;
} }
tbl->highest_used_slotid = NFS4_NO_SLOT; tbl->highest_used_slotid = NFS4_NO_SLOT;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
/* maximum number of slots to use */ /* maximum number of slots to use */
#define NFS4_DEF_SLOT_TABLE_SIZE (64U) #define NFS4_DEF_SLOT_TABLE_SIZE (64U)
#define NFS4_DEF_CB_SLOT_TABLE_SIZE (1U) #define NFS4_DEF_CB_SLOT_TABLE_SIZE (16U)
#define NFS4_MAX_SLOT_TABLE (1024U) #define NFS4_MAX_SLOT_TABLE (1024U)
#define NFS4_NO_SLOT ((u32)-1) #define NFS4_NO_SLOT ((u32)-1)
...@@ -23,8 +23,9 @@ struct nfs4_slot { ...@@ -23,8 +23,9 @@ struct nfs4_slot {
unsigned long generation; unsigned long generation;
u32 slot_nr; u32 slot_nr;
u32 seq_nr; u32 seq_nr;
unsigned int interrupted : 1, u32 seq_nr_last_acked;
privileged : 1, u32 seq_nr_highest_sent;
unsigned int privileged : 1,
seq_done : 1; seq_done : 1;
}; };
......
...@@ -563,6 +563,7 @@ static void nfs4_gc_state_owners(struct nfs_server *server) ...@@ -563,6 +563,7 @@ static void nfs4_gc_state_owners(struct nfs_server *server)
* nfs4_get_state_owner - Look up a state owner given a credential * nfs4_get_state_owner - Look up a state owner given a credential
* @server: nfs_server to search * @server: nfs_server to search
* @cred: RPC credential to match * @cred: RPC credential to match
* @gfp_flags: allocation mode
* *
* Returns a pointer to an instantiated nfs4_state_owner struct, or NULL. * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL.
*/ */
......
...@@ -524,6 +524,31 @@ TRACE_EVENT(nfs4_setup_sequence, ...@@ -524,6 +524,31 @@ TRACE_EVENT(nfs4_setup_sequence,
) )
); );
TRACE_EVENT(nfs4_xdr_status,
TP_PROTO(
u32 op,
int error
),
TP_ARGS(op, error),
TP_STRUCT__entry(
__field(u32, op)
__field(int, error)
),
TP_fast_assign(
__entry->op = op;
__entry->error = -error;
),
TP_printk(
"operation %d: nfs status %d (%s)",
__entry->op,
__entry->error, show_nfsv4_errors(__entry->error)
)
);
DECLARE_EVENT_CLASS(nfs4_open_event, DECLARE_EVENT_CLASS(nfs4_open_event,
TP_PROTO( TP_PROTO(
const struct nfs_open_context *ctx, const struct nfs_open_context *ctx,
......
This diff is collapsed.
...@@ -11,3 +11,4 @@ ...@@ -11,3 +11,4 @@
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter); EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter);
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit); EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit);
EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status);
...@@ -969,6 +969,91 @@ TRACE_EVENT(nfs_commit_done, ...@@ -969,6 +969,91 @@ TRACE_EVENT(nfs_commit_done,
) )
); );
TRACE_DEFINE_ENUM(NFS_OK);
TRACE_DEFINE_ENUM(NFSERR_PERM);
TRACE_DEFINE_ENUM(NFSERR_NOENT);
TRACE_DEFINE_ENUM(NFSERR_IO);
TRACE_DEFINE_ENUM(NFSERR_NXIO);
TRACE_DEFINE_ENUM(NFSERR_ACCES);
TRACE_DEFINE_ENUM(NFSERR_EXIST);
TRACE_DEFINE_ENUM(NFSERR_XDEV);
TRACE_DEFINE_ENUM(NFSERR_NODEV);
TRACE_DEFINE_ENUM(NFSERR_NOTDIR);
TRACE_DEFINE_ENUM(NFSERR_ISDIR);
TRACE_DEFINE_ENUM(NFSERR_INVAL);
TRACE_DEFINE_ENUM(NFSERR_FBIG);
TRACE_DEFINE_ENUM(NFSERR_NOSPC);
TRACE_DEFINE_ENUM(NFSERR_ROFS);
TRACE_DEFINE_ENUM(NFSERR_MLINK);
TRACE_DEFINE_ENUM(NFSERR_NAMETOOLONG);
TRACE_DEFINE_ENUM(NFSERR_NOTEMPTY);
TRACE_DEFINE_ENUM(NFSERR_DQUOT);
TRACE_DEFINE_ENUM(NFSERR_STALE);
TRACE_DEFINE_ENUM(NFSERR_REMOTE);
TRACE_DEFINE_ENUM(NFSERR_WFLUSH);
TRACE_DEFINE_ENUM(NFSERR_BADHANDLE);
TRACE_DEFINE_ENUM(NFSERR_NOT_SYNC);
TRACE_DEFINE_ENUM(NFSERR_BAD_COOKIE);
TRACE_DEFINE_ENUM(NFSERR_NOTSUPP);
TRACE_DEFINE_ENUM(NFSERR_TOOSMALL);
TRACE_DEFINE_ENUM(NFSERR_SERVERFAULT);
TRACE_DEFINE_ENUM(NFSERR_BADTYPE);
TRACE_DEFINE_ENUM(NFSERR_JUKEBOX);
#define nfs_show_status(x) \
__print_symbolic(x, \
{ NFS_OK, "OK" }, \
{ NFSERR_PERM, "PERM" }, \
{ NFSERR_NOENT, "NOENT" }, \
{ NFSERR_IO, "IO" }, \
{ NFSERR_NXIO, "NXIO" }, \
{ NFSERR_ACCES, "ACCES" }, \
{ NFSERR_EXIST, "EXIST" }, \
{ NFSERR_XDEV, "XDEV" }, \
{ NFSERR_NODEV, "NODEV" }, \
{ NFSERR_NOTDIR, "NOTDIR" }, \
{ NFSERR_ISDIR, "ISDIR" }, \
{ NFSERR_INVAL, "INVAL" }, \
{ NFSERR_FBIG, "FBIG" }, \
{ NFSERR_NOSPC, "NOSPC" }, \
{ NFSERR_ROFS, "ROFS" }, \
{ NFSERR_MLINK, "MLINK" }, \
{ NFSERR_NAMETOOLONG, "NAMETOOLONG" }, \
{ NFSERR_NOTEMPTY, "NOTEMPTY" }, \
{ NFSERR_DQUOT, "DQUOT" }, \
{ NFSERR_STALE, "STALE" }, \
{ NFSERR_REMOTE, "REMOTE" }, \
{ NFSERR_WFLUSH, "WFLUSH" }, \
{ NFSERR_BADHANDLE, "BADHANDLE" }, \
{ NFSERR_NOT_SYNC, "NOTSYNC" }, \
{ NFSERR_BAD_COOKIE, "BADCOOKIE" }, \
{ NFSERR_NOTSUPP, "NOTSUPP" }, \
{ NFSERR_TOOSMALL, "TOOSMALL" }, \
{ NFSERR_SERVERFAULT, "REMOTEIO" }, \
{ NFSERR_BADTYPE, "BADTYPE" }, \
{ NFSERR_JUKEBOX, "JUKEBOX" })
TRACE_EVENT(nfs_xdr_status,
TP_PROTO(
int error
),
TP_ARGS(error),
TP_STRUCT__entry(
__field(int, error)
),
TP_fast_assign(
__entry->error = error;
),
TP_printk(
"error=%d (%s)",
__entry->error, nfs_show_status(__entry->error)
)
);
#endif /* _TRACE_NFS_H */ #endif /* _TRACE_NFS_H */
#undef TRACE_INCLUDE_PATH #undef TRACE_INCLUDE_PATH
......
This diff is collapsed.
This diff is collapsed.
...@@ -104,6 +104,7 @@ enum { ...@@ -104,6 +104,7 @@ enum {
NFS_LAYOUT_RETURN_REQUESTED, /* Return this layout ASAP */ NFS_LAYOUT_RETURN_REQUESTED, /* Return this layout ASAP */
NFS_LAYOUT_INVALID_STID, /* layout stateid id is invalid */ NFS_LAYOUT_INVALID_STID, /* layout stateid id is invalid */
NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */ NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */
NFS_LAYOUT_INODE_FREEING, /* The inode is being freed */
}; };
enum layoutdriver_policy_flags { enum layoutdriver_policy_flags {
...@@ -349,6 +350,7 @@ void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nf ...@@ -349,6 +350,7 @@ void nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *, const struct nf
void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, struct nfs_server *, void nfs4_init_deviceid_node(struct nfs4_deviceid_node *, struct nfs_server *,
const struct nfs4_deviceid *); const struct nfs4_deviceid *);
bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *); bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *);
void nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node);
void nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node); void nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node);
bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node); bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node);
void nfs4_deviceid_purge_client(const struct nfs_client *); void nfs4_deviceid_purge_client(const struct nfs_client *);
......
...@@ -283,11 +283,23 @@ nfs4_put_deviceid_node(struct nfs4_deviceid_node *d) ...@@ -283,11 +283,23 @@ nfs4_put_deviceid_node(struct nfs4_deviceid_node *d)
} }
EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node); EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node);
void
nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node)
{
if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) {
clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
smp_mb__after_atomic();
}
}
EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_available);
void void
nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node) nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node)
{ {
node->timestamp_unavailable = jiffies; node->timestamp_unavailable = jiffies;
smp_mb__before_atomic();
set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
smp_mb__after_atomic();
} }
EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable); EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable);
...@@ -302,6 +314,7 @@ nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node) ...@@ -302,6 +314,7 @@ nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node)
if (time_in_range(node->timestamp_unavailable, start, end)) if (time_in_range(node->timestamp_unavailable, start, end))
return true; return true;
clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags); clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
smp_mb__after_atomic();
} }
return false; return false;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment