diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 15:39:07 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 15:39:07 -0800 |
commit | 8a1f006ad302ea178aefb1f8c67e679c696289e9 (patch) | |
tree | 8ba312b39070556d2062a9f90b1a7fa394041d78 /fs/nfs | |
parent | 14864a52cd8189e8567df8351d9fc7e435133abd (diff) | |
parent | a1800acaf7d1c2bf6d68b9a8f4ab8560cc66555a (diff) |
Merge tag 'nfs-for-3.14-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
Pull NFS client bugfixes from Trond Myklebust:
"Highlights:
- Fix several races in nfs_revalidate_mapping
- NFSv4.1 slot leakage in the pNFS files driver
- Stable fix for a slot leak in nfs40_sequence_done
- Don't reject NFSv4 servers that support ACLs with only ALLOW aces"
* tag 'nfs-for-3.14-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
nfs: initialize the ACL support bits to zero.
NFSv4.1: Cleanup
NFSv4.1: Clean up nfs41_sequence_done
NFSv4: Fix a slot leak in nfs40_sequence_done
NFSv4.1 free slot before resending I/O to MDS
nfs: add memory barriers around NFS_INO_INVALID_DATA and NFS_INO_INVALIDATING
NFS: Fix races in nfs_revalidate_mapping
sunrpc: turn warn_gssd() log message into a dprintk()
NFS: fix the handling of NFS_INO_INVALID_DATA flag in nfs_revalidate_mapping
nfs: handle servers that support only ALLOW ACE type.
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 13 | ||||
-rw-r--r-- | fs/nfs/inode.c | 51 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4client.c | 5 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 8 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 31 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 2 | ||||
-rw-r--r-- | fs/nfs/nfstrace.h | 1 | ||||
-rw-r--r-- | fs/nfs/write.c | 7 |
9 files changed, 84 insertions, 35 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index b266f734bd5..be38b573495 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -274,6 +274,15 @@ out_eof: return -EBADCOOKIE; } +static bool +nfs_readdir_inode_mapping_valid(struct nfs_inode *nfsi) +{ + if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) + return false; + smp_rmb(); + return !test_bit(NFS_INO_INVALIDATING, &nfsi->flags); +} + static int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) { @@ -287,8 +296,8 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des struct nfs_open_dir_context *ctx = desc->file->private_data; new_pos = desc->current_index + i; - if (ctx->attr_gencount != nfsi->attr_gencount - || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { + if (ctx->attr_gencount != nfsi->attr_gencount || + !nfs_readdir_inode_mapping_valid(nfsi)) { ctx->duped = 0; ctx->attr_gencount = nfsi->attr_gencount; } else if (new_pos < desc->ctx->pos) { diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ea00b34ff07..28a0a3cbd3b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -977,11 +977,11 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map if (ret < 0) return ret; } - spin_lock(&inode->i_lock); - nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) { + spin_lock(&inode->i_lock); memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); - spin_unlock(&inode->i_lock); + spin_unlock(&inode->i_lock); + } nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); nfs_fscache_wait_on_invalidate(inode); @@ -1008,6 +1008,7 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode) int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) { struct nfs_inode *nfsi = NFS_I(inode); + unsigned long *bitlock = &nfsi->flags; int ret = 0; /* swapfiles are not supposed to be shared. */ @@ -1019,12 +1020,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) if (ret < 0) goto out; } - if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { - trace_nfs_invalidate_mapping_enter(inode); - ret = nfs_invalidate_mapping(inode, mapping); - trace_nfs_invalidate_mapping_exit(inode, ret); + + /* + * We must clear NFS_INO_INVALID_DATA first to ensure that + * invalidations that come in while we're shooting down the mappings + * are respected. But, that leaves a race window where one revalidator + * can clear the flag, and then another checks it before the mapping + * gets invalidated. Fix that by serializing access to this part of + * the function. + * + * At the same time, we need to allow other tasks to see whether we + * might be in the middle of invalidating the pages, so we only set + * the bit lock here if it looks like we're going to be doing that. + */ + for (;;) { + ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING, + nfs_wait_bit_killable, TASK_KILLABLE); + if (ret) + goto out; + spin_lock(&inode->i_lock); + if (test_bit(NFS_INO_INVALIDATING, bitlock)) { + spin_unlock(&inode->i_lock); + continue; + } + if (nfsi->cache_validity & NFS_INO_INVALID_DATA) + break; + spin_unlock(&inode->i_lock); + goto out; } + set_bit(NFS_INO_INVALIDATING, bitlock); + smp_wmb(); + nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; + spin_unlock(&inode->i_lock); + trace_nfs_invalidate_mapping_enter(inode); + ret = nfs_invalidate_mapping(inode, mapping); + trace_nfs_invalidate_mapping_exit(inode, ret); + + clear_bit_unlock(NFS_INO_INVALIDATING, bitlock); + smp_mb__after_clear_bit(); + wake_up_bit(bitlock, NFS_INO_INVALIDATING); out: return ret; } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5609edc742a..a5b27c2d968 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -270,6 +270,7 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser extern int nfs41_setup_sequence(struct nfs4_session *session, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct rpc_task *task); +extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); extern int nfs4_proc_create_session(struct nfs_client *, struct rpc_cred *); extern int nfs4_proc_destroy_session(struct nfs4_session *, struct rpc_cred *); extern int nfs4_proc_get_lease_time(struct nfs_client *clp, diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 73d4ecda1e3..dbb3e1f30c6 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -372,10 +372,7 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); - error = -EINVAL; - if (gssd_running(clp->cl_net)) - error = nfs_create_rpc_client(clp, timeparms, - RPC_AUTH_GSS_KRB5I); + error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I); if (error == -EINVAL) error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); if (error < 0) diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 03fd8be8c0c..12c8132ad40 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -335,8 +335,10 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data) dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) && - task->tk_status == 0) + task->tk_status == 0) { + nfs41_sequence_done(task, &rdata->res.seq_res); return; + } /* Note this may cause RPC to be resent */ rdata->header->mds_ops->rpc_call_done(task, data); @@ -442,8 +444,10 @@ static void filelayout_write_call_done(struct rpc_task *task, void *data) struct nfs_write_data *wdata = data; if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) && - task->tk_status == 0) + task->tk_status == 0) { + nfs41_sequence_done(task, &wdata->res.seq_res); return; + } /* Note this may cause RPC to be resent */ wdata->header->mds_ops->rpc_call_done(task, data); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a1965329a12..42da6af7758 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -539,7 +539,7 @@ static int nfs40_sequence_done(struct rpc_task *task, struct nfs4_slot *slot = res->sr_slot; struct nfs4_slot_table *tbl; - if (!RPC_WAS_SENT(task)) + if (slot == NULL) goto out; tbl = slot->table; @@ -559,15 +559,10 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { struct nfs4_session *session; struct nfs4_slot_table *tbl; + struct nfs4_slot *slot = res->sr_slot; bool send_new_highest_used_slotid = false; - if (!res->sr_slot) { - /* just wake up the next guy waiting since - * we may have not consumed a slot after all */ - dprintk("%s: No slot\n", __func__); - return; - } - tbl = res->sr_slot->table; + tbl = slot->table; session = tbl->session; spin_lock(&tbl->slot_tbl_lock); @@ -577,11 +572,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) if (tbl->highest_used_slotid > tbl->target_highest_slotid) send_new_highest_used_slotid = true; - if (nfs41_wake_and_assign_slot(tbl, res->sr_slot)) { + if (nfs41_wake_and_assign_slot(tbl, slot)) { send_new_highest_used_slotid = false; goto out_unlock; } - nfs4_free_slot(tbl, res->sr_slot); + nfs4_free_slot(tbl, slot); if (tbl->highest_used_slotid != NFS4_NO_SLOT) send_new_highest_used_slotid = false; @@ -592,19 +587,20 @@ out_unlock: nfs41_server_notify_highest_slotid_update(session->clp); } -static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) +int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { struct nfs4_session *session; - struct nfs4_slot *slot; + struct nfs4_slot *slot = res->sr_slot; struct nfs_client *clp; bool interrupted = false; int ret = 1; + if (slot == NULL) + goto out_noaction; /* don't increment the sequence number if the task wasn't sent */ if (!RPC_WAS_SENT(task)) goto out; - slot = res->sr_slot; session = slot->table->session; if (slot->interrupted) { @@ -679,6 +675,7 @@ out: /* The session may be reset by one of the error handlers. */ dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); nfs41_sequence_free_slot(res); +out_noaction: return ret; retry_nowait: if (rpc_restart_call_prepare(task)) { @@ -692,6 +689,7 @@ out_retry: rpc_delay(task, NFS4_POLL_RETRY_MAX); return 0; } +EXPORT_SYMBOL_GPL(nfs41_sequence_done); static int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) @@ -2744,7 +2742,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME| NFS_CAP_CTIME|NFS_CAP_MTIME| NFS_CAP_SECURITY_LABEL); - if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) + if (res.attr_bitmask[0] & FATTR4_WORD0_ACL && + res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) server->caps |= NFS_CAP_ACLS; if (res.has_links != 0) server->caps |= NFS_CAP_HARDLINKS; @@ -4321,9 +4320,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) static inline int nfs4_server_supports_acls(struct nfs_server *server) { - return (server->caps & NFS_CAP_ACLS) - && (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL) - && (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL); + return server->caps & NFS_CAP_ACLS; } /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 8c21d69a9dc..72f3bf1754e 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3449,7 +3449,7 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint { __be32 *p; - *res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL; + *res = 0; if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) return -EIO; if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index 89fe741e58b..59f838cdc00 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -36,6 +36,7 @@ __print_flags(v, "|", \ { 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \ { 1 << NFS_INO_STALE, "STALE" }, \ + { 1 << NFS_INO_INVALIDATING, "INVALIDATING" }, \ { 1 << NFS_INO_FLUSHING, "FLUSHING" }, \ { 1 << NFS_INO_FSCACHE, "FSCACHE" }, \ { 1 << NFS_INO_COMMIT, "COMMIT" }, \ diff --git a/fs/nfs/write.c b/fs/nfs/write.c index a44a87268a6..9a3b6a4cd6b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -909,9 +909,14 @@ bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx) */ static bool nfs_write_pageuptodate(struct page *page, struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + if (nfs_have_delegated_attributes(inode)) goto out; - if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE)) + if (nfsi->cache_validity & (NFS_INO_INVALID_DATA|NFS_INO_REVAL_PAGECACHE)) + return false; + smp_rmb(); + if (test_bit(NFS_INO_INVALIDATING, &nfsi->flags)) return false; out: return PageUptodate(page) != 0; |