NFS client bugfixes for Linux 4.7

Stable bugfixes:
 - Fix _cancel_empty_pagelist
 - Fix a double page unlock
 - Make nfs_atomic_open() call d_drop() on all ->open_context() errors.
 - Fix another OPEN_DOWNGRADE bug
 
 Other bugfixes:
 - Ensure we handle delegation errors in nfs4_proc_layoutget()
 - Layout stateids start out as being invalid
 - Add sparse lock annotations for pnfs_find_alloc_layout
 - Handle bad delegation stateids in nfs4_layoutget_handle_exception
 - Fix up O_DIRECT results
 - Fix potential use after free of state in nfs4_do_reclaim.
 - Mark the layout stateid invalid when all segments are removed
 - Don't let readdirplus revalidate an inode that was marked as stale
 - Fix potential race in nfs_fhget()
 - Fix an unused variable warning
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJXdDWkAAoJENfLVL+wpUDrpsUP/1F2zu12r/Zkv3ZEFhShcpQc
 2N1TRD9X7Lruod2pUD95qqjjdw+/vu3LjcyJljrasRaJENijvZ2GQhKkB7xPODlu
 qxZcmnQsH+WmpmJKcqAByAW1czcNGMoMHnt4tV0gG21NH+XUb92fgn+aGeIJDVrK
 Hcd9d8TfnFWO70ZgTUXW/hv0CXwu4MEJhN2JfF4lolbxUkmjLHHLoxSDDm0AdXGC
 EE8f0V9/7xurvOeLe5bQOQXfZPedBydsLNXa1ZacMGKgmBUoRNxJ5yCpPUtcTVBx
 HwbiY+WDFQ7MdKTzUQqqbnrIqKw8Hu4SugIV/vHRqR+Lhc6u29YGOqdU4d2G8IKW
 Nh8MBqS+dDefCkL3TJoE7MpjhP3EOO6HXnv5FjMZLOuu2X2o+Sz3+DkhYCq6pj/g
 fFh480vZfZYaTsfDf1ttvVN8kIvQ+1Uk3LK6aC2EVwgPrv+0OIRu36F0JQQimxOp
 EbYDlhVk7mzH/ZQ31GmPPbSIk+3sm2V58lqXnUMovoqPFPiN3xZDuBcnlyFrrzaI
 NjOvsdVxkOdHWbYZyBQzj16Vo651EYbAAUEwsud70N8C3aCgkTxCZ30Q0v+KqqxU
 pP5kz3zYUdkXQeHxE6T0iXG9fGcv/nGS21hTfT01YJCK7v67K8TYRNMrOEVURVgk
 LSD/CZJXJHVJn1Vr4F7o
 =IGNO
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-4.7-2' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client bugfixes from Anna Schumaker:
 "Stable bugfixes:
   - Fix _cancel_empty_pagelist
   - Fix a double page unlock
   - Make nfs_atomic_open() call d_drop() on all ->open_context() errors.
   - Fix another OPEN_DOWNGRADE bug

  Other bugfixes:
   - Ensure we handle delegation errors in nfs4_proc_layoutget()
   - Layout stateids start out as being invalid
   - Add sparse lock annotations for pnfs_find_alloc_layout
   - Handle bad delegation stateids in nfs4_layoutget_handle_exception
   - Fix up O_DIRECT results
   - Fix potential use after free of state in nfs4_do_reclaim.
   - Mark the layout stateid invalid when all segments are removed
   - Don't let readdirplus revalidate an inode that was marked as stale
   - Fix potential race in nfs_fhget()
   - Fix an unused variable warning"

* tag 'nfs-for-4.7-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFS: Fix another OPEN_DOWNGRADE bug
  make nfs_atomic_open() call d_drop() on all ->open_context() errors.
  NFS: Fix an unused variable warning
  NFS: Fix potential race in nfs_fhget()
  NFS: Don't let readdirplus revalidate an inode that was marked as stale
  NFSv4.1/pnfs: Mark the layout stateid invalid when all segments are removed
  NFS: Fix a double page unlock
  pnfs_nfs: fix _cancel_empty_pagelist
  nfs4: Fix potential use after free of state in nfs4_do_reclaim.
  NFS: Fix up O_DIRECT results
  NFS/pnfs: handle bad delegation stateids in nfs4_layoutget_handle_exception
  NFSv4.1/pnfs: Add sparse lock annotations for pnfs_find_alloc_layout
  NFSv4.1/pnfs: Layout stateids start out as being invalid
  NFSv4.1/pnfs: Ensure we handle delegation errors in nfs4_proc_layoutget()
This commit is contained in:
Linus Torvalds 2016-06-29 15:30:26 -07:00
commit e7bdea7750
8 changed files with 45 additions and 23 deletions

View File

