Commit 505d5c11 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull NFS client bugfixes from Anna Schumaker:
 "Stable bugfix:
   - Fix error reporting regression

  Bugfixes:
   - Fix setting filelayout ds address race
   - Fix subtle access bug when using ACLs
   - Fix setting mnt3_counts array size
   - Fix a couple of pNFS commit races"

* tag 'nfs-for-4.13-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFS/filelayout: Fix racy setting of fl->dsaddr in filelayout_check_deviceid()
  NFS: Be more careful about mapping file permissions
  NFS: Store the raw NFS access mask in the inode's access cache
  NFSv3: Convert nfs3_proc_access() to use nfs_access_set_mask()
  NFS: Refactor NFS access to kernel access mask calculation
  net/sunrpc/xprt_sock: fix regression in connection error reporting.
  nfs: count correct array for mnt3_counts array size
  Revert commit 722f0b89 ("pNFS: Don't send COMMITs to the DSes if...")
  pNFS/flexfiles: Handle expired layout segments in ff_layout_initiate_commit()
  NFS: Fix another COMMIT race in pNFS
  NFS: Fix a COMMIT race in pNFS
  mount: copy the port field into the cloned nfs_server structure.
  NFS: Don't run wake_up_bit() when nobody is waiting...
  nfs: add export operations
