NFS Client Bugfixes for Linux 5.6-rc2
Stable Bugfixes: - Fix DMA scatter-gather list mapping imbalance Other Fixes: - 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAl5HEa4ACgkQ18tUv7Cl QOvwAA/9FAIwiE5UU+OrbpJ47IKFay2lfu6ywWVt0Q9i/tDeTRawa9aZDNKmKuYk rViVzRhIrmdkhpSVeTl3BdV/U9oXmNQ9ubFxWkQ0TI1j/tkTotT4dA0/kjeampZJ dZeW+NtRnFxYtfyV6fV/Bp/GB1RE9tAr1hTzHYEpNfnKxmAgkOeIXsVebuWKvUxz XCeRED30Ekt2n1JQqb1m3VAMpYmprF2i9TErAVR8ILt0dkkJD8nNlPogrZwMjzuD GtB7BSJ7rIDHQomuF3HVrz+SpMhM530mpsZiVGYCKjChJWaPm12+AZGPtv3UcpAf /M2VTa+z8MJDwirzjWogo3OvYAqZGI6KUG0o2t5dKmguQoi1RrDlPbFMLXlxow2e 5gi/qKFbnBOt6iIV9FffMb50jpnShBE6hQS4/rb2inGmAKYgEXcL+ouBY6308soS fnWJSViTQfGbF9uVxGeOYl766zEqlNleclfjKBCNXMC3HOfIslIBpilMDwCoOHeB MqYSqmnwAH9ZrqjNmR3Qh2RlcOxN8CTUMhkUW2KLMAgnEAd9WINaYu4u2Y50Fe6j yDa6YEAmaNSGVYVofh8qA1R1Zi4tnKlg1/WFdy/SnEQKi8mQqXASGiVu2bXjhlF1 22cHVXNIjp1PMijZKnJtimuxChygj9xxgzw4Kstd1WCLm2VirAc= =Ase9 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
829e694469
|
@ -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,
|
||||||
|
|
||||||
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)
|
||||||
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,
|
||||||
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
|
||||||
|
|
||||||
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)
|
||||||
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 @@ restart:
|
||||||
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 {
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
126
fs/nfs/dir.c
126
fs/nfs/dir.c
|
@ -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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ again:
|
||||||
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 @@ again:
|
||||||
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
|
||||||
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,
|
||||||
* 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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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
|
||||||
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
|
||||||
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 @@ no_entry:
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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;
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue