Skip to content

Commit

Permalink
Merge tag 'nfs-for-4.19-1' of git://git.linux-nfs.org/projects/anna/l…
Browse files Browse the repository at this point in the history
…inux-nfs

Pull NFS client updates from Anna Schumaker:
 "These patches include adding async support for the v4.2 COPY
  operation. I think Bruce is planning to send the server patches for
  the next release, but I figured we could get the client side out of
  the way now since it's been in my tree for a while. This shouldn't
  cause any problems, since the server will still respond with
  synchronous copies even if the client requests async.

  Features:
   - Add support for asynchronous server-side COPY operations

  Stable bufixes:
   - Fix an off-by-one in bl_map_stripe() (v3.17+)
   - NFSv4 client live hangs after live data migration recovery (v4.9+)
   - xprtrdma: Fix disconnect regression (v4.18+)
   - Fix locking in pnfs_generic_recover_commit_reqs (v4.14+)
   - Fix a sleep in atomic context in nfs4_callback_sequence() (v4.9+)

  Other bugfixes and cleanups:
   - Optimizations and fixes involving NFS v4.1 / pNFS layout handling
   - Optimize lseek(fd, SEEK_CUR, 0) on directories to avoid locking
   - Immediately reschedule writeback when the server replies with an
     error
   - Fix excessive attribute revalidation in nfs_execute_ok()
   - Add error checking to nfs_idmap_prepare_message()
   - Use new vm_fault_t return type
   - Return a delegation when reclaiming one that the server has
     recalled
   - Referrals should inherit proto setting from parents
   - Make rpc_auth_create_args a const
   - Improvements to rpc_iostats tracking
   - Fix a potential reference leak when there is an error processing a
     callback
   - Fix rmdir / mkdir / rename nlink accounting
   - Fix updating inode change attribute
   - Fix error handling in nfsn4_sp4_select_mode()
   - Use an appropriate work queue for direct-write completion
   - Don't busy wait if NFSv4 session draining is interrupted"

* tag 'nfs-for-4.19-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (54 commits)
  pNFS: Remove unwanted optimisation of layoutget
  pNFS/flexfiles: ff_layout_pg_init_read should exit on error
  pNFS: Treat RECALLCONFLICT like DELAY...
  pNFS: When updating the stateid in layoutreturn, also update the recall range
  NFSv4: Fix a sleep in atomic context in nfs4_callback_sequence()
  NFSv4: Fix locking in pnfs_generic_recover_commit_reqs
  NFSv4: Fix a typo in nfs4_init_channel_attrs()
  NFSv4: Don't busy wait if NFSv4 session draining is interrupted
  NFS recover from destination server reboot for copies
  NFS add a simple sync nfs4_proc_commit after async COPY
  NFS handle COPY ERR_OFFLOAD_NO_REQS
  NFS send OFFLOAD_CANCEL when COPY killed
  NFS export nfs4_async_handle_error
  NFS handle COPY reply CB_OFFLOAD call race
  NFS add support for asynchronous COPY
  NFS COPY xdr handle async reply
  NFS OFFLOAD_CANCEL xdr
  NFS CB_OFFLOAD xdr
  NFS: Use an appropriate work queue for direct-write completion
  NFSv4: Fix error handling in nfs4_sp4_select_mode()
  ...
  • Loading branch information
