mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
NFS: Always clear an invalid mapping when attempting a buffered write
If the page cache is invalid, then we can't do read-modify-write, so ensure that we do clear it when we know it is invalid. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
fc9dc40189
commit
28aa2f9e73
@ -632,6 +632,8 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
|
||||
goto out;
|
||||
}
|
||||
|
||||
nfs_clear_invalid_mapping(file->f_mapping);
|
||||
|
||||
since = filemap_sample_wb_err(file->f_mapping);
|
||||
nfs_start_io_write(inode);
|
||||
result = generic_write_checks(iocb, from);
|
||||
|
113
fs/nfs/inode.c
113
fs/nfs/inode.c
@ -1257,6 +1257,63 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_clear_invalid_mapping - Conditionally clear a mapping
|
||||
* @mapping: pointer to mapping
|
||||
*
|
||||
* If the NFS_INO_INVALID_DATA inode flag is set, clear the mapping.
|
||||
*/
|
||||
int nfs_clear_invalid_mapping(struct address_space *mapping)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
unsigned long *bitlock = &nfsi->flags;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* 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_action(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 | NFS_INO_DATA_INVAL_DEFER);
|
||||
spin_unlock(&inode->i_lock);
|
||||
trace_nfs_invalidate_mapping_enter(inode);
|
||||
ret = nfs_invalidate_mapping(inode, mapping);
|
||||
trace_nfs_invalidate_mapping_exit(inode, ret);
|
||||
|
||||
clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(bitlock, NFS_INO_INVALIDATING);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool nfs_mapping_need_revalidate_inode(struct inode *inode)
|
||||
{
|
||||
return nfs_check_cache_invalid(inode, NFS_INO_REVAL_PAGECACHE) ||
|
||||
@ -1289,65 +1346,19 @@ out:
|
||||
* @inode: pointer to host inode
|
||||
* @mapping: pointer to mapping
|
||||
*/
|
||||
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);
|
||||
unsigned long *bitlock = &nfsi->flags;
|
||||
int ret = 0;
|
||||
|
||||
/* swapfiles are not supposed to be shared. */
|
||||
if (IS_SWAPFILE(inode))
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
if (nfs_mapping_need_revalidate_inode(inode)) {
|
||||
ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
int ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_action(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|
|
||||
NFS_INO_DATA_INVAL_DEFER);
|
||||
spin_unlock(&inode->i_lock);
|
||||
trace_nfs_invalidate_mapping_enter(inode);
|
||||
ret = nfs_invalidate_mapping(inode, mapping);
|
||||
trace_nfs_invalidate_mapping_exit(inode, ret);
|
||||
|
||||
clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(bitlock, NFS_INO_INVALIDATING);
|
||||
out:
|
||||
return ret;
|
||||
return nfs_clear_invalid_mapping(mapping);
|
||||
}
|
||||
|
||||
static bool nfs_file_has_writers(struct nfs_inode *nfsi)
|
||||
|
@ -387,6 +387,7 @@ extern int nfs_open(struct inode *, struct file *);
|
||||
extern int nfs_attribute_cache_expired(struct inode *inode);
|
||||
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
|
||||
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
||||
extern int nfs_clear_invalid_mapping(struct address_space *mapping);
|
||||
extern bool nfs_mapping_need_revalidate_inode(struct inode *inode);
|
||||
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
||||
extern int nfs_revalidate_mapping_rcu(struct inode *inode);
|
||||
|
Loading…
Reference in New Issue
Block a user