diff options
author | Christoph Hellwig <hch@lst.de> | 2008-12-22 15:11:15 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-01-05 11:54:28 -0500 |
commit | 4c728ef583b3d82266584da5cb068294c09df31e (patch) | |
tree | 1252fa82b5a7cf60c0898c3da810228b4c34ebb3 | |
parent | 6110e3abbff8b785907d4db50240e63c1be726e3 (diff) |
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>
-rw-r--r-- | drivers/usb/gadget/file_storage.c | 18 | ||||
-rw-r--r-- | fs/coda/file.c | 12 | ||||
-rw-r--r-- | fs/ecryptfs/file.c | 15 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 35 | ||||
-rw-r--r-- | fs/sync.c | 48 | ||||
-rw-r--r-- | include/linux/fs.h | 2 | ||||
-rw-r--r-- | mm/msync.c | 2 |
7 files changed, 48 insertions, 84 deletions
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c4e62a6297d7..2e71368f45b4 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) | |||
1863 | static int fsync_sub(struct lun *curlun) | 1863 | static int fsync_sub(struct lun *curlun) |
1864 | { | 1864 | { |
1865 | struct file *filp = curlun->filp; | 1865 | struct file *filp = curlun->filp; |
1866 | struct inode *inode; | ||
1867 | int rc, err; | ||
1868 | 1866 | ||
1869 | if (curlun->ro || !filp) | 1867 | if (curlun->ro || !filp) |
1870 | return 0; | 1868 | return 0; |
1871 | if (!filp->f_op->fsync) | 1869 | return vfs_fsync(filp, filp->f_path.dentry, 1); |
1872 | return -EINVAL; | ||
1873 | |||
1874 | inode = filp->f_path.dentry->d_inode; | ||
1875 | mutex_lock(&inode->i_mutex); | ||
1876 | rc = filemap_fdatawrite(inode->i_mapping); | ||
1877 | err = filp->f_op->fsync(filp, filp->f_path.dentry, 1); | ||
1878 | if (!rc) | ||
1879 | rc = err; | ||
1880 | err = filemap_fdatawait(inode->i_mapping); | ||
1881 | if (!rc) | ||
1882 | rc = err; | ||
1883 | mutex_unlock(&inode->i_mutex); | ||
1884 | VLDBG(curlun, "fdatasync -> %d\n", rc); | ||
1885 | return rc; | ||
1886 | } | 1870 | } |
1887 | 1871 | ||
1888 | static void fsync_all(struct fsg_dev *fsg) | 1872 | static void fsync_all(struct fsg_dev *fsg) |
diff --git a/fs/coda/file.c b/fs/coda/file.c index 466303db2df6..6a347fbc998a 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) | |||
201 | int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) | 201 | int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) |
202 | { | 202 | { |
203 | struct file *host_file; | 203 | struct file *host_file; |
204 | struct dentry *host_dentry; | 204 | struct inode *coda_inode = coda_dentry->d_inode; |
205 | struct inode *host_inode, *coda_inode = coda_dentry->d_inode; | ||
206 | struct coda_file_info *cfi; | 205 | struct coda_file_info *cfi; |
207 | int err = 0; | 206 | int err = 0; |
208 | 207 | ||
@@ -214,14 +213,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) | |||
214 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | 213 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); |
215 | host_file = cfi->cfi_container; | 214 | host_file = cfi->cfi_container; |
216 | 215 | ||
217 | if (host_file->f_op && host_file->f_op->fsync) { | 216 | err = vfs_fsync(host_file, host_file->f_path.dentry, datasync); |
218 | host_dentry = host_file->f_path.dentry; | ||
219 | host_inode = host_dentry->d_inode; | ||
220 | mutex_lock(&host_inode->i_mutex); | ||
221 | err = host_file->f_op->fsync(host_file, host_dentry, datasync); | ||
222 | mutex_unlock(&host_inode->i_mutex); | ||
223 | } | ||
224 | |||
225 | if ( !err && !datasync ) { | 217 | if ( !err && !datasync ) { |
226 | lock_kernel(); | 218 | lock_kernel(); |
227 | err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); | 219 | err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); |
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index eb3dc4c7ac06..713834371229 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) | |||
275 | static int | 275 | static int |
276 | ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) | 276 | ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) |
277 | { | 277 | { |
278 | struct file *lower_file = ecryptfs_file_to_lower(file); | 278 | return vfs_fsync(ecryptfs_file_to_lower(file), |
279 | struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); | 279 | ecryptfs_dentry_to_lower(dentry), |
280 | struct inode *lower_inode = lower_dentry->d_inode; | 280 | datasync); |
281 | int rc = -EINVAL; | ||
282 | |||
283 | if (lower_inode->i_fop->fsync) { | ||
284 | mutex_lock(&lower_inode->i_mutex); | ||
285 | rc = lower_inode->i_fop->fsync(lower_file, lower_dentry, | ||
286 | datasync); | ||
287 | mutex_unlock(&lower_inode->i_mutex); | ||
288 | } | ||
289 | return rc; | ||
290 | } | 281 | } |
291 | 282 | ||
292 | static int ecryptfs_fasync(int fd, struct file *file, int flag) | 283 | static int ecryptfs_fasync(int fd, struct file *file, int flag) |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 5245a3965004..44aa92aba891 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -744,45 +744,16 @@ nfsd_close(struct file *filp) | |||
744 | fput(filp); | 744 | fput(filp); |
745 | } | 745 | } |
746 | 746 | ||
747 | /* | ||
748 | * Sync a file | ||
749 | * As this calls fsync (not fdatasync) there is no need for a write_inode | ||
750 | * after it. | ||
751 | */ | ||
752 | static inline int nfsd_dosync(struct file *filp, struct dentry *dp, | ||
753 | const struct file_operations *fop) | ||
754 | { | ||
755 | struct inode *inode = dp->d_inode; | ||
756 | int (*fsync) (struct file *, struct dentry *, int); | ||
757 | int err; | ||
758 | |||
759 | err = filemap_fdatawrite(inode->i_mapping); | ||
760 | if (err == 0 && fop && (fsync = fop->fsync)) | ||
761 | err = fsync(filp, dp, 0); | ||
762 | if (err == 0) | ||
763 | err = filemap_fdatawait(inode->i_mapping); | ||
764 | |||
765 | return err; | ||
766 | } | ||
767 | |||
768 | |||
769 | static int | 747 | static int |
770 | nfsd_sync(struct file *filp) | 748 | nfsd_sync(struct file *filp) |
771 | { | 749 | { |
772 | int err; | 750 | return vfs_fsync(filp, filp->f_path.dentry, 0); |
773 | struct inode *inode = filp->f_path.dentry->d_inode; | ||
774 | dprintk("nfsd: sync file %s\n", filp->f_path.dentry->d_name.name); | ||
775 | mutex_lock(&inode->i_mutex); | ||
776 | err=nfsd_dosync(filp, filp->f_path.dentry, filp->f_op); | ||
777 | mutex_unlock(&inode->i_mutex); | ||
778 | |||
779 | return err; | ||
780 | } | 751 | } |
781 | 752 | ||
782 | int | 753 | int |
783 | nfsd_sync_dir(struct dentry *dp) | 754 | nfsd_sync_dir(struct dentry *dentry) |
784 | { | 755 | { |
785 | return nfsd_dosync(NULL, dp, dp->d_inode->i_fop); | 756 | return vfs_fsync(NULL, dentry, 0); |
786 | } | 757 | } |
787 | 758 | ||
788 | /* | 759 | /* |
@@ -75,14 +75,39 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync) | |||
75 | return ret; | 75 | return ret; |
76 | } | 76 | } |
77 | 77 | ||
78 | long do_fsync(struct file *file, int datasync) | 78 | /** |
79 | * vfs_fsync - perform a fsync or fdatasync on a file | ||
80 | * @file: file to sync | ||
81 | * @dentry: dentry of @file | ||
82 | * @data: only perform a fdatasync operation | ||
83 | * | ||
84 | * Write back data and metadata for @file to disk. If @datasync is | ||
85 | * set only metadata needed to access modified file data is written. | ||
86 | * | ||
87 | * In case this function is called from nfsd @file may be %NULL and | ||
88 | * only @dentry is set. This can only happen when the filesystem | ||
89 | * implements the export_operations API. | ||
90 | */ | ||
91 | int vfs_fsync(struct file *file, struct dentry *dentry, int datasync) | ||
79 | { | 92 | { |
80 | int ret; | 93 | const struct file_operations *fop; |
81 | int err; | 94 | struct address_space *mapping; |
82 | struct address_space *mapping = file->f_mapping; | 95 | int err, ret; |
96 | |||
97 | /* | ||
98 | * Get mapping and operations from the file in case we have | ||
99 | * as file, or get the default values for them in case we | ||
100 | * don't have a struct file available. Damn nfsd.. | ||
101 | */ | ||
102 | if (file) { | ||
103 | mapping = file->f_mapping; | ||
104 | fop = file->f_op; | ||
105 | } else { | ||
106 | mapping = dentry->d_inode->i_mapping; | ||
107 | fop = dentry->d_inode->i_fop; | ||
108 | } | ||
83 | 109 | ||
84 | if (!file->f_op || !file->f_op->fsync) { | 110 | if (!fop || !fop->fsync) { |
85 | /* Why? We can still call filemap_fdatawrite */ | ||
86 | ret = -EINVAL; | 111 | ret = -EINVAL; |
87 | goto out; | 112 | goto out; |
88 | } | 113 | } |
@@ -94,7 +119,7 @@ long do_fsync(struct file *file, int datasync) | |||
94 | * livelocks in fsync_buffers_list(). | 119 | * livelocks in fsync_buffers_list(). |
95 | */ | 120 | */ |
96 | mutex_lock(&mapping->host->i_mutex); | 121 | mutex_lock(&mapping->host->i_mutex); |
97 | err = file->f_op->fsync(file, file->f_path.dentry, datasync); | 122 | err = fop->fsync(file, dentry, datasync); |
98 | if (!ret) | 123 | if (!ret) |
99 | ret = err; | 124 | ret = err; |
100 | mutex_unlock(&mapping->host->i_mutex); | 125 | mutex_unlock(&mapping->host->i_mutex); |
@@ -104,15 +129,16 @@ long do_fsync(struct file *file, int datasync) | |||
104 | out: | 129 | out: |
105 | return ret; | 130 | return ret; |
106 | } | 131 | } |
132 | EXPORT_SYMBOL(vfs_fsync); | ||
107 | 133 | ||
108 | static long __do_fsync(unsigned int fd, int datasync) | 134 | static int do_fsync(unsigned int fd, int datasync) |
109 | { | 135 | { |
110 | struct file *file; | 136 | struct file *file; |
111 | int ret = -EBADF; | 137 | int ret = -EBADF; |
112 | 138 | ||
113 | file = fget(fd); | 139 | file = fget(fd); |
114 | if (file) { | 140 | if (file) { |
115 | ret = do_fsync(file, datasync); | 141 | ret = vfs_fsync(file, file->f_path.dentry, datasync); |
116 | fput(file); | 142 | fput(file); |
117 | } | 143 | } |
118 | return ret; | 144 | return ret; |
@@ -120,12 +146,12 @@ static long __do_fsync(unsigned int fd, int datasync) | |||
120 | 146 | ||
121 | asmlinkage long sys_fsync(unsigned int fd) | 147 | asmlinkage long sys_fsync(unsigned int fd) |
122 | { | 148 | { |
123 | return __do_fsync(fd, 0); | 149 | return do_fsync(fd, 0); |
124 | } | 150 | } |
125 | 151 | ||
126 | asmlinkage long sys_fdatasync(unsigned int fd) | 152 | asmlinkage long sys_fdatasync(unsigned int fd) |
127 | { | 153 | { |
128 | return __do_fsync(fd, 1); | 154 | return do_fsync(fd, 1); |
129 | } | 155 | } |
130 | 156 | ||
131 | /* | 157 | /* |
diff --git a/include/linux/fs.h b/include/linux/fs.h index e2170ee21e18..9ad9eac9eb0c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1827,7 +1827,7 @@ extern int __filemap_fdatawrite_range(struct address_space *mapping, | |||
1827 | extern int filemap_fdatawrite_range(struct address_space *mapping, | 1827 | extern int filemap_fdatawrite_range(struct address_space *mapping, |
1828 | loff_t start, loff_t end); | 1828 | loff_t start, loff_t end); |
1829 | 1829 | ||
1830 | extern long do_fsync(struct file *file, int datasync); | 1830 | extern int vfs_fsync(struct file *file, struct dentry *dentry, int datasync); |
1831 | extern void sync_supers(void); | 1831 | extern void sync_supers(void); |
1832 | extern void sync_filesystems(int wait); | 1832 | extern void sync_filesystems(int wait); |
1833 | extern void __fsync_super(struct super_block *sb); | 1833 | extern void __fsync_super(struct super_block *sb); |
diff --git a/mm/msync.c b/mm/msync.c index 144a7570535d..07dae08cf31c 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) | |||
82 | (vma->vm_flags & VM_SHARED)) { | 82 | (vma->vm_flags & VM_SHARED)) { |
83 | get_file(file); | 83 | get_file(file); |
84 | up_read(&mm->mmap_sem); | 84 | up_read(&mm->mmap_sem); |
85 | error = do_fsync(file, 0); | 85 | error = vfs_fsync(file, file->f_path.dentry, 0); |
86 | fput(file); | 86 | fput(file); |
87 | if (error || start >= end) | 87 | if (error || start >= end) |
88 | goto out; | 88 | goto out; |