torvalds committed Aug 23, 2018
2 parents 9157141 + 0af4c8b commit 53a01c9
Show file tree
Hide file tree
Showing 52 changed files with 924 additions and 216 deletions.
1 change: 1 addition & 0 deletions fs/nfs/blocklayout/blocklayout.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ bl_alloc_lseg(struct pnfs_layout_hdr *lo, struct nfs4_layoutget_res *lgr,
case -ENODEV:
/* Our extent block devices are unavailable */
set_bit(NFS_LSEG_UNAVAILABLE, &lseg->pls_flags);
/* Fall through */
case 0:
return lseg;
default:
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/blocklayout/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ static bool bl_map_stripe(struct pnfs_block_dev *dev, u64 offset,
chunk = div_u64(offset, dev->chunk_size);
div_u64_rem(chunk, dev->nr_children, &chunk_idx);

if (chunk_idx > dev->nr_children) {
if (chunk_idx >= dev->nr_children) {
dprintk("%s: invalid chunk idx %d (%lld/%lld)\n",
__func__, chunk_idx, offset, dev->chunk_size);
/* error, should not happen */
Expand Down
12 changes: 12 additions & 0 deletions fs/nfs/callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,18 @@ struct cb_notify_lock_args {
extern __be32 nfs4_callback_notify_lock(void *argp, void *resp,
struct cb_process_state *cps);
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
struct cb_offloadargs {
struct nfs_fh coa_fh;
nfs4_stateid coa_stateid;
uint32_t error;
uint64_t wr_count;
struct nfs_writeverf wr_writeverf;
};

extern __be32 nfs4_callback_offload(void *args, void *dummy,
struct cb_process_state *cps);
#endif /* CONFIG_NFS_V4_2 */
extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *);
extern __be32 nfs4_callback_getattr(void *argp, void *resp,
struct cb_process_state *cps);
Expand Down
97 changes: 79 additions & 18 deletions fs/nfs/callback_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,9 @@ static u32 pnfs_check_callback_stateid(struct pnfs_layout_hdr *lo,
{
u32 oldseq, newseq;

/* Is the stateid still not initialised? */
/* Is the stateid not initialised? */
if (!pnfs_layout_is_valid(lo))
return NFS4ERR_DELAY;
return NFS4ERR_NOMATCHING_LAYOUT;

/* Mismatched stateid? */
if (!nfs4_stateid_match_other(&lo->plh_stateid, new))
Expand Down Expand Up @@ -273,7 +273,6 @@ static u32 initiate_file_draining(struct nfs_client *clp,
rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
if (rv != NFS_OK)
goto unlock;
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);

/*
* Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
Expand All @@ -283,19 +282,23 @@ static u32 initiate_file_draining(struct nfs_client *clp,
goto unlock;
}

if (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
switch (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
&args->cbl_range,
be32_to_cpu(args->cbl_stateid.seqid))) {
case 0:
case -EBUSY:
/* There are layout segments that need to be returned */
rv = NFS4_OK;
goto unlock;
}

/* Embrace your forgetfulness! */
rv = NFS4ERR_NOMATCHING_LAYOUT;
break;
case -ENOENT:
/* Embrace your forgetfulness! */
rv = NFS4ERR_NOMATCHING_LAYOUT;

if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
&args->cbl_range);
if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
&args->cbl_range);
}
}
unlock:
spin_unlock(&ino->i_lock);
Expand Down Expand Up @@ -328,8 +331,6 @@ static u32 initiate_bulk_draining(struct nfs_client *clp,
static u32 do_callback_layoutrecall(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
write_seqcount_begin(&clp->cl_callback_count);
write_seqcount_end(&clp->cl_callback_count);
if (args->cbl_recall_type == RETURN_FILE)
return initiate_file_draining(clp, args);
return initiate_bulk_draining(clp, args);
Expand Down Expand Up @@ -441,11 +442,14 @@ validate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot,
* a match. If the slot is in use and the sequence numbers match, the
* client is still waiting for a response to the original request.
*/
static bool referring_call_exists(struct nfs_client *clp,
static int referring_call_exists(struct nfs_client *clp,
uint32_t nrclists,
struct referring_call_list *rclists)
struct referring_call_list *rclists,
spinlock_t *lock)
__releases(lock)
__acquires(lock)
{
bool status = false;
int status = 0;
int i, j;
struct nfs4_session *session;
struct nfs4_slot_table *tbl;
Expand All @@ -468,8 +472,10 @@ static bool referring_call_exists(struct nfs_client *clp,

for (j = 0; j < rclist->rcl_nrefcalls; j++) {
ref = &rclist->rcl_refcalls[j];
spin_unlock(lock);
status = nfs4_slot_wait_on_seqid(tbl, ref->rc_slotid,
ref->rc_sequenceid, HZ >> 1) < 0;
spin_lock(lock);
if (status)
goto out;
}
Expand Down Expand Up @@ -546,7 +552,8 @@ __be32 nfs4_callback_sequence(void *argp, void *resp,
* related callback was received before the response to the original
* call.
*/
if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists,
&tbl->slot_tbl_lock) < 0) {
status = htonl(NFS4ERR_DELAY);
goto out_unlock;
}
Expand Down Expand Up @@ -660,3 +667,57 @@ __be32 nfs4_callback_notify_lock(void *argp, void *resp,
return htonl(NFS4_OK);
}
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
static void nfs4_copy_cb_args(struct nfs4_copy_state *cp_state,
struct cb_offloadargs *args)
{
cp_state->count = args->wr_count;
cp_state->error = args->error;
if (!args->error) {
cp_state->verf.committed = args->wr_writeverf.committed;
memcpy(&cp_state->verf.verifier.data[0],
&args->wr_writeverf.verifier.data[0],
NFS4_VERIFIER_SIZE);
}
}

__be32 nfs4_callback_offload(void *data, void *dummy,
struct cb_process_state *cps)
{
struct cb_offloadargs *args = data;
struct nfs_server *server;
struct nfs4_copy_state *copy;
bool found = false;

spin_lock(&cps->clp->cl_lock);
rcu_read_lock();
list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
client_link) {
list_for_each_entry(copy, &server->ss_copies, copies) {
if (memcmp(args->coa_stateid.other,
copy->stateid.other,
sizeof(args->coa_stateid.other)))
continue;
nfs4_copy_cb_args(copy, args);
complete(&copy->completion);
found = true;
goto out;
}
}
out:
rcu_read_unlock();
if (!found) {
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
if (!copy) {
spin_unlock(&cps->clp->cl_lock);
return htonl(NFS4ERR_SERVERFAULT);
}
memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
nfs4_copy_cb_args(copy, args);
list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
}
spin_unlock(&cps->clp->cl_lock);

return 0;
}
#endif /* CONFIG_NFS_V4_2 */
91 changes: 87 additions & 4 deletions fs/nfs/callback_xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
#define CB_OP_RECALLSLOT_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define CB_OP_NOTIFY_LOCK_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
#define CB_OP_OFFLOAD_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#endif /* CONFIG_NFS_V4_2 */

#define NFSDBG_FACILITY NFSDBG_CALLBACK

Expand Down Expand Up @@ -527,7 +530,72 @@ static __be32 decode_notify_lock_args(struct svc_rqst *rqstp,
}

#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
static __be32 decode_write_response(struct xdr_stream *xdr,
struct cb_offloadargs *args)
{
__be32 *p;

/* skip the always zero field */
p = read_buf(xdr, 4);
if (unlikely(!p))
goto out;
p++;

/* decode count, stable_how, verifier */
p = xdr_inline_decode(xdr, 8 + 4);
if (unlikely(!p))
goto out;
p = xdr_decode_hyper(p, &args->wr_count);
args->wr_writeverf.committed = be32_to_cpup(p);
p = xdr_inline_decode(xdr, NFS4_VERIFIER_SIZE);
if (likely(p)) {
memcpy(&args->wr_writeverf.verifier.data[0], p,
NFS4_VERIFIER_SIZE);
return 0;
}
out:
return htonl(NFS4ERR_RESOURCE);
}

static __be32 decode_offload_args(struct svc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct cb_offloadargs *args = data;
__be32 *p;
__be32 status;

/* decode fh */
status = decode_fh(xdr, &args->coa_fh);
if (unlikely(status != 0))
return status;

/* decode stateid */
status = decode_stateid(xdr, &args->coa_stateid);
if (unlikely(status != 0))
return status;

/* decode status */
p = read_buf(xdr, 4);
if (unlikely(!p))
goto out;
args->error = ntohl(*p++);
if (!args->error) {
status = decode_write_response(xdr, args);
if (unlikely(status != 0))
return status;
} else {
p = xdr_inline_decode(xdr, 8);
if (unlikely(!p))
goto out;
p = xdr_decode_hyper(p, &args->wr_count);
}
return 0;
out:
return htonl(NFS4ERR_RESOURCE);
}
#endif /* CONFIG_NFS_V4_2 */
static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
if (unlikely(xdr_stream_encode_opaque(xdr, str, len) < 0))
Expand Down Expand Up @@ -773,7 +841,10 @@ preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
if (status != htonl(NFS4ERR_OP_ILLEGAL))
return status;

if (op_nr == OP_CB_OFFLOAD)
if (op_nr == OP_CB_OFFLOAD) {
*op = &callback_ops[op_nr];
return htonl(NFS_OK);
} else
return htonl(NFS4ERR_NOTSUPP);
return htonl(NFS4ERR_OP_ILLEGAL);
}
Expand Down Expand Up @@ -883,16 +954,21 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)

if (hdr_arg.minorversion == 0) {
cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) {
if (cps.clp)
nfs_put_client(cps.clp);
goto out_invalidcred;
}
}

