add a vfs_fsync helper
authorChristoph Hellwig <hch@lst.de>
Mon, 22 Dec 2008 20:11:15 +0000 (21:11 +0100)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 5 Jan 2009 16:54:28 +0000 (11:54 -0500)
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 <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
drivers/usb/gadget/file_storage.c
fs/coda/file.c
fs/ecryptfs/file.c
fs/nfsd/vfs.c
fs/sync.c
include/linux/fs.h
mm/msync.c

index c4e62a6297d7254b375c682e24e3c3d9c5d84e5d..2e71368f45b4c6d423f27c11ab11aa14bc4dd06c 100644 (file)
@@ -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)
index 466303db2df6acbd11e047338b00739408fa428d..6a347fbc998a4cc3a23f60d3d2e91854db4206b2 100644 (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)
 {
        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;
 
@@ -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));
index eb3dc4c7ac066c45c99d26218ccda7f623cae9c6..713834371229b2d011d8355d6365810ab2b9eafe 100644 (file)
@@ -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)
index 5245a3965004259f2d156d495875260b7d254a6b..44aa92aba891c96fa7061ee5dda5bc6aaf6f0fec 100644 (file)
@@ -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);
 }
 
 /*
index 2967562d416fb52e54a1af5840d0dc63c0d25cfb..0921d6d4b5e6d27b6fe471128da5b07cf699f137 100644 (file)
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -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;
        }
@@ -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);
@@ -104,15 +129,16 @@ 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;
@@ -120,12 +146,12 @@ static long __do_fsync(unsigned int fd, int datasync)
 
 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);
 }
 
 /*
index e2170ee21e185bbfe6d4a7c4b3605402e634eca2..9ad9eac9eb0cf88ac49ff79d6eb1f4f0d0eb06eb 100644 (file)
@@ -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);
index 144a7570535d2b57cad3e9abac4618aa9257e29a..07dae08cf31c70c25b8c5ea6300265489c4c7d97 100644 (file)
@@ -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;