@ -424,12 +424,17 @@ static int xdr_decode(nfs_readdir_descriptor_t *desc,
static static
int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry) int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
{ {
struct inode *inode;
struct nfs_inode *nfsi; struct nfs_inode *nfsi;
if (d_really_is_negative(dentry)) if (d_really_is_negative(dentry))
return 0; return 0;
nfsi = NFS_I(d_inode(dentry)); inode = d_inode(dentry);
if (is_bad_inode(inode) || NFS_STALE(inode))
return 0;
nfsi = NFS_I(inode);
if (entry->fattr->fileid == nfsi->fileid) if (entry->fattr->fileid == nfsi->fileid)
return 1; return 1;
if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0) if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
@ -1363,7 +1368,6 @@ EXPORT_SYMBOL_GPL(nfs_dentry_operations);
struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags) struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
{ {
struct dentry *res; struct dentry *res;
struct dentry *parent;
struct inode *inode = NULL; struct inode *inode = NULL;
struct nfs_fh *fhandle = NULL; struct nfs_fh *fhandle = NULL;
struct nfs_fattr *fattr = NULL; struct nfs_fattr *fattr = NULL;
@ -1393,7 +1397,6 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
if (IS_ERR(label)) if (IS_ERR(label))
goto out; goto out;
parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */ /* Protect against concurrent sillydeletes */
trace_nfs_lookup_enter(dir, dentry, flags); trace_nfs_lookup_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
@ -1536,9 +1539,9 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
err = PTR_ERR(inode); err = PTR_ERR(inode);
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err); trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
d_drop(dentry);
switch (err) { switch (err) {
case -ENOENT: case -ENOENT:
d_drop(dentry);
d_add(dentry, NULL); d_add(dentry, NULL);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
break; break;

View File

@ -353,10 +353,12 @@ static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
result = wait_for_completion_killable(&dreq->completion); result = wait_for_completion_killable(&dreq->completion);
if (!result) {
result = dreq->count;
WARN_ON_ONCE(dreq->count < 0);
}
if (!result) if (!result)
result = dreq->error; result = dreq->error;
if (!result)
result = dreq->count;
out: out:
return (ssize_t) result; return (ssize_t) result;
@ -386,8 +388,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq, bool write)
if (dreq->iocb) { if (dreq->iocb) {
long res = (long) dreq->error; long res = (long) dreq->error;
if (!res) if (dreq->count != 0) {
res = (long) dreq->count; res = (long) dreq->count;
WARN_ON_ONCE(dreq->count < 0);
}
dreq->iocb->ki_complete(dreq->iocb, res, 0); dreq->iocb->ki_complete(dreq->iocb, res, 0);
} }

View File

@ -282,6 +282,7 @@ nfs_init_locked(struct inode *inode, void *opaque)
struct nfs_fattr *fattr = desc->fattr; struct nfs_fattr *fattr = desc->fattr;
set_nfs_fileid(inode, fattr->fileid); set_nfs_fileid(inode, fattr->fileid);
inode->i_mode = fattr->mode;
nfs_copy_fh(NFS_FH(inode), desc->fh); nfs_copy_fh(NFS_FH(inode), desc->fh);
return 0; return 0;
} }

View File

@ -2882,12 +2882,11 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
call_close |= is_wronly; call_close |= is_wronly;
else if (is_wronly) else if (is_wronly)
calldata->arg.fmode |= FMODE_WRITE; calldata->arg.fmode |= FMODE_WRITE;
if (calldata->arg.fmode != (FMODE_READ|FMODE_WRITE))
call_close |= is_rdwr;
} else if (is_rdwr) } else if (is_rdwr)
calldata->arg.fmode |= FMODE_READ|FMODE_WRITE; calldata->arg.fmode |= FMODE_READ|FMODE_WRITE;
if (calldata->arg.fmode == 0)
call_close |= is_rdwr;
if (!nfs4_valid_open_stateid(state)) if (!nfs4_valid_open_stateid(state))
call_close = 0; call_close = 0;
spin_unlock(&state->owner->so_lock); spin_unlock(&state->owner->so_lock);
@ -7924,8 +7923,8 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
break; break;
} }
lo = NFS_I(inode)->layout; lo = NFS_I(inode)->layout;
if (lo && nfs4_stateid_match(&lgp->args.stateid, if (lo && !test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags) &&
&lo->plh_stateid)) { nfs4_stateid_match_other(&lgp->args.stateid, &lo->plh_stateid)) {
LIST_HEAD(head); LIST_HEAD(head);
/* /*
@ -7936,10 +7935,10 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0); pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&head); pnfs_free_lseg_list(&head);
status = -EAGAIN;
goto out;
} else } else
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
status = -EAGAIN;
goto out;
} }
status = nfs4_handle_exception(server, status, exception); status = nfs4_handle_exception(server, status, exception);
@ -8036,7 +8035,10 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout, gfp_t gfp_flags)
.flags = RPC_TASK_ASYNC, .flags = RPC_TASK_ASYNC,
}; };
struct pnfs_layout_segment *lseg = NULL; struct pnfs_layout_segment *lseg = NULL;
struct nfs4_exception exception = { .timeout = *timeout }; struct nfs4_exception exception = {
.inode = inode,
.timeout = *timeout,
};
int status = 0; int status = 0;
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);

View File

@ -1488,9 +1488,9 @@ restart:
} }
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
} }
nfs4_put_open_state(state);
clear_bit(NFS_STATE_RECLAIM_NOGRACE, clear_bit(NFS_STATE_RECLAIM_NOGRACE,
&state->flags); &state->flags);
nfs4_put_open_state(state);
spin_lock(&sp->so_lock); spin_lock(&sp->so_lock);
goto restart; goto restart;
} }

View File

@ -361,8 +361,10 @@ pnfs_layout_remove_lseg(struct pnfs_layout_hdr *lo,
list_del_init(&lseg->pls_list); list_del_init(&lseg->pls_list);
/* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */ /* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */
atomic_dec(&lo->plh_refcount); atomic_dec(&lo->plh_refcount);
if (list_empty(&lo->plh_segs)) if (list_empty(&lo->plh_segs)) {
set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags); clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
}
rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq); rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq);
} }
@ -1290,6 +1292,7 @@ alloc_init_layout_hdr(struct inode *ino,
INIT_LIST_HEAD(&lo->plh_bulk_destroy); INIT_LIST_HEAD(&lo->plh_bulk_destroy);
lo->plh_inode = ino; lo->plh_inode = ino;
lo->plh_lc_cred = get_rpccred(ctx->cred); lo->plh_lc_cred = get_rpccred(ctx->cred);
lo->plh_flags |= 1 << NFS_LAYOUT_INVALID_STID;
return lo; return lo;
} }
@ -1297,6 +1300,8 @@ static struct pnfs_layout_hdr *
pnfs_find_alloc_layout(struct inode *ino, pnfs_find_alloc_layout(struct inode *ino,
struct nfs_open_context *ctx, struct nfs_open_context *ctx,
gfp_t gfp_flags) gfp_t gfp_flags)
__releases(&ino->i_lock)
__acquires(&ino->i_lock)
{ {
struct nfs_inode *nfsi = NFS_I(ino); struct nfs_inode *nfsi = NFS_I(ino);
struct pnfs_layout_hdr *new = NULL; struct pnfs_layout_hdr *new = NULL;
@ -1565,8 +1570,7 @@ lookup_again:
* stateid, or it has been invalidated, then we must use the open * stateid, or it has been invalidated, then we must use the open
* stateid. * stateid.
*/ */
if (lo->plh_stateid.seqid == 0 || if (test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) {
test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) {
/* /*
* The first layoutget for the file. Need to serialize per * The first layoutget for the file. Need to serialize per

View File

@ -247,7 +247,11 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages,
} }
/* Helper function for pnfs_generic_commit_pagelist to catch an empty /* Helper function for pnfs_generic_commit_pagelist to catch an empty
* page list. This can happen when two commits race. */ * page list. This can happen when two commits race.
*
* This must be called instead of nfs_init_commit - call one or the other, but
* not both!
*/
static bool static bool
pnfs_generic_commit_cancel_empty_pagelist(struct list_head *pages, pnfs_generic_commit_cancel_empty_pagelist(struct list_head *pages,
struct nfs_commit_data *data, struct nfs_commit_data *data,
@ -256,7 +260,11 @@ pnfs_generic_commit_cancel_empty_pagelist(struct list_head *pages,
if (list_empty(pages)) { if (list_empty(pages)) {
if (atomic_dec_and_test(&cinfo->mds->rpcs_out)) if (atomic_dec_and_test(&cinfo->mds->rpcs_out))
wake_up_atomic_t(&cinfo->mds->rpcs_out); wake_up_atomic_t(&cinfo->mds->rpcs_out);
nfs_commitdata_release(data); /* don't call nfs_commitdata_release - it tries to put
* the open_context which is not acquired until nfs_init_commit
* which has not been called on @data */
WARN_ON_ONCE(data->context);
nfs_commit_free(data);
return true; return true;
} }

View File

@ -367,13 +367,13 @@ readpage_async_filler(void *data, struct page *page)
nfs_list_remove_request(new); nfs_list_remove_request(new);
nfs_readpage_release(new); nfs_readpage_release(new);
error = desc->pgio->pg_error; error = desc->pgio->pg_error;
goto out_unlock; goto out;
} }
return 0; return 0;
out_error: out_error:
error = PTR_ERR(new); error = PTR_ERR(new);
out_unlock:
unlock_page(page); unlock_page(page);
out:
return error; return error;
} }