cps.minorversion = hdr_arg.minorversion;
hdr_res.taglen = hdr_arg.taglen;
hdr_res.tag = hdr_arg.tag;
if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) {
if (cps.clp)
nfs_put_client(cps.clp);
return rpc_system_err;

}
while (status == 0 && nops != hdr_arg.nops) {
status = process_op(nops, rqstp, &xdr_in,
rqstp->rq_argp, &xdr_out, rqstp->rq_resp,
Expand Down Expand Up @@ -969,6 +1045,13 @@ static struct callback_op callback_ops[] = {
.res_maxsize = CB_OP_NOTIFY_LOCK_RES_MAXSZ,
},
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
[OP_CB_OFFLOAD] = {
.process_op = nfs4_callback_offload,
.decode_args = decode_offload_args,
.res_maxsize = CB_OP_OFFLOAD_RES_MAXSZ,
},
#endif /* CONFIG_NFS_V4_2 */
};

/*
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ struct nfs_server *nfs_alloc_server(void)
INIT_LIST_HEAD(&server->delegations);
INIT_LIST_HEAD(&server->layouts);
INIT_LIST_HEAD(&server->state_owners_lru);
INIT_LIST_HEAD(&server->ss_copies);

atomic_set(&server->active, 0);

Expand Down
32 changes: 20 additions & 12 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -904,23 +904,29 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
dfprintk(FILE, "NFS: llseek dir(%pD2, %lld, %d)\n",
filp, offset, whence);

inode_lock(inode);
switch (whence) {
case 1:
offset += filp->f_pos;
case 0:
if (offset >= 0)
break;
default:
offset = -EINVAL;
goto out;
default:
return -EINVAL;
case SEEK_SET:
if (offset < 0)
return -EINVAL;
inode_lock(inode);
break;
case SEEK_CUR:
if (offset == 0)
return filp->f_pos;
inode_lock(inode);
offset += filp->f_pos;
if (offset < 0) {
inode_unlock(inode);
return -EINVAL;
}
}
if (offset != filp->f_pos) {
filp->f_pos = offset;
dir_ctx->dir_cookie = 0;
dir_ctx->duped = 0;
}
out:
inode_unlock(inode);
return offset;
}
Expand Down Expand Up @@ -1032,7 +1038,7 @@ int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)
if (flags & LOOKUP_REVAL)
goto out_force;
out:
return (inode->i_nlink == 0) ? -ENOENT : 0;
return (inode->i_nlink == 0) ? -ESTALE : 0;
out_force:
if (flags & LOOKUP_RCU)
return -ECHILD;
Expand Down Expand Up @@ -2499,7 +2505,9 @@ static int nfs_execute_ok(struct inode *inode, int mask)
struct nfs_server *server = NFS_SERVER(inode);
int ret = 0;

if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS)) {
if (S_ISDIR(inode->i_mode))
return 0;
if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_OTHER)) {
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
ret = __nfs_revalidate_inode(server, inode);
Expand Down
Loading

0 comments on commit 53a01c9

Please sign in to comment.