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:
commit
e7bdea7750
11
fs/nfs/dir.c
11
fs/nfs/dir.c
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
} else
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
status = -EAGAIN;
|
status = -EAGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
|
} else
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
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__);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user