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)
|
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)
|
||||||
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)
|
||||||
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)
|
||||||
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;
|
datasync);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
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;
|
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;
|
||||||
|
|
||||||
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;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
(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;
|
||||||
|
|
Loading…
Reference in a new issue