Commit 829e6944 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull NFS client bugfixes from Anna Schumaker:
 "The only stable fix this time is the DMA scatter-gather list bug fixed
  by Chuck.

  The rest fix up races and refcounting issues that have been found
  during testing.

  Stable fix:
   - fix DMA scatter-gather list mapping imbalance

  The rest:
   - fix directory verifier races
   - fix races between open and dentry revalidation
   - fix revalidation of dentries with delegations
   - fix "cachethis" setting for writes
   - fix delegation and delegation cred pinning"

* tag 'nfs-for-5.6-2' of git://git.linux-nfs.org/projects/anna/linux-nfs:
  NFSv4: Ensure the delegation cred is pinned when we call delegreturn
  NFSv4: Ensure the delegation is pinned in nfs_do_return_delegation()
  NFSv4.1 make cachethis=no for writes
  xprtrdma: Fix DMA scatter-gather list mapping imbalance
  NFSv4: Fix revalidation of dentries with delegations
  NFSv4: Fix races between open and dentry revalidation
  NFS: Fix up directory verifier races
parents cf556edf 5d63944f
...@@ -42,13 +42,27 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) ...@@ -42,13 +42,27 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) { if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
delegation->stateid.type = NFS4_INVALID_STATEID_TYPE; delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
atomic_long_dec(&nfs_active_delegations); atomic_long_dec(&nfs_active_delegations);
if (!test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
nfs_clear_verifier_delegated(delegation->inode);
} }
} }
static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation)
{
refcount_inc(&delegation->refcount);
return delegation;
}
static void nfs_put_delegation(struct nfs_delegation *delegation)
{
if (refcount_dec_and_test(&delegation->refcount))
__nfs_free_delegation(delegation);
}
static void nfs_free_delegation(struct nfs_delegation *delegation) static void nfs_free_delegation(struct nfs_delegation *delegation)
{ {
nfs_mark_delegation_revoked(delegation); nfs_mark_delegation_revoked(delegation);
__nfs_free_delegation(delegation); nfs_put_delegation(delegation);
} }
/** /**
...@@ -241,13 +255,18 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, ...@@ -241,13 +255,18 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
{ {
const struct cred *cred;
int res = 0; int res = 0;
if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
res = nfs4_proc_delegreturn(inode, spin_lock(&delegation->lock);
delegation->cred, cred = get_cred(delegation->cred);
spin_unlock(&delegation->lock);
res = nfs4_proc_delegreturn(inode, cred,
&delegation->stateid, &delegation->stateid,
issync); issync);
put_cred(cred);
}
return res; return res;
} }
...@@ -273,9 +292,13 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi) ...@@ -273,9 +292,13 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
if (delegation == NULL) if (delegation == NULL)
goto out; goto out;
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
ret = delegation; /* Refcount matched in nfs_end_delegation_return() */
ret = nfs_get_delegation(delegation);
}
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
if (ret)
nfs_clear_verifier_delegated(&nfsi->vfs_inode);
out: out:
return ret; return ret;
} }
...@@ -393,6 +416,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, ...@@ -393,6 +416,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
if (delegation == NULL) if (delegation == NULL)
return -ENOMEM; return -ENOMEM;
nfs4_stateid_copy(&delegation->stateid, stateid); nfs4_stateid_copy(&delegation->stateid, stateid);
refcount_set(&delegation->refcount, 1);
delegation->type = type; delegation->type = type;
delegation->pagemod_limit = pagemod_limit; delegation->pagemod_limit = pagemod_limit;
delegation->change_attr = inode_peek_iversion_raw(inode); delegation->change_attr = inode_peek_iversion_raw(inode);
...@@ -492,6 +516,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation ...@@ -492,6 +516,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
err = nfs_do_return_delegation(inode, delegation, issync); err = nfs_do_return_delegation(inode, delegation, issync);
out: out:
/* Refcount matched in nfs_start_delegation_return_locked() */
nfs_put_delegation(delegation);
return err; return err;
} }
...@@ -686,9 +712,12 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) ...@@ -686,9 +712,12 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode)
list_empty(&NFS_I(inode)->open_files) && list_empty(&NFS_I(inode)->open_files) &&
!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
ret = delegation; /* Refcount matched in nfs_end_delegation_return() */
ret = nfs_get_delegation(delegation);
} }
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
if (ret)
nfs_clear_verifier_delegated(inode);
} }
out: out:
rcu_read_unlock(); rcu_read_unlock();
...@@ -1088,10 +1117,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) ...@@ -1088,10 +1117,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
delegation = nfs_start_delegation_return_locked(NFS_I(inode)); delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock(); rcu_read_unlock();
if (delegation != NULL) { if (delegation != NULL) {
delegation = nfs_detach_delegation(NFS_I(inode), if (nfs_detach_delegation(NFS_I(inode), delegation,
delegation, server); server) != NULL)
if (delegation != NULL)
nfs_free_delegation(delegation); nfs_free_delegation(delegation);
/* Match nfs_start_delegation_return_locked */
nfs_put_delegation(delegation);
} }
iput(inode); iput(inode);
nfs_sb_deactive(server->super); nfs_sb_deactive(server->super);
......
...@@ -22,6 +22,7 @@ struct nfs_delegation { ...@@ -22,6 +22,7 @@ struct nfs_delegation {
unsigned long pagemod_limit; unsigned long pagemod_limit;
__u64 change_attr; __u64 change_attr;
unsigned long flags; unsigned long flags;
refcount_t refcount;
spinlock_t lock; spinlock_t lock;
struct rcu_head rcu; struct rcu_head rcu;
}; };
......
...@@ -155,6 +155,7 @@ typedef struct { ...@@ -155,6 +155,7 @@ typedef struct {
loff_t current_index; loff_t current_index;
decode_dirent_t decode; decode_dirent_t decode;
unsigned long dir_verifier;
unsigned long timestamp; unsigned long timestamp;
unsigned long gencount; unsigned long gencount;
unsigned int cache_entry_index; unsigned int cache_entry_index;
...@@ -353,6 +354,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, ...@@ -353,6 +354,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
again: again:
timestamp = jiffies; timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter(); gencount = nfs_inc_attr_generation_counter();
desc->dir_verifier = nfs_save_change_attribute(inode);
error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages, error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
NFS_SERVER(inode)->dtsize, desc->plus); NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) { if (error < 0) {
...@@ -455,13 +457,13 @@ void nfs_force_use_readdirplus(struct inode *dir) ...@@ -455,13 +457,13 @@ void nfs_force_use_readdirplus(struct inode *dir)
} }
static static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
unsigned long dir_verifier)
{ {
struct qstr filename = QSTR_INIT(entry->name, entry->len); struct qstr filename = QSTR_INIT(entry->name, entry->len);
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct dentry *dentry; struct dentry *dentry;
struct dentry *alias; struct dentry *alias;
struct inode *dir = d_inode(parent);
struct inode *inode; struct inode *inode;
int status; int status;
...@@ -500,7 +502,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) ...@@ -500,7 +502,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
if (nfs_same_file(dentry, entry)) { if (nfs_same_file(dentry, entry)) {
if (!entry->fh->size) if (!entry->fh->size)
goto out; goto out;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, dir_verifier);
status = nfs_refresh_inode(d_inode(dentry), entry->fattr); status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
if (!status) if (!status)
nfs_setsecurity(d_inode(dentry), entry->fattr, entry->label); nfs_setsecurity(d_inode(dentry), entry->fattr, entry->label);
...@@ -526,7 +528,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) ...@@ -526,7 +528,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
dput(dentry); dput(dentry);
dentry = alias; dentry = alias;
} }
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, dir_verifier);
out: out:
dput(dentry); dput(dentry);
} }
...@@ -564,7 +566,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en ...@@ -564,7 +566,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
count++; count++;
if (desc->plus) if (desc->plus)
nfs_prime_dcache(file_dentry(desc->file), entry); nfs_prime_dcache(file_dentry(desc->file), entry,
desc->dir_verifier);
status = nfs_readdir_add_to_array(entry, page); status = nfs_readdir_add_to_array(entry, page);
if (status != 0) if (status != 0)
...@@ -983,14 +986,113 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, ...@@ -983,14 +986,113 @@ static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
* full lookup on all child dentries of 'dir' whenever a change occurs * full lookup on all child dentries of 'dir' whenever a change occurs
* on the server that might have invalidated our dcache. * on the server that might have invalidated our dcache.
* *
* Note that we reserve bit '0' as a tag to let us know when a dentry
* was revalidated while holding a delegation on its inode.
*
* The caller should be holding dir->i_lock * The caller should be holding dir->i_lock
*/ */
void nfs_force_lookup_revalidate(struct inode *dir) void nfs_force_lookup_revalidate(struct inode *dir)
{ {
NFS_I(dir)->cache_change_attribute++; NFS_I(dir)->cache_change_attribute += 2;
} }
EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate); EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
/**
* nfs_verify_change_attribute - Detects NFS remote directory changes
* @dir: pointer to parent directory inode
* @verf: previously saved change attribute
*
* Return "false" if the verifiers doesn't match the change attribute.
* This would usually indicate that the directory contents have changed on
* the server, and that any dentries need revalidating.
*/
static bool nfs_verify_change_attribute(struct inode *dir, unsigned long verf)
{
return (verf & ~1UL) == nfs_save_change_attribute(dir);
}
static void nfs_set_verifier_delegated(unsigned long *verf)
{
*verf |= 1UL;
}
#if IS_ENABLED(CONFIG_NFS_V4)
static void nfs_unset_verifier_delegated(unsigned long *verf)
{
*verf &= ~1UL;
}
#endif /* IS_ENABLED(CONFIG_NFS_V4) */
static bool nfs_test_verifier_delegated(unsigned long verf)
{
return verf & 1;
}
static bool nfs_verifier_is_delegated(struct dentry *dentry)
{
return nfs_test_verifier_delegated(dentry->d_time);
}
static void nfs_set_verifier_locked(struct dentry *dentry, unsigned long verf)
{
struct inode *inode = d_inode(dentry);
if (!nfs_verifier_is_delegated(dentry) &&
!nfs_verify_change_attribute(d_inode(dentry->d_parent), verf))
goto out;
if (inode && NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
nfs_set_verifier_delegated(&verf);
out:
dentry->d_time = verf;
}
/**
* nfs_set_verifier - save a parent directory verifier in the dentry
* @dentry: pointer to dentry
* @verf: verifier to save
*
* Saves the parent directory verifier in @dentry. If the inode has
* a delegation, we also tag the dentry as having been revalidated
* while holding a delegation so that we know we don't have to
* look it up again after a directory change.
*/
void nfs_set_verifier(struct dentry *dentry, unsigned long verf)
{
spin_lock(&dentry->d_lock);
nfs_set_verifier_locked(dentry, verf);
spin_unlock(&dentry->d_lock);
}
EXPORT_SYMBOL_GPL(nfs_set_verifier);
#if IS_ENABLED(CONFIG_NFS_V4)
/**
* nfs_clear_verifier_delegated - clear the dir verifier delegation tag
* @inode: pointer to inode
*
* Iterates through the dentries in the inode alias list and clears
* the tag used to indicate that the dentry has been revalidated
* while holding a delegation.
* This function is intended for use when the delegation is being
* returned or revoked.
*/
void nfs_clear_verifier_delegated(struct inode *inode)
{
struct dentry *alias;
if (!inode)
return;
spin_lock(&inode->i_lock);
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
spin_lock(&alias->d_lock);
nfs_unset_verifier_delegated(&alias->d_time);
spin_unlock(&alias->d_lock);
}
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_clear_verifier_delegated);
#endif /* IS_ENABLED(CONFIG_NFS_V4) */
/* /*
* A check for whether or not the parent directory has changed. * A check for whether or not the parent directory has changed.
* In the case it has, we assume that the dentries are untrustworthy * In the case it has, we assume that the dentries are untrustworthy
...@@ -1159,6 +1261,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry, ...@@ -1159,6 +1261,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
struct nfs_fh *fhandle; struct nfs_fh *fhandle;
struct nfs_fattr *fattr; struct nfs_fattr *fattr;
struct nfs4_label *label; struct nfs4_label *label;
unsigned long dir_verifier;
int ret; int ret;
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1168,6 +1271,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry, ...@@ -1168,6 +1271,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
if (fhandle == NULL || fattr == NULL || IS_ERR(label)) if (fhandle == NULL || fattr == NULL || IS_ERR(label))
goto out; goto out;
dir_verifier = nfs_save_change_attribute(dir);
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label); ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (ret < 0) { if (ret < 0) {
switch (ret) { switch (ret) {
...@@ -1188,7 +1292,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry, ...@@ -1188,7 +1292,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
goto out; goto out;
nfs_setsecurity(inode, fattr, label); nfs_setsecurity(inode, fattr, label);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, dir_verifier);
/* set a readdirplus hint that we had a cache miss */ /* set a readdirplus hint that we had a cache miss */
nfs_force_use_readdirplus(dir); nfs_force_use_readdirplus(dir);
...@@ -1230,7 +1334,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, ...@@ -1230,7 +1334,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
goto out_bad; goto out_bad;
} }
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) if (nfs_verifier_is_delegated(dentry))
return nfs_lookup_revalidate_delegated(dir, dentry, inode); return nfs_lookup_revalidate_delegated(dir, dentry, inode);
/* Force a full look up iff the parent directory has changed */ /* Force a full look up iff the parent directory has changed */
...@@ -1415,6 +1519,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in ...@@ -1415,6 +1519,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
struct nfs_fh *fhandle = NULL; struct nfs_fh *fhandle = NULL;
struct nfs_fattr *fattr = NULL; struct nfs_fattr *fattr = NULL;
struct nfs4_label *label = NULL; struct nfs4_label *label = NULL;
unsigned long dir_verifier;
int error; int error;
dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry); dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
...@@ -1440,6 +1545,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in ...@@ -1440,6 +1545,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
if (IS_ERR(label)) if (IS_ERR(label))
goto out; goto out;
dir_verifier = nfs_save_change_attribute(dir);
trace_nfs_lookup_enter(dir, dentry, flags); trace_nfs_lookup_enter(dir, dentry, flags);
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label); error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
if (error == -ENOENT) if (error == -ENOENT)
...@@ -1463,7 +1569,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in ...@@ -1463,7 +1569,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
goto out_label; goto out_label;
dentry = res; dentry = res;
} }
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, dir_verifier);
out_label: out_label:
trace_nfs_lookup_exit(dir, dentry, flags, error); trace_nfs_lookup_exit(dir, dentry, flags, error);
nfs4_label_free(label); nfs4_label_free(label);
...@@ -1668,7 +1774,7 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry, ...@@ -1668,7 +1774,7 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
if (inode == NULL) if (inode == NULL)
goto full_reval; goto full_reval;
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) if (nfs_verifier_is_delegated(dentry))
return nfs_lookup_revalidate_delegated(dir, dentry, inode); return nfs_lookup_revalidate_delegated(dir, dentry, inode);
/* NFS only supports OPEN on regular files */ /* NFS only supports OPEN on regular files */
......
...@@ -2114,6 +2114,7 @@ static void init_once(void *foo) ...@@ -2114,6 +2114,7 @@ static void init_once(void *foo)
init_rwsem(&nfsi->rmdir_sem); init_rwsem(&nfsi->rmdir_sem);
mutex_init(&nfsi->commit_mutex); mutex_init(&nfsi->commit_mutex);
nfs4_init_once(nfsi); nfs4_init_once(nfsi);
nfsi->cache_change_attribute = 0;
} }
static int __init nfs_init_inodecache(void) static int __init nfs_init_inodecache(void)
......
...@@ -87,7 +87,6 @@ nfs4_file_open(struct inode *inode, struct file *filp) ...@@ -87,7 +87,6 @@ nfs4_file_open(struct inode *inode, struct file *filp)
if (inode != d_inode(dentry)) if (inode != d_inode(dentry))
goto out_drop; goto out_drop;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_file_set_open_context(filp, ctx); nfs_file_set_open_context(filp, ctx);
nfs_fscache_open_file(inode, filp); nfs_fscache_open_file(inode, filp);
err = 0; err = 0;
......
...@@ -2974,10 +2974,13 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, ...@@ -2974,10 +2974,13 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
struct dentry *dentry; struct dentry *dentry;
struct nfs4_state *state; struct nfs4_state *state;
fmode_t acc_mode = _nfs4_ctx_to_accessmode(ctx); fmode_t acc_mode = _nfs4_ctx_to_accessmode(ctx);
struct inode *dir = d_inode(opendata->dir);
unsigned long dir_verifier;
unsigned int seq; unsigned int seq;
int ret; int ret;
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
dir_verifier = nfs_save_change_attribute(dir);
ret = _nfs4_proc_open(opendata, ctx); ret = _nfs4_proc_open(opendata, ctx);
if (ret != 0) if (ret != 0)
...@@ -3005,8 +3008,19 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, ...@@ -3005,8 +3008,19 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
dput(ctx->dentry); dput(ctx->dentry);
ctx->dentry = dentry = alias; ctx->dentry = dentry = alias;
} }
nfs_set_verifier(dentry, }
nfs_save_change_attribute(d_inode(opendata->dir)));
switch(opendata->o_arg.claim) {
default:
break;
case NFS4_OPEN_CLAIM_NULL:
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
case NFS4_OPEN_CLAIM_DELEGATE_PREV:
if (!opendata->rpc_done)
break;
if (opendata->o_res.delegation_type != 0)
dir_verifier = nfs_save_change_attribute(dir);
nfs_set_verifier(dentry, dir_verifier);
} }
/* Parse layoutget results before we check for access */ /* Parse layoutget results before we check for access */
...@@ -5322,7 +5336,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr, ...@@ -5322,7 +5336,7 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
hdr->timestamp = jiffies; hdr->timestamp = jiffies;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 1, 0); nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0);
nfs4_state_protect_write(server->nfs_client, clnt, msg, hdr); nfs4_state_protect_write(server->nfs_client, clnt, msg, hdr);
} }
......
...@@ -337,35 +337,17 @@ static inline int nfs_server_capable(struct inode *inode, int cap) ...@@ -337,35 +337,17 @@ static inline int nfs_server_capable(struct inode *inode, int cap)
return NFS_SERVER(inode)->caps & cap; return NFS_SERVER(inode)->caps & cap;
} }
static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
{
dentry->d_time = verf;
}
/** /**
* nfs_save_change_attribute - Returns the inode attribute change cookie * nfs_save_change_attribute - Returns the inode attribute change cookie
* @dir - pointer to parent directory inode * @dir - pointer to parent directory inode
* The "change attribute" is updated every time we finish an operation * The "cache change attribute" is updated when we need to revalidate
* that will result in a metadata change on the server. * our dentry cache after a directory was seen to change on the server.
*/ */
static inline unsigned long nfs_save_change_attribute(struct inode *dir) static inline unsigned long nfs_save_change_attribute(struct inode *dir)
{ {
return NFS_I(dir)->cache_change_attribute; return NFS_I(dir)->cache_change_attribute;
} }
/**
* nfs_verify_change_attribute - Detects NFS remote directory changes
* @dir - pointer to parent directory inode
* @chattr - previously saved change attribute
* Return "false" if the verifiers doesn't match the change attribute.
* This would usually indicate that the directory contents have changed on
* the server, and that any dentries need revalidating.
*/
static inline int nfs_verify_change_attribute(struct inode *dir, unsigned long chattr)
{
return chattr == NFS_I(dir)->cache_change_attribute;
}
/* /*
* linux/fs/nfs/inode.c * linux/fs/nfs/inode.c
*/ */
...@@ -495,6 +477,10 @@ extern const struct file_operations nfs_dir_operations; ...@@ -495,6 +477,10 @@ extern const struct file_operations nfs_dir_operations;
extern const struct dentry_operations nfs_dentry_operations; extern const struct dentry_operations nfs_dentry_operations;
extern void nfs_force_lookup_revalidate(struct inode *dir); extern void nfs_force_lookup_revalidate(struct inode *dir);
extern void nfs_set_verifier(struct dentry * dentry, unsigned long verf);
#if IS_ENABLED(CONFIG_NFS_V4)
extern void nfs_clear_verifier_delegated(struct inode *inode);
#endif /* IS_ENABLED(CONFIG_NFS_V4) */
extern struct dentry *nfs_add_or_obtain(struct dentry *dentry, extern struct dentry *nfs_add_or_obtain(struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs_fh *fh, struct nfs_fattr *fattr,
struct nfs4_label *label); struct nfs4_label *label);
......
...@@ -288,8 +288,8 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, ...@@ -288,8 +288,8 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt,
{ {
struct rpcrdma_ia *ia = &r_xprt->rx_ia; struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct ib_reg_wr *reg_wr; struct ib_reg_wr *reg_wr;
int i, n, dma_nents;
struct ib_mr *ibmr; struct ib_mr *ibmr;
int i, n;
u8 key; u8 key;
if (nsegs > ia->ri_max_frwr_depth) if (nsegs > ia->ri_max_frwr_depth)
...@@ -313,15 +313,16 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt, ...@@ -313,15 +313,16 @@ struct rpcrdma_mr_seg *frwr_map(struct rpcrdma_xprt *r_xprt,
break; break;
} }
mr->mr_dir = rpcrdma_data_dir(writing); mr->mr_dir = rpcrdma_data_dir(writing);
mr->mr_nents = i;
mr->mr_nents = dma_nents = ib_dma_map_sg(ia->ri_id->device, mr->mr_sg, mr->mr_nents,
ib_dma_map_sg(ia->ri_id->device, mr->mr_sg, i, mr->mr_dir); mr->mr_dir);
if (!mr->mr_nents) if (!dma_nents)
goto out_dmamap_err; goto out_dmamap_err;
ibmr = mr->frwr.fr_mr; ibmr = mr->frwr.fr_mr;
n = ib_map_mr_sg(ibmr, mr->mr_sg, mr->mr_nents, NULL, PAGE_SIZE); n = ib_map_mr_sg(ibmr, mr->mr_sg, dma_nents, NULL, PAGE_SIZE);
if (unlikely(n != mr->mr_nents)) if (n != dma_nents)
goto out_mapmr_err; goto out_mapmr_err;
ibmr->iova &= 0x00000000ffffffff; ibmr->iova &= 0x00000000ffffffff;
......
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