Skip to content

Commit

Permalink
add a vfs_fsync helper
Browse files Browse the repository at this point in the history
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: Christoph Hellwig <[email protected]>
Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Christoph Hellwig authored and Al Viro committed Jan 5, 2009
1 parent 6110e3a commit 4c728ef
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 84 deletions.
18 changes: 1 addition & 17 deletions drivers/usb/gadget/file_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -1863,26 +1863,10 @@ static int do_write(struct fsg_dev *fsg)
static int fsync_sub(struct lun *curlun)
{
struct file *filp = curlun->filp;
struct inode *inode;
int rc, err;

if (curlun->ro || !filp)
return 0;
if (!filp->f_op->fsync)
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;
return vfs_fsync(filp, filp->f_path.dentry, 1);
}

static void fsync_all(struct fsg_dev *fsg)
Expand Down
12 changes: 2 additions & 10 deletions fs/coda/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
struct file *host_file;
struct dentry *host_dentry;
struct inode *host_inode, *coda_inode = coda_dentry->d_inode;
struct inode *coda_inode = coda_dentry->d_inode;
struct coda_file_info *cfi;
int err = 0;

Expand All @@ -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);
host_file = cfi->cfi_container;

if (host_file->f_op && host_file->f_op->fsync) {
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);
}

err = vfs_fsync(host_file, host_file->f_path.dentry, datasync);
if ( !err && !datasync ) {
lock_kernel();
err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
Expand Down
15 changes: 3 additions & 12 deletions fs/ecryptfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,18 +275,9 @@ static int ecryptfs_release(struct inode *inode, struct file *file)
static int
ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct file *lower_file = ecryptfs_file_to_lower(file);
struct dentry *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);
mutex_unlock(&lower_inode->i_mutex);
}
return rc;
return vfs_fsync(ecryptfs_file_to_lower(file),
ecryptfs_dentry_to_lower(dentry),
datasync);
}

static int ecryptfs_fasync(int fd, struct file *file, int flag)
Expand Down
35 changes: 3 additions & 32 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,45 +744,16 @@ nfsd_close(struct file *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
nfsd_sync(struct file *filp)
{
int err;
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;
return vfs_fsync(filp, filp->f_path.dentry, 0);
}

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);
}

/*
Expand Down
48 changes: 37 additions & 11 deletions fs/sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,39 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
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;
int err;
struct address_space *mapping = file->f_mapping;
const struct file_operations *fop;
struct address_space *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) {
/* Why? We can still call filemap_fdatawrite */
if (!fop || !fop->fsync) {
ret = -EINVAL;
goto out;
}
Expand All @@ -94,7 +119,7 @@ long do_fsync(struct file *file, int datasync)
* livelocks in fsync_buffers_list().
*/
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)
ret = err;
mutex_unlock(&mapping->host->i_mutex);
Expand All @@ -104,28 +129,29 @@ long do_fsync(struct file *file, int datasync)
out:
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;
int ret = -EBADF;

file = fget(fd);
if (file) {
ret = do_fsync(file, datasync);
ret = vfs_fsync(file, file->f_path.dentry, datasync);
fput(file);
}
return ret;
}

asmlinkage long sys_fsync(unsigned int fd)
{
return __do_fsync(fd, 0);
return do_fsync(fd, 0);
}

asmlinkage long sys_fdatasync(unsigned int fd)
{
return __do_fsync(fd, 1);
return do_fsync(fd, 1);
}

/*
Expand Down
2 changes: 1 addition & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1827,7 +1827,7 @@ extern int __filemap_fdatawrite_range(struct address_space *mapping,
extern int filemap_fdatawrite_range(struct address_space *mapping,
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_filesystems(int wait);
extern void __fsync_super(struct super_block *sb);
Expand Down
2 changes: 1 addition & 1 deletion mm/msync.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
(vma->vm_flags & VM_SHARED)) {
get_file(file);
up_read(&mm->mmap_sem);
error = do_fsync(file, 0);
error = vfs_fsync(file, file->f_path.dentry, 0);
fput(file);
if (error || start >= end)
goto out;
Expand Down

0 comments on commit 4c728ef

Please sign in to comment.