Commit 8a1f006a authored by Linus Torvalds's avatar Linus Torvalds

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.
parents 14864a52 a1800aca
...@@ -274,6 +274,15 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri ...@@ -274,6 +274,15 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
return -EBADCOOKIE; 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 static
int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) 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 ...@@ -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; struct nfs_open_dir_context *ctx = desc->file->private_data;
new_pos = desc->current_index + i; new_pos = desc->current_index + i;
if (ctx->attr_gencount != nfsi->attr_gencount if (ctx->attr_gencount != nfsi->attr_gencount ||
|| (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { !nfs_readdir_inode_mapping_valid(nfsi)) {
ctx->duped = 0; ctx->duped = 0;
ctx->attr_gencount = nfsi->attr_gencount; ctx->attr_gencount = nfsi->attr_gencount;
} else if (new_pos < desc->ctx->pos) { } else if (new_pos < desc->ctx->pos) {
......
...@@ -977,11 +977,11 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map ...@@ -977,11 +977,11 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
if (S_ISDIR(inode->i_mode)) {
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
if (S_ISDIR(inode->i_mode))
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
}
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
nfs_fscache_wait_on_invalidate(inode); nfs_fscache_wait_on_invalidate(inode);
...@@ -1008,6 +1008,7 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *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) int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
unsigned long *bitlock = &nfsi->flags;
int ret = 0; int ret = 0;
/* swapfiles are not supposed to be shared. */ /* swapfiles are not supposed to be shared. */
...@@ -1019,12 +1020,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) ...@@ -1019,12 +1020,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
/*
* 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); trace_nfs_invalidate_mapping_enter(inode);
ret = nfs_invalidate_mapping(inode, mapping); ret = nfs_invalidate_mapping(inode, mapping);
trace_nfs_invalidate_mapping_exit(inode, ret); 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: out:
return ret; return ret;
} }
......
...@@ -270,6 +270,7 @@ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *ser ...@@ -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, extern int nfs41_setup_sequence(struct nfs4_session *session,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
struct rpc_task *task); 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_create_session(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_destroy_session(struct nfs4_session *, 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, extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
......
...@@ -372,10 +372,7 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, ...@@ -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_DISCRTRY, &clp->cl_flags);
__set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
error = -EINVAL; error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I);
if (gssd_running(clp->cl_net))
error = nfs_create_rpc_client(clp, timeparms,
RPC_AUTH_GSS_KRB5I);
if (error == -EINVAL) if (error == -EINVAL)
error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX);
if (error < 0) if (error < 0)
......
...@@ -335,8 +335,10 @@ static void filelayout_read_call_done(struct rpc_task *task, void *data) ...@@ -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); dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) && 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; return;
}
/* Note this may cause RPC to be resent */ /* Note this may cause RPC to be resent */
rdata->header->mds_ops->rpc_call_done(task, data); 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) ...@@ -442,8 +444,10 @@ static void filelayout_write_call_done(struct rpc_task *task, void *data)
struct nfs_write_data *wdata = data; struct nfs_write_data *wdata = data;
if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) && 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; return;
}
/* Note this may cause RPC to be resent */ /* Note this may cause RPC to be resent */
wdata->header->mds_ops->rpc_call_done(task, data); wdata->header->mds_ops->rpc_call_done(task, data);
......
...@@ -539,7 +539,7 @@ static int nfs40_sequence_done(struct rpc_task *task, ...@@ -539,7 +539,7 @@ static int nfs40_sequence_done(struct rpc_task *task,
struct nfs4_slot *slot = res->sr_slot; struct nfs4_slot *slot = res->sr_slot;
struct nfs4_slot_table *tbl; struct nfs4_slot_table *tbl;
if (!RPC_WAS_SENT(task)) if (slot == NULL)
goto out; goto out;
tbl = slot->table; tbl = slot->table;
...@@ -559,15 +559,10 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) ...@@ -559,15 +559,10 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
{ {
struct nfs4_session *session; struct nfs4_session *session;
struct nfs4_slot_table *tbl; struct nfs4_slot_table *tbl;
struct nfs4_slot *slot = res->sr_slot;
bool send_new_highest_used_slotid = false; bool send_new_highest_used_slotid = false;
if (!res->sr_slot) { tbl = slot->table;
/* 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;
session = tbl->session; session = tbl->session;
spin_lock(&tbl->slot_tbl_lock); spin_lock(&tbl->slot_tbl_lock);
...@@ -577,11 +572,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) ...@@ -577,11 +572,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
if (tbl->highest_used_slotid > tbl->target_highest_slotid) if (tbl->highest_used_slotid > tbl->target_highest_slotid)
send_new_highest_used_slotid = true; 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; send_new_highest_used_slotid = false;
goto out_unlock; goto out_unlock;
} }
nfs4_free_slot(tbl, res->sr_slot); nfs4_free_slot(tbl, slot);
if (tbl->highest_used_slotid != NFS4_NO_SLOT) if (tbl->highest_used_slotid != NFS4_NO_SLOT)
send_new_highest_used_slotid = false; send_new_highest_used_slotid = false;
...@@ -592,19 +587,20 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) ...@@ -592,19 +587,20 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
nfs41_server_notify_highest_slotid_update(session->clp); 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_session *session;
struct nfs4_slot *slot; struct nfs4_slot *slot = res->sr_slot;
struct nfs_client *clp; struct nfs_client *clp;
bool interrupted = false; bool interrupted = false;
int ret = 1; int ret = 1;
if (slot == NULL)
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))
goto out; goto out;
slot = res->sr_slot;
session = slot->table->session; session = slot->table->session;
if (slot->interrupted) { if (slot->interrupted) {
...@@ -679,6 +675,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * ...@@ -679,6 +675,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
/* The session may be reset by one of the error handlers. */ /* The session may be reset by one of the error handlers. */
dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
nfs41_sequence_free_slot(res); nfs41_sequence_free_slot(res);
out_noaction:
return ret; return ret;
retry_nowait: retry_nowait:
if (rpc_restart_call_prepare(task)) { if (rpc_restart_call_prepare(task)) {
...@@ -692,6 +689,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * ...@@ -692,6 +689,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
rpc_delay(task, NFS4_POLL_RETRY_MAX); rpc_delay(task, NFS4_POLL_RETRY_MAX);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nfs41_sequence_done);
static int nfs4_sequence_done(struct rpc_task *task, static int nfs4_sequence_done(struct rpc_task *task,
struct nfs4_sequence_res *res) struct nfs4_sequence_res *res)
...@@ -2744,7 +2742,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f ...@@ -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_OWNER_GROUP|NFS_CAP_ATIME|
NFS_CAP_CTIME|NFS_CAP_MTIME| NFS_CAP_CTIME|NFS_CAP_MTIME|
NFS_CAP_SECURITY_LABEL); 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; server->caps |= NFS_CAP_ACLS;
if (res.has_links != 0) if (res.has_links != 0)
server->caps |= NFS_CAP_HARDLINKS; server->caps |= NFS_CAP_HARDLINKS;
...@@ -4321,9 +4320,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) ...@@ -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) static inline int nfs4_server_supports_acls(struct nfs_server *server)
{ {
return (server->caps & NFS_CAP_ACLS) return server->caps & NFS_CAP_ACLS;
&& (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
} }
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that /* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
......
...@@ -3449,7 +3449,7 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint ...@@ -3449,7 +3449,7 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint
{ {
__be32 *p; __be32 *p;
*res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL; *res = 0;
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U))) if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
return -EIO; return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) { if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
__print_flags(v, "|", \ __print_flags(v, "|", \
{ 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \ { 1 << NFS_INO_ADVISE_RDPLUS, "ADVISE_RDPLUS" }, \
{ 1 << NFS_INO_STALE, "STALE" }, \ { 1 << NFS_INO_STALE, "STALE" }, \
{ 1 << NFS_INO_INVALIDATING, "INVALIDATING" }, \
{ 1 << NFS_INO_FLUSHING, "FLUSHING" }, \ { 1 << NFS_INO_FLUSHING, "FLUSHING" }, \
{ 1 << NFS_INO_FSCACHE, "FSCACHE" }, \ { 1 << NFS_INO_FSCACHE, "FSCACHE" }, \
{ 1 << NFS_INO_COMMIT, "COMMIT" }, \ { 1 << NFS_INO_COMMIT, "COMMIT" }, \
......
...@@ -909,9 +909,14 @@ bool nfs_ctx_key_to_expire(struct nfs_open_context *ctx) ...@@ -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) static bool nfs_write_pageuptodate(struct page *page, struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
if (nfs_have_delegated_attributes(inode)) if (nfs_have_delegated_attributes(inode))
goto out; 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; return false;
out: out:
return PageUptodate(page) != 0; return PageUptodate(page) != 0;
......
...@@ -211,6 +211,7 @@ struct nfs_inode { ...@@ -211,6 +211,7 @@ struct nfs_inode {
#define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */ #define NFS_INO_ADVISE_RDPLUS (0) /* advise readdirplus */
#define NFS_INO_STALE (1) /* possible stale inode */ #define NFS_INO_STALE (1) /* possible stale inode */
#define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */ #define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */
#define NFS_INO_INVALIDATING (3) /* inode is being invalidated */
#define NFS_INO_FLUSHING (4) /* inode is flushing out data */ #define NFS_INO_FLUSHING (4) /* inode is flushing out data */
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */ #define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */ #define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
......
...@@ -532,13 +532,7 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred) ...@@ -532,13 +532,7 @@ gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)
static void warn_gssd(void) static void warn_gssd(void)
{ {
static unsigned long ratelimit; dprintk("AUTH_GSS upcall failed. Please check user daemon is running.\n");
unsigned long now = jiffies;
if (time_after(now, ratelimit)) {
pr_warn("RPC: AUTH_GSS upcall failed. Please check user daemon is running.\n");
ratelimit = now + 15*HZ;
}
} }
static inline int static inline int
......
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