NFS client bugfixes for 4.13

Stable bugfixes:
 - 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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAllyVYYACgkQ18tUv7Cl
 QOtTwBAA8ek0Ba5wIfwlQe4MvIX1t6v7q7Otrwdombhuw1a6nW410hFwu2SG8kGd
 fQWr1xnqRsMKwu83UuImtlYYvMa271TZgXxPNWx7KJpi/zocnigJRg5sVpkcRqza
 AjjPc245pHCooQoaTAlm5WrO9zDm0s7lRGuTB1cvPRsElnFVWT0jwhTASIDOU1Zy
 9K/hElAnXp/dZv/ydjSePTPPsVQPJWLbJPlSm6vAIQPyeXWUeCgAym0yf2FVvtsd
 AQeozaq9xq6tofVPZhsYWeBKswjTHs3FxL8vhDOF9gF3QMPm43mwsfrKVidWo4vW
 0UJnRZRCFgG0WIxxhA7l1Z9MovAsXlbWmFufgCa4Ev/bC5WuUT4ZEkjBGJw2vXD4
 0/lxkhD41PBhCl/LIod9OT6iJ8koifl50JUC4N67D2illFy9a7Btzx3EPxfDz9uG
 6jEek9x6B1xf7AC4HhxByN/E8gKX08N4Q/afxTFuAwrzKRKqI4Me3qbCyU86bp+T
 wiAxgVPVnmb/VBVLU68i7titdsA6U8ZO12FFqu9QOr9wHMXxa6108h2Nia9jVTdk
 EZhanXa6tJThQQ/QZicuR5hTtoM4BuikaaJSHZ4ODbgrAjsMAyqy4qsf4tf3LLAo
 tEDu5sJDmHJhhdtqzuz+OtDn5iJ2Nga6a4/fMwt9YU2ZQBm7X/8=
 =ZcQv
 -----END PGP SIGNATURE-----

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 722f0b8911 ("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
This commit is contained in:
Linus Torvalds 2017-07-21 16:26:01 -07:00
commit 505d5c1119
9 changed files with 72 additions and 34 deletions

View File

@ -820,6 +820,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour
target->caps = source->caps;
target->options = source->options;
target->auth_info = source->auth_info;
target->port = source->port;
}
EXPORT_SYMBOL_GPL(nfs_server_copy_userdata);

View File

@ -2372,16 +2372,40 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
}
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)
{
entry->mask = 0;
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;
entry->mask = access_result;
}
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)
{
struct nfs_access_entry cache;
bool may_block = (mask & MAY_NOT_BLOCK) == 0;
int cache_mask;
int status;
trace_nfs_access_enter(inode);
@ -2404,7 +2429,8 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
goto out;
/* 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.jiffies = jiffies;
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)
}
nfs_access_add_cache(inode, &cache);
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;
out:
trace_nfs_access_exit(inode, status);

View File

@ -542,6 +542,10 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
struct nfs4_file_layout_dsaddr *dsaddr;
int status = -EINVAL;
/* Is the deviceid already set? If so, we're good. */
if (fl->dsaddr != NULL)
return 0;
/* find and reference the deviceid */
d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode), &fl->deviceid,
lo->plh_lc_cred, gfp_flags);
@ -553,8 +557,6 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
if (filelayout_test_devid_unavailable(&dsaddr->id_node))
goto out_put;
fl->dsaddr = dsaddr;
if (fl->first_stripe_index >= dsaddr->stripe_count) {
dprintk("%s Bad first_stripe_index %u\n",
__func__, fl->first_stripe_index);
@ -570,6 +572,13 @@ filelayout_check_deviceid(struct pnfs_layout_hdr *lo,
goto out_put;
}
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:
return status;
out_put:

View File

@ -1842,6 +1842,10 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
int vers, ret;
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);
ds = nfs4_ff_layout_prepare_ds(lseg, idx, true);
if (!ds)

View File

@ -512,7 +512,7 @@ static const struct rpc_version mnt_version1 = {
.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 = {
.number = 3,
.nrprocs = ARRAY_SIZE(mnt3_procedures),

View File

@ -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);
nfs_refresh_inode(inode, res.fattr);
if (status == 0) {
entry->mask = 0;
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;
}
if (status == 0)
nfs_access_set_mask(entry, res.access);
nfs_free_fattr(res.fattr);
out:
dprintk("NFS reply access: %d\n", status);

View File

@ -159,13 +159,18 @@ void pnfs_generic_recover_commit_reqs(struct list_head *dst,
{
struct pnfs_commit_bucket *b;
struct pnfs_layout_segment *freeme;
int nwritten;
int i;
lockdep_assert_held(&cinfo->inode->i_lock);
restart:
for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
if (pnfs_generic_transfer_commit_list(&b->written, dst,
cinfo, 0)) {
nwritten = pnfs_generic_transfer_commit_list(&b->written,
dst, cinfo, 0);
if (!nwritten)
continue;
cinfo->ds->nwritten -= nwritten;
if (list_empty(&b->written)) {
freeme = b->wlseg;
b->wlseg = NULL;
spin_unlock(&cinfo->inode->i_lock);
@ -174,7 +179,6 @@ restart:
goto restart;
}
}
cinfo->ds->nwritten = 0;
}
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)
struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
struct pnfs_commit_bucket *bucket;
struct pnfs_layout_segment *freeme;
struct list_head *pos;
LIST_HEAD(pages);
int i;
@ -193,6 +198,8 @@ static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
continue;
freeme = bucket->clseg;
bucket->clseg = NULL;
list_for_each(pos, &bucket->committing)
cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, &pages);
spin_unlock(&cinfo->inode->i_lock);
nfs_retry_commit(&pages, freeme, cinfo, i);
@ -217,13 +224,6 @@ pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
if (list_empty(&bucket->committing))
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);
if (!data)
break;
@ -243,9 +243,12 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages,
struct nfs_commit_info *cinfo)
{
struct pnfs_commit_bucket *bucket;
struct list_head *pos;
bucket = &cinfo->ds->buckets[data->ds_commit_index];
spin_lock(&cinfo->inode->i_lock);
list_for_each(pos, &bucket->committing)
cinfo->ds->ncommitting--;
list_splice_init(&bucket->committing, pages);
data->lseg = bucket->clseg;
bucket->clseg = NULL;
@ -330,7 +333,6 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
}
}
out:
cinfo->ds->ncommitting = 0;
return PNFS_ATTEMPTED;
}
EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);

View File

@ -51,7 +51,7 @@ struct nfs_access_entry {
struct list_head lru;
unsigned long jiffies;
struct rpc_cred * cred;
int mask;
__u32 mask;
struct rcu_head rcu_head;
};

View File

@ -1624,6 +1624,8 @@ static void xs_tcp_state_change(struct sock *sk)
if (test_and_clear_bit(XPRT_SOCK_CONNECTING,
&transport->sock_state))
xprt_clear_connecting(xprt);
if (sk->sk_err)
xprt_wake_pending_tasks(xprt, -sk->sk_err);
xs_sock_mark_closed(xprt);
}
out: