Commit 4c728ef5 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Al Viro

add a vfs_fsync helper

Fsync currently has a fdatawrite/fdatawait pair around the method call,
and a mutex_lock/unlock of the inode mutex.  All callers of fsync have
to duplicate this, but we have a few and most of them don't quite get
it right.  This patch adds a new vfs_fsync that takes care of this.
It's a little more complicated as usual as ->fsync might get a NULL file
pointer and just a dentry from nfsd, but otherwise gets afile and we
want to take the mapping and file operations from it when it is there.

Notes on the fsync callers:

 - ecryptfs wasn't calling filemap_fdatawrite / filemap_fdatawait on the
   	lower file
 - coda wasn't calling filemap_fdatawrite / filemap_fdatawait on the host
	file, and returning 0 when ->fsync was missing
 - shm wasn't calling either filemap_fdatawrite / filemap_fdatawait nor
   taking i_mutex.  Now given that shared memory doesn't have disk
   backing not doing anything in fsync seems fine and I left it out of
   the vfs_fsync conversion for now, but in that case we might just
   not pass it through to the lower file at all but just call the no-op
   simple_sync_file directly.

[and now actually export vfs_fsync]
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 6110e3ab
...@@ -1863,26 +1863,10 @@ static int do_write(struct fsg_dev *fsg) ...@@ -1863,26 +1863,10 @@ static int do_write(struct fsg_dev *fsg)
static int fsync_sub(struct lun *curlun) static int fsync_sub(struct lun *curlun)
{ {
struct file *filp = curlun->filp; struct file *filp = curlun->filp;
struct inode *inode;
int rc, err;
if (curlun->ro || !filp) if (curlun->ro || !filp)
return 0; return 0;
if (!filp->f_op->fsync) return vfs_fsync(filp, filp->f_path.dentry, 1);
return -EINVAL;
inode = filp->f_path.dentry->d_inode;
mutex_lock(&inode->i_mutex);
rc = filemap_fdatawrite(inode->i_mapping);
err = filp->f_op->fsync(filp, filp->f_path.dentry, 1);
if (!rc)
rc = err;
err = filemap_fdatawait(inode->i_mapping);
if (!rc)
rc = err;
mutex_unlock(&inode->i_mutex);
VLDBG(curlun, "fdatasync -> %d\n", rc);
return rc;
} }
static void fsync_all(struct fsg_dev *fsg) static void fsync_all(struct fsg_dev *fsg)
......
...@@ -201,8 +201,7 @@ int coda_release(struct inode *coda_inode, struct file *coda_file) ...@@ -201,8 +201,7 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)
int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync)
{ {
struct file *host_file; struct file *host_file;
struct dentry *host_dentry; struct inode *coda_inode = coda_dentry->d_inode;
struct inode *host_inode, *coda_inode = coda_dentry->d_inode;
struct coda_file_info *cfi; struct coda_file_info *cfi;
int err = 0; int err = 0;
...@@ -214,14 +213,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) ...@@ -214,14 +213,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync)
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
host_file = cfi->cfi_container; host_file = cfi->cfi_container;
if (host_file->f_op && host_file->f_op->fsync) { err = vfs_fsync(host_file, host_file->f_path.dentry, datasync);
host_dentry = host_file->f_path.dentry;
host_inode = host_dentry->d_inode;
mutex_lock(&host_inode->i_mutex);
err = host_file->f_op->fsync(host_file, host_dentry, datasync);
mutex_unlock(&host_inode->i_mutex);
}
if ( !err && !datasync ) { if ( !err && !datasync ) {
lock_kernel(); lock_kernel();
err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
......
...@@ -275,18 +275,9 @@ static int ecryptfs_release(struct inode *inode, struct file *file) ...@@ -275,18 +275,9 @@ static int ecryptfs_release(struct inode *inode, struct file *file)
static int static int
ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{ {
struct file *lower_file = ecryptfs_file_to_lower(file); return vfs_fsync(ecryptfs_file_to_lower(file),
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); ecryptfs_dentry_to_lower(dentry),
struct inode *lower_inode = lower_dentry->d_inode;
int rc = -EINVAL;
if (lower_inode->i_fop->fsync) {
mutex_lock(&lower_inode->i_mutex);
rc = lower_inode->i_fop->fsync(lower_file, lower_dentry,
datasync); datasync);
mutex_unlock(&lower_inode->i_mutex);
}
return rc;
} }
static int ecryptfs_fasync(int fd, struct file *file, int flag) static int ecryptfs_fasync(int fd, struct file *file, int flag)
......
...@@ -744,45 +744,16 @@ nfsd_close(struct file *filp) ...@@ -744,45 +744,16 @@ nfsd_close(struct file *filp)
fput(filp); fput(filp);
} }
/*
* Sync a file
* As this calls fsync (not fdatasync) there is no need for a write_inode
* after it.
*/
static inline int nfsd_dosync(struct file *filp, struct dentry *dp,
const struct file_operations *fop)
{
struct inode *inode = dp->d_inode;
int (*fsync) (struct file *, struct dentry *, int);
int err;
err = filemap_fdatawrite(inode->i_mapping);
if (err == 0 && fop && (fsync = fop->fsync))
err = fsync(filp, dp, 0);
if (err == 0)
err = filemap_fdatawait(inode->i_mapping);
return err;
}
static int static int
nfsd_sync(struct file *filp) nfsd_sync(struct file *filp)
{ {
int err; return vfs_fsync(filp, filp->f_path.dentry, 0);
struct inode *inode = filp->f_path.dentry->d_inode;
dprintk("nfsd: sync file %s\n", filp->f_path.dentry->d_name.name);
mutex_lock(&inode->i_mutex);
err=nfsd_dosync(filp, filp->f_path.dentry, filp->f_op);
mutex_unlock(&inode->i_mutex);
return err;
} }
int int
nfsd_sync_dir(struct dentry *dp) nfsd_sync_dir(struct dentry *dentry)
{ {
return nfsd_dosync(NULL, dp, dp->d_inode->i_fop); return vfs_fsync(NULL, dentry, 0);
} }
/* /*
......
...@@ -75,14 +75,39 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync) ...@@ -75,14 +75,39 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
return ret; return ret;
} }
long do_fsync(struct file *file, int datasync) /**
* vfs_fsync - perform a fsync or fdatasync on a file
* @file: file to sync
* @dentry: dentry of @file
* @data: only perform a fdatasync operation
*
* Write back data and metadata for @file to disk. If @datasync is
* set only metadata needed to access modified file data is written.
*
* In case this function is called from nfsd @file may be %NULL and
* only @dentry is set. This can only happen when the filesystem
* implements the export_operations API.
*/
int vfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{ {
int ret; const struct file_operations *fop;
int err; struct address_space *mapping;
struct address_space *mapping = file->f_mapping; int err, ret;
/*
* Get mapping and operations from the file in case we have
* as file, or get the default values for them in case we
* don't have a struct file available. Damn nfsd..
*/
if (file) {
mapping = file->f_mapping;
fop = file->f_op;
} else {
mapping = dentry->d_inode->i_mapping;
fop = dentry->d_inode->i_fop;
}
if (!file->f_op || !file->f_op->fsync) { if (!fop || !fop->fsync) {
/* Why? We can still call filemap_fdatawrite */
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
...@@ -94,7 +119,7 @@ long do_fsync(struct file *file, int datasync) ...@@ -94,7 +119,7 @@ long do_fsync(struct file *file, int datasync)
* livelocks in fsync_buffers_list(). * livelocks in fsync_buffers_list().
*/ */
mutex_lock(&mapping->host->i_mutex); mutex_lock(&mapping->host->i_mutex);
err = file->f_op->fsync(file, file->f_path.dentry, datasync); err = fop->fsync(file, dentry, datasync);
if (!ret) if (!ret)
ret = err; ret = err;
mutex_unlock(&mapping->host->i_mutex); mutex_unlock(&mapping->host->i_mutex);
...@@ -104,15 +129,16 @@ long do_fsync(struct file *file, int datasync) ...@@ -104,15 +129,16 @@ long do_fsync(struct file *file, int datasync)
out: out:
return ret; return ret;
} }
EXPORT_SYMBOL(vfs_fsync);
static long __do_fsync(unsigned int fd, int datasync) static int do_fsync(unsigned int fd, int datasync)
{ {
struct file *file; struct file *file;
int ret = -EBADF; int ret = -EBADF;
file = fget(fd); file = fget(fd);
if (file) { if (file) {
ret = do_fsync(file, datasync); ret = vfs_fsync(file, file->f_path.dentry, datasync);
fput(file); fput(file);
} }
return ret; return ret;
...@@ -120,12 +146,12 @@ static long __do_fsync(unsigned int fd, int datasync) ...@@ -120,12 +146,12 @@ static long __do_fsync(unsigned int fd, int datasync)
asmlinkage long sys_fsync(unsigned int fd) asmlinkage long sys_fsync(unsigned int fd)
{ {
return __do_fsync(fd, 0); return do_fsync(fd, 0);
} }
asmlinkage long sys_fdatasync(unsigned int fd) asmlinkage long sys_fdatasync(unsigned int fd)
{ {
return __do_fsync(fd, 1); return do_fsync(fd, 1);
} }
/* /*
......
...@@ -1827,7 +1827,7 @@ extern int __filemap_fdatawrite_range(struct address_space *mapping, ...@@ -1827,7 +1827,7 @@ extern int __filemap_fdatawrite_range(struct address_space *mapping,
extern int filemap_fdatawrite_range(struct address_space *mapping, extern int filemap_fdatawrite_range(struct address_space *mapping,
loff_t start, loff_t end); loff_t start, loff_t end);
extern long do_fsync(struct file *file, int datasync); extern int vfs_fsync(struct file *file, struct dentry *dentry, int datasync);
extern void sync_supers(void); extern void sync_supers(void);
extern void sync_filesystems(int wait); extern void sync_filesystems(int wait);
extern void __fsync_super(struct super_block *sb); extern void __fsync_super(struct super_block *sb);
......
...@@ -82,7 +82,7 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags) ...@@ -82,7 +82,7 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
(vma->vm_flags & VM_SHARED)) { (vma->vm_flags & VM_SHARED)) {
get_file(file); get_file(file);
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
error = do_fsync(file, 0); error = vfs_fsync(file, file->f_path.dentry, 0);
fput(file); fput(file);
if (error || start >= end) if (error || start >= end)
goto out; goto out;
......
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