From 4c728ef583b3d82266584da5cb068294c09df31e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 22 Dec 2008 21:11:15 +0100 Subject: 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: Christoph Hellwig Signed-off-by: Al Viro diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c4e62a6..2e71368 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -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) diff --git a/fs/coda/file.c b/fs/coda/file.c index 466303d..6a347fb 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -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)); diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index eb3dc4c..7138343 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -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) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 5245a39..44aa92a 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -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); } /* diff --git a/fs/sync.c b/fs/sync.c index 2967562..0921d6d 100644 --- 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); } /* diff --git a/include/linux/fs.h b/include/linux/fs.h index e2170ee..9ad9eac 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -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); diff --git a/mm/msync.c b/mm/msync.c index 144a757..07dae08 100644 --- a/mm/msync.c +++ b/mm/msync.c @@ -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; -- cgit v0.10.2