@@ -739,15 +739,15 @@ static int do_dentry_open(struct file *f,
f->f_inode = inode;
f->f_mapping = inode->i_mapping;
- /* Ensure that we skip any errors that predate opening of the file */
- f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
-
if (unlikely(f->f_flags & O_PATH)) {
f->f_mode = FMODE_PATH;
f->f_op = &empty_fops;
+ f->f_wb_err = errseq_sample(&f->f_path.dentry->d_sb->s_wb_err);
goto done;
}
+ f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
+
if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
error = get_write_access(inode);
if (unlikely(error))
@@ -686,7 +686,7 @@ int dquot_quota_sync(struct super_block *sb, int type)
/* This is not very clever (and fast) but currently I don't know about
* any other simple way of getting quota data to disk and we must get
* them there for userspace to be visible... */
- vfs_sync_fs(sb, 1);
+ vfs_sync_fs(sb, 1, NULL);
/*
* Now when everything is written we can discard the pagecache so
@@ -2243,7 +2243,7 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
/* Sync the superblock so that buffers with quota data are written to
* disk (and so userspace sees correct data afterwards). */
- vfs_sync_fs(sb, 1);
+ vfs_sync_fs(sb, 1, NULL);
/* Now the quota files are just ordinary files and we can set the
* inode flags back. Moreover we discard the pagecache so that
@@ -25,11 +25,20 @@
* Many legacy filesystems don't have a sync_fs op. For them, we just flush
* the block device (if there is one).
*/
-int vfs_sync_fs(struct super_block *sb, int wait)
+int vfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
{
+ int ret;
+
if (sb->s_op->sync_fs)
- return sb->s_op->sync_fs(sb, wait);
- return __sync_blockdev(sb->s_bdev, wait);
+ ret = sb->s_op->sync_fs(sb, wait);
+ ret = __sync_blockdev(sb->s_bdev, wait);
+
+ if (since) {
+ int ret2 = errseq_check_and_advance(&sb->s_wb_err, since);
+ if (ret == 0)
+ ret = ret2;
+ }
+ return ret;
}
EXPORT_SYMBOL(vfs_sync_fs);
@@ -47,7 +56,7 @@ static int __sync_filesystem(struct super_block *sb, int wait, errseq_t *since)
else
writeback_inodes_sb(sb, WB_REASON_SYNC);
- return vfs_sync_fs(sb, wait);
+ return vfs_sync_fs(sb, wait, since);
}
/*
@@ -90,7 +99,7 @@ static void sync_fs_one_sb(struct super_block *sb, void *arg)
if (sb_rdonly(sb))
return;
- vfs_sync_fs(sb, wait);
+ vfs_sync_fs(sb, wait, NULL);
}
static void fdatawrite_one_bdev(struct block_device *bdev, void *arg)
@@ -172,16 +181,21 @@ void emergency_sync(void)
*/
SYSCALL_DEFINE1(syncfs, int, fd)
{
- struct fd f = fdget(fd);
+ struct fd f = fdget_raw(fd);
struct super_block *sb;
+ errseq_t *wberr = NULL;
int ret;
if (!f.file)
return -EBADF;
+
sb = f.file->f_path.dentry->d_sb;
+ if (f.file->f_flags & O_PATH)
+ wberr = &f.file->f_wb_err;
+
down_read(&sb->s_umount);
- ret = sync_filesystem(sb, NULL);
+ ret = sync_filesystem(sb, wberr);
up_read(&sb->s_umount);
fdput(f);
@@ -1416,6 +1416,9 @@ struct super_block {
/* Being remounted read-only */
int s_readonly_remount;
+ /* per-sb errseq_t for reporting writeback errors via syncfs */
+ errseq_t s_wb_err;
+
/* AIO completions deferred from interrupt context */
struct workqueue_struct *s_dio_done_wq;
struct hlist_head s_pins;
@@ -2491,7 +2494,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
return false;
}
#endif
-int vfs_sync_fs(struct super_block *sb, int wait);
+int vfs_sync_fs(struct super_block *sb, int wait, errseq_t *since);
extern int sync_filesystem(struct super_block *, errseq_t *);
extern const struct file_operations def_blk_fops;
extern const struct file_operations def_chr_fops;
@@ -51,7 +51,10 @@ static inline void mapping_set_error(struct address_space *mapping, int error)
return;
/* Record in wb_err for checkers using errseq_t based tracking */
- filemap_set_wb_err(mapping, error);
+ __filemap_set_wb_err(mapping, error);
+
+ /* Record it in superblock */
+ errseq_set(&mapping->host->i_sb->s_wb_err, error);
/* Record it in flags for now, for legacy callers */
if (error == -ENOSPC)