NFS: Fix another O_DIRECT race

Ensure we call unmap_mapping_range() and sync dirty pages to disk before
 doing an NFS direct write.

 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust 2005-12-13 16:13:54 -05:00
parent b079fa7baa
commit 29884df0d8
4 changed files with 34 additions and 42 deletions

View file

@ -678,15 +678,9 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
if (!count)
goto out;
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = nfs_wb_all(inode);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = nfs_sync_mapping(mapping);
if (retval)
goto out;
retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
if (retval > 0)
@ -764,15 +758,9 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
if (!count)
goto out;
if (mapping->nrpages) {
retval = filemap_fdatawrite(mapping);
if (retval == 0)
retval = nfs_wb_all(inode);
if (retval == 0)
retval = filemap_fdatawait(mapping);
if (retval)
goto out;
}
retval = nfs_sync_mapping(mapping);
if (retval)
goto out;
retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
if (mapping->nrpages)

View file

@ -433,11 +433,7 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* Flush all pending writes before doing anything
* with locks..
*/
filemap_fdatawrite(filp->f_mapping);
down(&inode->i_sem);
nfs_wb_all(inode);
up(&inode->i_sem);
filemap_fdatawait(filp->f_mapping);
nfs_sync_mapping(filp->f_mapping);
/* NOTE: special case
* If we're signalled while cleaning up locks on process exit, we
@ -465,15 +461,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
* Flush all pending writes before doing anything
* with locks..
*/
status = filemap_fdatawrite(filp->f_mapping);
if (status == 0) {
down(&inode->i_sem);
status = nfs_wb_all(inode);
up(&inode->i_sem);
if (status == 0)
status = filemap_fdatawait(filp->f_mapping);
}
if (status < 0)
status = nfs_sync_mapping(filp->f_mapping);
if (status != 0)
goto out;
lock_kernel();
@ -497,11 +486,7 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
* Make sure we clear the cache whenever we try to get the lock.
* This makes locking act as a cache coherency point.
*/
filemap_fdatawrite(filp->f_mapping);
down(&inode->i_sem);
nfs_wb_all(inode); /* we may have slept */
up(&inode->i_sem);
filemap_fdatawait(filp->f_mapping);
nfs_sync_mapping(filp->f_mapping);
nfs_zap_caches(inode);
out:
rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);

View file

@ -640,6 +640,27 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0;
}
/**
* nfs_sync_mapping - helper to flush all mmapped dirty data to disk
*/
int nfs_sync_mapping(struct address_space *mapping)
{
int ret;
if (mapping->nrpages == 0)
return 0;
unmap_mapping_range(mapping, 0, 0, 0);
ret = filemap_fdatawrite(mapping);
if (ret != 0)
goto out;
ret = filemap_fdatawait(mapping);
if (ret != 0)
goto out;
ret = nfs_wb_all(mapping->host);
out:
return ret;
}
/*
* Invalidate the local caches
*/
@ -1179,11 +1200,8 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
struct nfs_inode *nfsi = NFS_I(inode);
if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
if (S_ISREG(inode->i_mode)) {
if (filemap_fdatawrite(mapping) == 0)
filemap_fdatawait(mapping);
nfs_wb_all(inode);
}
if (S_ISREG(inode->i_mode))
nfs_sync_mapping(mapping);
invalidate_inode_pages2(mapping);
spin_lock(&inode->i_lock);

View file

@ -291,6 +291,7 @@ static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long
/*
* linux/fs/nfs/inode.c
*/
extern int nfs_sync_mapping(struct address_space *mapping);
extern void nfs_zap_caches(struct inode *);
extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
struct nfs_fattr *);