parents 99313414 1ebf9801
...@@ -820,6 +820,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour ...@@ -820,6 +820,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour
target->caps = source->caps; target->caps = source->caps;
target->options = source->options; target->options = source->options;
target->auth_info = source->auth_info; target->auth_info = source->auth_info;
target->port = source->port;
} }
EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); EXPORT_SYMBOL_GPL(nfs_server_copy_userdata);
......
...@@ -2372,16 +2372,40 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) ...@@ -2372,16 +2372,40 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
} }
EXPORT_SYMBOL_GPL(nfs_access_add_cache); EXPORT_SYMBOL_GPL(nfs_access_add_cache);
#define NFS_MAY_READ (NFS4_ACCESS_READ)
#define NFS_MAY_WRITE (NFS4_ACCESS_MODIFY | \
NFS4_ACCESS_EXTEND | \
NFS4_ACCESS_DELETE)
#define NFS_FILE_MAY_WRITE (NFS4_ACCESS_MODIFY | \
NFS4_ACCESS_EXTEND)
#define NFS_DIR_MAY_WRITE NFS_MAY_WRITE
#define NFS_MAY_LOOKUP (NFS4_ACCESS_LOOKUP)
#define NFS_MAY_EXECUTE (NFS4_ACCESS_EXECUTE)
static int
nfs_access_calc_mask(u32 access_result, umode_t umode)
{
int mask = 0;
if (access_result & NFS_MAY_READ)
mask |= MAY_READ;
if (S_ISDIR(umode)) {
if ((access_result & NFS_DIR_MAY_WRITE) == NFS_DIR_MAY_WRITE)
mask |= MAY_WRITE;
if ((access_result & NFS_MAY_LOOKUP) == NFS_MAY_LOOKUP)
mask |= MAY_EXEC;
} else if (S_ISREG(umode)) {
if ((access_result & NFS_FILE_MAY_WRITE) == NFS_FILE_MAY_WRITE)
mask |= MAY_WRITE;
if ((access_result & NFS_MAY_EXECUTE) == NFS_MAY_EXECUTE)
mask |= MAY_EXEC;
} else if (access_result & NFS_MAY_WRITE)
mask |= MAY_WRITE;
return mask;
}
void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result) void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
{ {
entry->mask = 0; entry->mask = access_result;
if (access_result & NFS4_ACCESS_READ)
entry->mask |= MAY_READ;
if (access_result &
(NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (access_result & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
} }
EXPORT_SYMBOL_GPL(nfs_access_set_mask); EXPORT_SYMBOL_GPL(nfs_access_set_mask);
...@@ -2389,6 +2413,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) ...@@ -2389,6 +2413,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{ {
struct nfs_access_entry cache; struct nfs_access_entry cache;
bool may_block = (mask & MAY_NOT_BLOCK) == 0; bool may_block = (mask & MAY_NOT_BLOCK) == 0;
int cache_mask;
int status; int status;
trace_nfs_access_enter(inode); trace_nfs_access_enter(inode);
...@@ -2404,7 +2429,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) ...@@ -2404,7 +2429,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
goto out; goto out;
/* Be clever: ask server to check for all possible rights */ /* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ; cache.mask = NFS_MAY_LOOKUP | NFS_MAY_EXECUTE
| NFS_MAY_WRITE | NFS_MAY_READ;
cache.cred = cred; cache.cred = cred;
cache.jiffies = jiffies; cache.jiffies = jiffies;
status = NFS_PROTO(inode)->access(inode, &cache); status = NFS_PROTO(inode)->access(inode, &cache);
...@@ -2418,7 +2444,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) ...@@ -2418,7 +2444,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
} }
nfs_access_add_cache(inode, &cache); nfs_access_add_cache(inode, &cache);
out_cached: out_cached:
if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0) cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
status = -EACCES; status = -EACCES;
out: out:
trace_nfs_access_exit(inode, status); trace_nfs_access_exit(inode, status);
......
...@@ -542,6 +542,10 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo, ...@@ -542,6 +542,10 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
struct nfs4_file_layout_dsaddr *dsaddr; struct nfs4_file_layout_dsaddr *dsaddr;
int status = -EINVAL; int status = -EINVAL;
/* Is the deviceid already set? If so, we're good. */
if (fl->dsaddr != NULL)
return 0;
/* find and reference the deviceid */ /* find and reference the deviceid */
d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &fl->deviceid, d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &fl->deviceid,
lo->plh_lc_cred, gfp_flags); lo->plh_lc_cred, gfp_flags);
...@@ -553,8 +557,6 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo, ...@@ -553,8 +557,6 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
if (filelayout_test_devid_unavailable(&dsaddr->id_node)) if (filelayout_test_devid_unavailable(&dsaddr->id_node))
goto out_put; goto out_put;
fl->dsaddr = dsaddr;
if (fl->first_stripe_index >= dsaddr->stripe_count) { if (fl->first_stripe_index >= dsaddr->stripe_count) {
dprintk("%s Bad first_stripe_index %u\n", dprintk("%s Bad first_stripe_index %u\n",
__func__, fl->first_stripe_index); __func__, fl->first_stripe_index);
...@@ -570,6 +572,13 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo, ...@@ -570,6 +572,13 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
goto out_put; goto out_put;
} }
status = 0; status = 0;
/*
* Atomic compare and xchange to ensure we don't scribble
* over a non-NULL pointer.
*/
if (cmpxchg(&fl->dsaddr, NULL, dsaddr) != NULL)
goto out_put;
out: out:
return status; return status;
out_put: out_put:
......
...@@ -1842,6 +1842,10 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how) ...@@ -1842,6 +1842,10 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
int vers, ret; int vers, ret;
struct nfs_fh *fh; struct nfs_fh *fh;
if (!lseg || !(pnfs_is_valid_lseg(lseg) ||
test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags)))
goto out_err;
idx = calc_ds_index_from_commit(lseg, data->ds_commit_index); idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
ds = nfs4_ff_layout_prepare_ds(lseg, idx, true); ds = nfs4_ff_layout_prepare_ds(lseg, idx, true);
if (!ds) if (!ds)
......
...@@ -512,7 +512,7 @@ static const struct rpc_version mnt_version1 = { ...@@ -512,7 +512,7 @@ static const struct rpc_version mnt_version1 = {
.counts = mnt_counts, .counts = mnt_counts,
}; };
static unsigned int mnt3_counts[ARRAY_SIZE(mnt_procedures)]; static unsigned int mnt3_counts[ARRAY_SIZE(mnt3_procedures)];
static const struct rpc_version mnt_version3 = { static const struct rpc_version mnt_version3 = {
.number = 3, .number = 3,
.nrprocs = ARRAY_SIZE(mnt3_procedures), .nrprocs = ARRAY_SIZE(mnt3_procedures),
......
...@@ -220,15 +220,8 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry) ...@@ -220,15 +220,8 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, res.fattr); nfs_refresh_inode(inode, res.fattr);
if (status == 0) { if (status == 0)
entry->mask = 0; nfs_access_set_mask(entry, res.access);
if (res.access & NFS3_ACCESS_READ)
entry->mask |= MAY_READ;
if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
nfs_free_fattr(res.fattr); nfs_free_fattr(res.fattr);
out: out:
dprintk("NFS reply access: %d\n", status); dprintk("NFS reply access: %d\n", status);
......
...@@ -159,13 +159,18 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst, ...@@ -159,13 +159,18 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst,
{ {
struct pnfs_commit_bucket *b; struct pnfs_commit_bucket *b;
struct pnfs_layout_segment *freeme; struct pnfs_layout_segment *freeme;
int nwritten;
int i; int i;
lockdep_assert_held(&cinfo->inode->i_lock); lockdep_assert_held(&cinfo->inode->i_lock);
restart: restart:
for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) { for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
if (pnfs_generic_transfer_commit_list(&b->written, dst, nwritten = pnfs_generic_transfer_commit_list(&b->written,
cinfo, 0)) { dst, cinfo, 0);
if (!nwritten)
continue;
cinfo->ds->nwritten -= nwritten;
if (list_empty(&b->written)) {
freeme = b->wlseg; freeme = b->wlseg;
b->wlseg = NULL; b->wlseg = NULL;
spin_unlock(&cinfo->inode->i_lock); spin_unlock(&cinfo->inode->i_lock);
...@@ -174,7 +179,6 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst, ...@@ -174,7 +179,6 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst,
goto restart; goto restart;
} }
} }
cinfo->ds->nwritten = 0;
} }
EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs); EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
...@@ -183,6 +187,7 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx) ...@@ -183,6 +187,7 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds; struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
struct pnfs_commit_bucket *bucket; struct pnfs_commit_bucket *bucket;
struct pnfs_layout_segment *freeme; struct pnfs_layout_segment *freeme;
struct list_head *pos;
LIST_HEAD(pages); LIST_HEAD(pages);
int i; int i;
...@@ -193,6 +198,8 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx) ...@@ -193,6 +198,8 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
continue; continue;
freeme = bucket->clseg; freeme = bucket->clseg;
bucket->clseg = NULL; bucket->clseg = NULL;
list_for_each(pos, &bucket->committing)
cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, &pages); list_splice_init(&bucket->committing, &pages);
spin_unlock(&cinfo->inode->i_lock); spin_unlock(&cinfo->inode->i_lock);
nfs_retry_commit(&pages, freeme, cinfo, i); nfs_retry_commit(&pages, freeme, cinfo, i);
...@@ -217,13 +224,6 @@ pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo, ...@@ -217,13 +224,6 @@ pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) { for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
if (list_empty(&bucket->committing)) if (list_empty(&bucket->committing))
continue; continue;
/*
* If the layout segment is invalid, then let
* pnfs_generic_retry_commit() clean up the bucket.
*/
if (bucket->clseg && !pnfs_is_valid_lseg(bucket->clseg) &&
!test_bit(NFS_LSEG_LAYOUTRETURN, &bucket->clseg->pls_flags))
break;
data = nfs_commitdata_alloc(false); data = nfs_commitdata_alloc(false);
if (!data) if (!data)
break; break;
...@@ -243,9 +243,12 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages, ...@@ -243,9 +243,12 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages,
struct nfs_commit_info *cinfo) struct nfs_commit_info *cinfo)
{ {
struct pnfs_commit_bucket *bucket; struct pnfs_commit_bucket *bucket;
struct list_head *pos;
bucket = &cinfo->ds->buckets[data->ds_commit_index]; bucket = &cinfo->ds->buckets[data->ds_commit_index];
spin_lock(&cinfo->inode->i_lock); spin_lock(&cinfo->inode->i_lock);
list_for_each(pos, &bucket->committing)
cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, pages); list_splice_init(&bucket->committing, pages);
data->lseg = bucket->clseg; data->lseg = bucket->clseg;
bucket->clseg = NULL; bucket->clseg = NULL;
...@@ -330,7 +333,6 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages, ...@@ -330,7 +333,6 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
} }
} }
out: out:
cinfo->ds->ncommitting = 0;
return PNFS_ATTEMPTED; return PNFS_ATTEMPTED;
} }
EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist); EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);
......
...@@ -51,7 +51,7 @@ struct nfs_access_entry { ...@@ -51,7 +51,7 @@ struct nfs_access_entry {
struct list_head lru; struct list_head lru;
unsigned long jiffies; unsigned long jiffies;
struct rpc_cred * cred; struct rpc_cred * cred;
int mask; __u32 mask;
struct rcu_head rcu_head; struct rcu_head rcu_head;
}; };
......
...@@ -1624,6 +1624,8 @@ static void xs_tcp_state_change(struct sock *sk) ...@@ -1624,6 +1624,8 @@ static void xs_tcp_state_change(struct sock *sk)
if (test_and_clear_bit(XPRT_SOCK_CONNECTING, if (test_and_clear_bit(XPRT_SOCK_CONNECTING,
&transport->sock_state)) &transport->sock_state))
xprt_clear_connecting(xprt); xprt_clear_connecting(xprt);
if (sk->sk_err)
xprt_wake_pending_tasks(xprt, -sk->sk_err);
xs_sock_mark_closed(xprt); xs_sock_mark_closed(xprt);
} }
out: out:
......
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