mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
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 <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
6110e3abbf
commit
4c728ef583
7 changed files with 48 additions and 84 deletions
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
48
fs/sync.c
48
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;
|
||||
|
||||
if (!file->f_op || !file->f_op->fsync) {
|
||||
/* Why? We can still call filemap_fdatawrite */
|
||||
/*
|
||||
* 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 (!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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue