linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails
@ 2018-05-18 12:34 Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines Jeff Layton
                   ` (10 more replies)
  0 siblings, 11 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Currently, syncfs does not return errors when one of the inodes fails
to be written back. It will return errors based on the legacy AS_EIO
and AS_ENOSPC flags when syncing out the block device fails, but that's
not particularly helpful for filesystems that aren't backed by a
blockdev and more granular errors are often not caught.

At LSF/MM this year, we had a discussion about tracking writeback
errors on a per-sb basis to help remedy this. This patchset is an inital
foray into doing that:

The basic idea is to add a new errseq_t to struct super_block, and use
that to track writeback errors for the filesystem as a whole.

Because syncfs is not a common operation, it's not desirable to grow
struct file in order to house an errseq_t cursor for it. Willy's
suggestion was to reuse file->f_wb_err, but only when the file is opened
with O_PATH. fsync always returns an error on an O_PATH fd, so this
should be safe, and seems semantically sane, given that syncfs operates
on the whole filesystem.

The patchset starts by pushing the __sync_blockdev calls down into the
various sync_fs operations. It then plumbs in an extra errseq_t pointer
into the sync_fs ops, and the relevant callers. It then adds the new
errseq_t field to struct super_block, and adds the machinery to handle
syncfs on an O_PATH open correctly. Lastly, it has some filesystems
return stored writeback errors in sync_fs ops when they are passed a
non-NULL errseq_t pointer.

This is just a start (more filesystems will need similar patches to the
later ones in the pile). This all works vs. the fstests testcase I have.
I haven't submitted that testcase yet, as I want to get consensus about
the interface. Does it make sense to treat O_PATH opens specially like
this?

Another thing on the wishlist for this is to expose the per-sb errseq_t
via another interface so that applications could periodically check to
see whether there has been a writeback error without syncing out the
whole fs. This patch doesn't add that yet, but once sb->s_wb_err is in
place and tracking errors it should be simple to do (possibly via a new
generic filesystem ioctl).

Jeff Layton (11):
  vfs: push __sync_blockdev calls down into sync_fs routines
  vfs: add a new errseq_t pointer to sync_fs prototype
  vfs: add an errseq_t pointer to sync_filesystem
  vfs: add errseq_t pointer to __sync_filesystem
  fs: track per-sb writeback errors and report them to syncfs
  buffer: record blockdev write errors in super_block that backs them
  ext4: have sync_fs op report writeback errors when passed a since
    pointer
  xfs: have sync_fs op report writeback errors when passed a since
    pointer
  btrfs: have sync_fs op report writeback errors when passed a since
    pointer
  ext2: have sync_fs op report writeback errors when passed a since
    pointer
  vfs: have call_sync_fs op report writeback errors when passed a since
    pointer

 drivers/staging/ncpfs/inode.c |  2 +-
 fs/adfs/super.c               |  2 +-
 fs/affs/super.c               |  6 +++---
 fs/befs/linuxvfs.c            |  2 +-
 fs/block_dev.c                |  3 ++-
 fs/btrfs/ctree.h              |  2 +-
 fs/btrfs/ioctl.c              |  2 +-
 fs/btrfs/super.c              | 28 +++++++++++++++++++---------
 fs/buffer.c                   |  2 ++
 fs/cachefiles/interface.c     |  2 +-
 fs/ceph/super.c               |  2 +-
 fs/cifs/cifsfs.c              |  2 +-
 fs/coda/inode.c               |  2 +-
 fs/cramfs/inode.c             |  2 +-
 fs/debugfs/inode.c            |  2 +-
 fs/efs/super.c                |  2 +-
 fs/exofs/super.c              |  2 +-
 fs/ext2/super.c               | 19 +++++++++++++------
 fs/ext4/super.c               | 19 +++++++++++--------
 fs/f2fs/f2fs.h                |  2 +-
 fs/f2fs/file.c                |  6 +++---
 fs/f2fs/namei.c               | 16 ++++++++--------
 fs/f2fs/segment.c             |  2 +-
 fs/f2fs/super.c               | 19 +++++++++++--------
 fs/fat/inode.c                |  2 +-
 fs/freevxfs/vxfs_super.c      |  2 +-
 fs/fuse/inode.c               |  2 +-
 fs/gfs2/super.c               |  8 +++++---
 fs/hfs/super.c                |  6 +++---
 fs/hfsplus/super.c            | 10 +++++-----
 fs/hpfs/super.c               |  2 +-
 fs/internal.h                 |  7 -------
 fs/isofs/inode.c              |  2 +-
 fs/jffs2/super.c              |  4 ++--
 fs/jfs/super.c                |  7 +++----
 fs/minix/inode.c              |  2 +-
 fs/nfs/super.c                |  2 +-
 fs/nilfs2/super.c             |  9 +++++----
 fs/ntfs/super.c               |  2 +-
 fs/ocfs2/super.c              |  8 ++++----
 fs/open.c                     |  6 +++---
 fs/openpromfs/inode.c         |  2 +-
 fs/overlayfs/super.c          |  4 ++--
 fs/proc/root.c                |  2 +-
 fs/pstore/inode.c             |  2 +-
 fs/qnx4/inode.c               |  2 +-
 fs/qnx6/inode.c               |  2 +-
 fs/quota/dquot.c              | 11 ++++-------
 fs/reiserfs/super.c           |  8 ++++----
 fs/romfs/super.c              |  2 +-
 fs/squashfs/super.c           |  2 +-
 fs/super.c                    |  4 ++--
 fs/sync.c                     | 28 +++++++++++++++++-----------
 fs/sysv/inode.c               |  7 +++----
 fs/tracefs/inode.c            |  2 +-
 fs/ubifs/super.c              |  4 ++--
 fs/udf/super.c                |  6 +++---
 fs/ufs/super.c                |  6 +++---
 fs/xfs/xfs_super.c            | 18 +++++++++++++-----
 include/linux/fs.h            | 35 +++++++++++++++++++++++++++++++++--
 include/linux/pagemap.h       |  5 ++++-
 61 files changed, 223 insertions(+), 158 deletions(-)

-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 15:56   ` Christoph Hellwig
  2018-05-18 12:34 ` [RFC PATCH 02/11] vfs: add a new errseq_t pointer to sync_fs prototype Jeff Layton
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

In later patches, we're going to want allow the sync_fs routine to
override the return value of __sync_blockdev in some situations.  This
call is pointless for filesystems that do not set sb->s_bdev, so we can
also make things slightly more efficient for those filesystems by not
calling it in them.

Export __sync_blockdev and push the calls to __sync_blockdev down into
the sync_fs routines.

Many older filesystems still rely on flushing out the bd_inode cache to
ensure that it's safely written to the backing store, so when sync_fs is
not defined, we do still call __sync_blockdev to support them.

While we're in here, add a call_sync_fs helper to encapsulate this
detail.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/affs/super.c     |  2 +-
 fs/block_dev.c      |  1 +
 fs/ext2/super.c     |  2 +-
 fs/ext4/super.c     |  9 +++++----
 fs/f2fs/super.c     | 15 +++++++++------
 fs/gfs2/super.c     |  4 +++-
 fs/hfs/super.c      |  2 +-
 fs/internal.h       |  7 -------
 fs/jfs/super.c      |  3 +--
 fs/nilfs2/super.c   |  5 +++--
 fs/ocfs2/super.c    |  2 +-
 fs/quota/dquot.c    |  9 +++------
 fs/reiserfs/super.c |  2 +-
 fs/sync.c           |  9 ++++-----
 fs/sysv/inode.c     |  3 +--
 fs/xfs/xfs_super.c  |  6 +++---
 include/linux/fs.h  | 18 ++++++++++++++++++
 17 files changed, 56 insertions(+), 43 deletions(-)

diff --git a/fs/affs/super.c b/fs/affs/super.c
index e602619aed9d..b76af8e3c87d 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -58,7 +58,7 @@ static int
 affs_sync_fs(struct super_block *sb, int wait)
 {
 	affs_commit_super(sb, wait);
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 static void flush_superblock(struct work_struct *work)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 7ec920e27065..8f1d13a3f02b 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -447,6 +447,7 @@ int __sync_blockdev(struct block_device *bdev, int wait)
 		return filemap_flush(bdev->bd_inode->i_mapping);
 	return filemap_write_and_wait(bdev->bd_inode->i_mapping);
 }
+EXPORT_SYMBOL(__sync_blockdev);
 
 /*
  * Write out and wait upon all the dirty data associated with a block
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index de1694512f1f..fd8536bc13da 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -1280,7 +1280,7 @@ static int ext2_sync_fs(struct super_block *sb, int wait)
 	}
 	spin_unlock(&sbi->s_lock);
 	ext2_sync_super(sb, es, wait);
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 static int ext2_freeze(struct super_block *sb)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index eb104e8476f0..ac2ffdbf54e6 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4857,13 +4857,13 @@ int ext4_force_commit(struct super_block *sb)
 
 static int ext4_sync_fs(struct super_block *sb, int wait)
 {
-	int ret = 0;
+	int ret = 0, ret2;
 	tid_t target;
 	bool needs_barrier = false;
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
 
 	if (unlikely(ext4_forced_shutdown(sbi)))
-		return 0;
+		goto out;
 
 	trace_ext4_sync_fs(sb, wait);
 	flush_workqueue(sbi->rsv_conversion_wq);
@@ -4896,8 +4896,9 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
 		if (!ret)
 			ret = err;
 	}
-
-	return ret;
+out:
+	ret2 = __sync_blockdev(sb->s_bdev, wait);
+	return ret ? ret : ret2;
 }
 
 /*
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 42d564c5ccd0..70fb16aac0bd 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1058,15 +1058,17 @@ static void f2fs_put_super(struct super_block *sb)
 int f2fs_sync_fs(struct super_block *sb, int sync)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-	int err = 0;
+	int err = 0, err2;
 
 	if (unlikely(f2fs_cp_error(sbi)))
-		return 0;
+		goto out;
 
 	trace_f2fs_sync_fs(sb, sync);
 
-	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
-		return -EAGAIN;
+	if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) {
+		err = -EAGAIN;
+		goto out;
+	}
 
 	if (sync) {
 		struct cp_control cpc;
@@ -1078,8 +1080,9 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
 		mutex_unlock(&sbi->gc_mutex);
 	}
 	f2fs_trace_ios(NULL, 1);
-
-	return err;
+out:
+	err2 = __sync_blockdev(sb->s_bdev, sync);
+	return err ? err : err2;
 }
 
 static int f2fs_freeze(struct super_block *sb)
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index cf5c7f3080d2..884dd8b7d7b3 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -951,13 +951,15 @@ static void gfs2_put_super(struct super_block *sb)
 
 static int gfs2_sync_fs(struct super_block *sb, int wait)
 {
+	int bderr;
 	struct gfs2_sbd *sdp = sb->s_fs_info;
 
 	gfs2_quota_sync(sb, -1);
 	if (wait)
 		gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
 			       GFS2_LFC_SYNC_FS);
-	return sdp->sd_log_error;
+	bderr = __sync_blockdev(sb->s_bdev, wait);
+	return sdp->sd_log_error ? sdp->sd_log_error : bderr;
 }
 
 void gfs2_freeze_func(struct work_struct *work)
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 173876782f73..9cb410ebab7c 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -33,7 +33,7 @@ MODULE_LICENSE("GPL");
 static int hfs_sync_fs(struct super_block *sb, int wait)
 {
 	hfs_mdb_commit(sb);
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 /*
diff --git a/fs/internal.h b/fs/internal.h
index e08972db0303..148b74293dfe 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -24,17 +24,10 @@ struct shrink_control;
 #ifdef CONFIG_BLOCK
 extern void __init bdev_cache_init(void);
 
-extern int __sync_blockdev(struct block_device *bdev, int wait);
-
 #else
 static inline void bdev_cache_init(void)
 {
 }
-
-static inline int __sync_blockdev(struct block_device *bdev, int wait)
-{
-	return 0;
-}
 #endif
 
 /*
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 1b9264fd54b6..c4b99ad53f9c 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -717,8 +717,7 @@ static int jfs_sync_fs(struct super_block *sb, int wait)
 		jfs_flush_journal(log, wait);
 		jfs_syncpt(log, 0);
 	}
-
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 static int jfs_show_options(struct seq_file *seq, struct dentry *root)
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 6ffeca84d7c3..280a28b62d13 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -495,7 +495,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
 {
 	struct the_nilfs *nilfs = sb->s_fs_info;
 	struct nilfs_super_block **sbp;
-	int err = 0;
+	int err = 0, bderr;
 
 	/* This function is called when super block should be written back */
 	if (wait)
@@ -514,7 +514,8 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
 	if (!err)
 		err = nilfs_flush_device(nilfs);
 
-	return err;
+	bderr = __sync_blockdev(sb->s_bdev, wait);
+	return err ? err : bderr;
 }
 
 int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt,
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 3415e0b09398..07a1a297c2ed 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -429,7 +429,7 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait)
 			jbd2_log_wait_commit(osb->journal->j_journal,
 					     target);
 	}
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 static int ocfs2_need_system_inode(struct ocfs2_super *osb, int ino)
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index d88231e3b2be..96522760826c 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -686,9 +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... */
-	if (sb->s_op->sync_fs)
-		sb->s_op->sync_fs(sb, 1);
-	sync_blockdev(sb->s_bdev);
+	call_sync_fs(sb, 1);
 
 	/*
 	 * Now when everything is written we can discard the pagecache so
@@ -2245,9 +2243,8 @@ 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). */
-	if (sb->s_op->sync_fs)
-		sb->s_op->sync_fs(sb, 1);
-	sync_blockdev(sb->s_bdev);
+	call_sync_fs(sb, 1);
+
 	/* Now the quota files are just ordinary files and we can set the
 	 * inode flags back. Moreover we discard the pagecache so that
 	 * userspace sees the writes we did bypassing the pagecache. We
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 1fc934d24459..b3a390eab9b7 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -78,7 +78,7 @@ static int reiserfs_sync_fs(struct super_block *s, int wait)
 		if (!journal_end_sync(&th))
 			reiserfs_flush_old_commits(s);
 	reiserfs_write_unlock(s);
-	return 0;
+	return __sync_blockdev(s->s_bdev, wait);
 }
 
 static void flush_old_commits(struct work_struct *work)
diff --git a/fs/sync.c b/fs/sync.c
index a863cd2490ce..44cdb38a8b67 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -35,9 +35,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)
 	else
 		writeback_inodes_sb(sb, WB_REASON_SYNC);
 
-	if (sb->s_op->sync_fs)
-		sb->s_op->sync_fs(sb, wait);
-	return __sync_blockdev(sb->s_bdev, wait);
+	return call_sync_fs(sb, wait);
 }
 
 /*
@@ -78,8 +76,9 @@ static void sync_fs_one_sb(struct super_block *sb, void *arg)
 {
 	int wait = arg ? 1 : 0;
 
-	if (!sb_rdonly(sb) && sb->s_op->sync_fs)
-		sb->s_op->sync_fs(sb, wait);
+	if (sb_rdonly(sb))
+		return;
+	call_sync_fs(sb, wait);
 }
 
 static void fdatawrite_one_bdev(struct block_device *bdev, void *arg)
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index bec9f79adb25..2232cf97840b 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -53,8 +53,7 @@ static int sysv_sync_fs(struct super_block *sb, int wait)
 	}
 
 	mutex_unlock(&sbi->s_lock);
-
-	return 0;
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 static int sysv_remount(struct super_block *sb, int *flags, char *data)
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index d71424052917..8683f8f2666f 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1097,7 +1097,7 @@ xfs_fs_sync_fs(
 	 * Doing anything during the async pass would be counterproductive.
 	 */
 	if (!wait)
-		return 0;
+		goto out;
 
 	xfs_log_force(mp, XFS_LOG_SYNC);
 	if (laptop_mode) {
@@ -1108,8 +1108,8 @@ xfs_fs_sync_fs(
 		 */
 		flush_delayed_work(&mp->m_log->l_work);
 	}
-
-	return 0;
+out:
+	return __sync_blockdev(sb->s_bdev, wait);
 }
 
 STATIC int
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6bccf323c01e..69b76f394954 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2441,6 +2441,7 @@ extern void bd_forget(struct inode *inode);
 extern void bdput(struct block_device *);
 extern void invalidate_bdev(struct block_device *);
 extern void iterate_bdevs(void (*)(struct block_device *, void *), void *);
+extern int __sync_blockdev(struct block_device *bdev, int wait);
 extern int sync_blockdev(struct block_device *bdev);
 extern void kill_bdev(struct block_device *);
 extern struct super_block *freeze_bdev(struct block_device *);
@@ -2461,6 +2462,11 @@ static inline int sync_blockdev(struct block_device *bdev) { return 0; }
 static inline void kill_bdev(struct block_device *bdev) {}
 static inline void invalidate_bdev(struct block_device *bdev) {}
 
+static inline int __sync_blockdev(struct block_device *bdev, int wait)
+{
+	return 0;
+}
+
 static inline struct super_block *freeze_bdev(struct block_device *sb)
 {
 	return NULL;
@@ -2488,6 +2494,18 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
 extern int sync_filesystem(struct super_block *);
 extern const struct file_operations def_blk_fops;
 extern const struct file_operations def_chr_fops;
+
+/*
+ * Many legacy filesystems don't have a sync_fs op. For them, we just flush
+ * the block device (if there is one).
+ */
+static inline int call_sync_fs(struct super_block *sb, int wait)
+{
+	if (sb->s_op->sync_fs)
+		return sb->s_op->sync_fs(sb, wait);
+	return __sync_blockdev(sb->s_bdev, wait);
+}
+
 #ifdef CONFIG_BLOCK
 extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long);
 extern int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 02/11] vfs: add a new errseq_t pointer to sync_fs prototype
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 03/11] vfs: add an errseq_t pointer to sync_filesystem Jeff Layton
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Plumb a new "since" pointer into the ->sync_fs operation. This will
eventually allow us to pass in an optional pointer to an errseq_t to
act as a cursor for error reporting.

For now it's ignored and all of the callers pass in NULL.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/affs/super.c      |  2 +-
 fs/btrfs/ctree.h     |  2 +-
 fs/btrfs/ioctl.c     |  2 +-
 fs/btrfs/super.c     |  2 +-
 fs/ceph/super.c      |  2 +-
 fs/exofs/super.c     |  2 +-
 fs/ext2/super.c      |  8 ++++----
 fs/ext4/super.c      |  4 ++--
 fs/f2fs/f2fs.h       |  2 +-
 fs/f2fs/file.c       |  6 +++---
 fs/f2fs/namei.c      | 16 ++++++++--------
 fs/f2fs/segment.c    |  2 +-
 fs/f2fs/super.c      |  4 ++--
 fs/gfs2/super.c      |  2 +-
 fs/hfs/super.c       |  2 +-
 fs/hfsplus/super.c   |  8 ++++----
 fs/jffs2/super.c     |  2 +-
 fs/jfs/super.c       |  2 +-
 fs/nilfs2/super.c    |  2 +-
 fs/ocfs2/super.c     |  4 ++--
 fs/overlayfs/super.c |  2 +-
 fs/quota/dquot.c     |  4 ++--
 fs/reiserfs/super.c  |  4 ++--
 fs/sync.c            |  4 ++--
 fs/sysv/inode.c      |  2 +-
 fs/ubifs/super.c     |  2 +-
 fs/udf/super.c       |  4 ++--
 fs/ufs/super.c       |  4 ++--
 fs/xfs/xfs_super.c   |  3 ++-
 include/linux/fs.h   |  7 ++++---
 30 files changed, 57 insertions(+), 55 deletions(-)

diff --git a/fs/affs/super.c b/fs/affs/super.c
index b76af8e3c87d..b85a10a930b4 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -55,7 +55,7 @@ affs_put_super(struct super_block *sb)
 }
 
 static int
-affs_sync_fs(struct super_block *sb, int wait)
+affs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	affs_commit_super(sb, wait);
 	return __sync_blockdev(sb->s_bdev, wait);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 2771cc56a622..58f1058a9012 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3316,7 +3316,7 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info);
 /* super.c */
 int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
 			unsigned long new_flags);
-int btrfs_sync_fs(struct super_block *sb, int wait);
+int btrfs_sync_fs(struct super_block *sb, int wait, errseq_t *since);
 
 static inline __printf(2, 3) __cold
 void btrfs_no_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 632e26d6f7ce..ed3bfd89d8c9 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -5500,7 +5500,7 @@ long btrfs_ioctl(struct file *file, unsigned int
 		ret = btrfs_start_delalloc_roots(fs_info, 0, -1);
 		if (ret)
 			return ret;
-		ret = btrfs_sync_fs(inode->i_sb, 1);
+		ret = btrfs_sync_fs(inode->i_sb, 1, NULL);
 		/*
 		 * The transaction thread may want to do more work,
 		 * namely it pokes the cleaner kthread that will start
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 0628092b0b1b..4a404c07acee 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1226,7 +1226,7 @@ static int btrfs_fill_super(struct super_block *sb,
 	return err;
 }
 
-int btrfs_sync_fs(struct super_block *sb, int wait)
+int btrfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index b33082e6878f..f8711d572743 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -103,7 +103,7 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
 }
 
 
-static int ceph_sync_fs(struct super_block *sb, int wait)
+static int ceph_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
 
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index 179cd5c2f52a..2c40233f5dc7 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -364,7 +364,7 @@ static const struct export_operations exofs_export_ops;
 /*
  * Write the superblock to the OSD
  */
-static int exofs_sync_fs(struct super_block *sb, int wait)
+static int exofs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct exofs_sb_info *sbi;
 	struct exofs_fscb *fscb;
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index fd8536bc13da..0366897bf02e 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -41,7 +41,7 @@
 static void ext2_write_super(struct super_block *sb);
 static int ext2_remount (struct super_block * sb, int * flags, char * data);
 static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf);
-static int ext2_sync_fs(struct super_block *sb, int wait);
+static int ext2_sync_fs(struct super_block *sb, int wait, errseq_t *since);
 static int ext2_freeze(struct super_block *sb);
 static int ext2_unfreeze(struct super_block *sb);
 
@@ -1262,7 +1262,7 @@ void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,
  * may have been checked while mounted and e2fsck may have
  * set s_state to EXT2_VALID_FS after some corrections.
  */
-static int ext2_sync_fs(struct super_block *sb, int wait)
+static int ext2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct ext2_sb_info *sbi = EXT2_SB(sb);
 	struct ext2_super_block *es = EXT2_SB(sb)->s_es;
@@ -1293,7 +1293,7 @@ static int ext2_freeze(struct super_block *sb)
 	 * consistent.
 	 */
 	if (atomic_long_read(&sb->s_remove_count)) {
-		ext2_sync_fs(sb, 1);
+		ext2_sync_fs(sb, 1, NULL);
 		return 0;
 	}
 	/* Set EXT2_FS_VALID flag */
@@ -1316,7 +1316,7 @@ static int ext2_unfreeze(struct super_block *sb)
 static void ext2_write_super(struct super_block *sb)
 {
 	if (!sb_rdonly(sb))
-		ext2_sync_fs(sb, 1);
+		ext2_sync_fs(sb, 1, NULL);
 }
 
 static int ext2_remount (struct super_block * sb, int * flags, char * data)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index ac2ffdbf54e6..e2b4956969b8 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -69,7 +69,7 @@ static void ext4_mark_recovery_complete(struct super_block *sb,
 					struct ext4_super_block *es);
 static void ext4_clear_journal_err(struct super_block *sb,
 				   struct ext4_super_block *es);
-static int ext4_sync_fs(struct super_block *sb, int wait);
+static int ext4_sync_fs(struct super_block *sb, int wait, errseq_t *since);
 static int ext4_remount(struct super_block *sb, int *flags, char *data);
 static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
 static int ext4_unfreeze(struct super_block *sb);
@@ -4855,7 +4855,7 @@ int ext4_force_commit(struct super_block *sb)
 	return ext4_journal_force_commit(journal);
 }
 
-static int ext4_sync_fs(struct super_block *sb, int wait)
+static int ext4_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	int ret = 0, ret2;
 	tid_t target;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1df7f10476d6..9a4356a1f3d4 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2703,7 +2703,7 @@ void f2fs_inode_synced(struct inode *inode);
 int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly);
 void f2fs_quota_off_umount(struct super_block *sb);
 int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
-int f2fs_sync_fs(struct super_block *sb, int sync);
+int f2fs_sync_fs(struct super_block *sb, int sync, errseq_t *since);
 extern __printf(3, 4)
 void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...);
 int sanity_check_ckpt(struct f2fs_sb_info *sbi);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 6b94f19b3fa8..a6c9e47b2729 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -260,7 +260,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
 
 	if (cp_reason) {
 		/* all the dirty node pages should be flushed for POR */
-		ret = f2fs_sync_fs(inode->i_sb, 1);
+		ret = f2fs_sync_fs(inode->i_sb, 1, NULL);
 
 		/*
 		 * We've secured consistency through sync_fs. Following pino
@@ -1869,7 +1869,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
 		break;
 	case F2FS_GOING_DOWN_METASYNC:
 		/* do checkpoint only */
-		ret = f2fs_sync_fs(sb, 1);
+		ret = f2fs_sync_fs(sb, 1, NULL);
 		if (ret)
 			goto out;
 		f2fs_stop_checkpoint(sbi, false);
@@ -2097,7 +2097,7 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
 	if (ret)
 		return ret;
 
-	ret = f2fs_sync_fs(sbi->sb, 1);
+	ret = f2fs_sync_fs(sbi->sb, 1, NULL);
 
 	mnt_drop_write_file(filp);
 	return ret;
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index d5098efe577c..62a3082ca6b6 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -298,7 +298,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 	unlock_new_inode(inode);
 
 	if (IS_DIRSYNC(dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 
 	f2fs_balance_fs(sbi, true);
 	return 0;
@@ -345,7 +345,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
 	d_instantiate(dentry, inode);
 
 	if (IS_DIRSYNC(dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 	return 0;
 out:
 	clear_inode_flag(inode, FI_INC_LINK);
@@ -531,7 +531,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
 	f2fs_unlock_op(sbi);
 
 	if (IS_DIRSYNC(dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 fail:
 	trace_f2fs_unlink_exit(inode, err);
 	return err;
@@ -614,7 +614,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 							disk_link.len - 1);
 
 		if (IS_DIRSYNC(dir))
-			f2fs_sync_fs(sbi->sb, 1);
+			f2fs_sync_fs(sbi->sb, 1, NULL);
 	} else {
 		f2fs_unlink(dir, dentry);
 	}
@@ -665,7 +665,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 	unlock_new_inode(inode);
 
 	if (IS_DIRSYNC(dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 
 	f2fs_balance_fs(sbi, true);
 	return 0;
@@ -717,7 +717,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
 	unlock_new_inode(inode);
 
 	if (IS_DIRSYNC(dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 
 	f2fs_balance_fs(sbi, true);
 	return 0;
@@ -979,7 +979,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	f2fs_unlock_op(sbi);
 
 	if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 	return 0;
 
 put_out_dir:
@@ -1132,7 +1132,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 	f2fs_unlock_op(sbi);
 
 	if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
-		f2fs_sync_fs(sbi->sb, 1);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 	return 0;
 out_new_dir:
 	if (new_dir_entry) {
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 5854cc4e1d67..d49bb295e0c6 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -507,7 +507,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
 			sync_dirty_inodes(sbi, FILE_INODE);
 			blk_finish_plug(&plug);
 		}
-		f2fs_sync_fs(sbi->sb, true);
+		f2fs_sync_fs(sbi->sb, 1, NULL);
 		stat_inc_bg_cp_count(sbi->stat_info);
 	}
 }
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 70fb16aac0bd..f5f6dc90a700 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1055,7 +1055,7 @@ static void f2fs_put_super(struct super_block *sb)
 	kfree(sbi);
 }
 
-int f2fs_sync_fs(struct super_block *sb, int sync)
+int f2fs_sync_fs(struct super_block *sb, int sync, errseq_t *since)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 	int err = 0, err2;
@@ -1497,7 +1497,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 
 		set_sbi_flag(sbi, SBI_IS_DIRTY);
 		set_sbi_flag(sbi, SBI_IS_CLOSE);
-		f2fs_sync_fs(sb, 1);
+		f2fs_sync_fs(sb, 1, NULL);
 		clear_sbi_flag(sbi, SBI_IS_CLOSE);
 	}
 
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 884dd8b7d7b3..f5a9f2fe7d7a 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -949,7 +949,7 @@ static void gfs2_put_super(struct super_block *sb)
  * Flushes the log to disk.
  */
 
-static int gfs2_sync_fs(struct super_block *sb, int wait)
+static int gfs2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	int bderr;
 	struct gfs2_sbd *sdp = sb->s_fs_info;
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 9cb410ebab7c..f8c9b745e33f 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -30,7 +30,7 @@ static struct kmem_cache *hfs_inode_cachep;
 
 MODULE_LICENSE("GPL");
 
-static int hfs_sync_fs(struct super_block *sb, int wait)
+static int hfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	hfs_mdb_commit(sb);
 	return __sync_blockdev(sb->s_bdev, wait);
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 513c357c734b..93f0e96527cf 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -172,7 +172,7 @@ static void hfsplus_evict_inode(struct inode *inode)
 	}
 }
 
-static int hfsplus_sync_fs(struct super_block *sb, int wait)
+static int hfsplus_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
 	struct hfsplus_vh *vhdr = sbi->s_vhdr;
@@ -254,7 +254,7 @@ static void delayed_sync_fs(struct work_struct *work)
 	sbi->work_queued = 0;
 	spin_unlock(&sbi->work_lock);
 
-	err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1);
+	err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1, NULL);
 	if (err)
 		pr_err("delayed sync fs err %d\n", err);
 }
@@ -291,7 +291,7 @@ static void hfsplus_put_super(struct super_block *sb)
 		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
 		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
 
-		hfsplus_sync_fs(sb, 1);
+		hfsplus_sync_fs(sb, 1, NULL);
 	}
 
 	hfs_btree_close(sbi->attr_tree);
@@ -545,7 +545,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 		be32_add_cpu(&vhdr->write_count, 1);
 		vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
 		vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
-		hfsplus_sync_fs(sb, 1);
+		hfsplus_sync_fs(sb, 1, NULL);
 
 		if (!sbi->hidden_dir) {
 			mutex_lock(&sbi->vh_mutex);
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 87bdf0f4cba1..ce773153c1a0 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -96,7 +96,7 @@ static int jffs2_show_options(struct seq_file *s, struct dentry *root)
 	return 0;
 }
 
-static int jffs2_sync_fs(struct super_block *sb, int wait)
+static int jffs2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index c4b99ad53f9c..722b786731d6 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -703,7 +703,7 @@ static struct dentry *jfs_do_mount(struct file_system_type *fs_type,
 	return mount_bdev(fs_type, flags, dev_name, data, jfs_fill_super);
 }
 
-static int jfs_sync_fs(struct super_block *sb, int wait)
+static int jfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct jfs_log *log = JFS_SBI(sb)->log;
 
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 280a28b62d13..4588ca0c7589 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -491,7 +491,7 @@ static void nilfs_put_super(struct super_block *sb)
 	sb->s_fs_info = NULL;
 }
 
-static int nilfs_sync_fs(struct super_block *sb, int wait)
+static int nilfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct the_nilfs *nilfs = sb->s_fs_info;
 	struct nilfs_super_block **sbp;
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 07a1a297c2ed..5eac259e3899 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -115,7 +115,7 @@ static void ocfs2_delete_osb(struct ocfs2_super *osb);
 
 static int ocfs2_statfs(struct dentry *dentry, struct kstatfs *buf);
 
-static int ocfs2_sync_fs(struct super_block *sb, int wait);
+static int ocfs2_sync_fs(struct super_block *sb, int wait, errseq_t *since);
 
 static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb);
 static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb);
@@ -406,7 +406,7 @@ static const struct file_operations ocfs2_osb_debug_fops = {
 	.llseek =	generic_file_llseek,
 };
 
-static int ocfs2_sync_fs(struct super_block *sb, int wait)
+static int ocfs2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	int status;
 	tid_t target;
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index e8551c97de51..f660e0e8430d 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -266,7 +266,7 @@ static void ovl_put_super(struct super_block *sb)
 }
 
 /* Sync real dirty inodes in upper filesystem (if it exists) */
-static int ovl_sync_fs(struct super_block *sb, int wait)
+static int ovl_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct ovl_fs *ofs = sb->s_fs_info;
 	struct super_block *upper_sb;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 96522760826c..e9cafa4766c5 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -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... */
-	call_sync_fs(sb, 1);
+	call_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). */
-	call_sync_fs(sb, 1);
+	call_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
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index b3a390eab9b7..490f5bca544e 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -64,7 +64,7 @@ static int is_any_reiserfs_magic_string(struct reiserfs_super_block *rs)
 static int reiserfs_remount(struct super_block *s, int *flags, char *data);
 static int reiserfs_statfs(struct dentry *dentry, struct kstatfs *buf);
 
-static int reiserfs_sync_fs(struct super_block *s, int wait)
+static int reiserfs_sync_fs(struct super_block *s, int wait, errseq_t *since)
 {
 	struct reiserfs_transaction_handle th;
 
@@ -108,7 +108,7 @@ static void flush_old_commits(struct work_struct *work)
 		sbi->work_queued = 0;
 	spin_unlock(&sbi->old_work_lock);
 
-	reiserfs_sync_fs(s, 1);
+	reiserfs_sync_fs(s, 1, NULL);
 	up_read(&s->s_umount);
 }
 
diff --git a/fs/sync.c b/fs/sync.c
index 44cdb38a8b67..b512c85bc9c5 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -35,7 +35,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)
 	else
 		writeback_inodes_sb(sb, WB_REASON_SYNC);
 
-	return call_sync_fs(sb, wait);
+	return call_sync_fs(sb, wait, NULL);
 }
 
 /*
@@ -78,7 +78,7 @@ static void sync_fs_one_sb(struct super_block *sb, void *arg)
 
 	if (sb_rdonly(sb))
 		return;
-	call_sync_fs(sb, wait);
+	call_sync_fs(sb, wait, NULL);
 }
 
 static void fdatawrite_one_bdev(struct block_device *bdev, void *arg)
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 2232cf97840b..f202f3d73772 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -32,7 +32,7 @@
 #include <asm/byteorder.h>
 #include "sysv.h"
 
-static int sysv_sync_fs(struct super_block *sb, int wait)
+static int sysv_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct sysv_sb_info *sbi = SYSV_SB(sb);
 	unsigned long time = get_seconds(), old_time;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 6c397a389105..4faaf6f9fdbc 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -449,7 +449,7 @@ static int ubifs_show_options(struct seq_file *s, struct dentry *root)
 	return 0;
 }
 
-static int ubifs_sync_fs(struct super_block *sb, int wait)
+static int ubifs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	int i, err;
 	struct ubifs_info *c = sb->s_fs_info;
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 7949c338efa5..12cdc9d5acf0 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -89,7 +89,7 @@ enum { UDF_MAX_LINKS = 0xffff };
 /* These are the "meat" - everything else is stuffing */
 static int udf_fill_super(struct super_block *, void *, int);
 static void udf_put_super(struct super_block *);
-static int udf_sync_fs(struct super_block *, int);
+static int udf_sync_fs(struct super_block *, int, errseq_t *);
 static int udf_remount_fs(struct super_block *, int *, char *);
 static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad);
 static int udf_find_fileset(struct super_block *, struct kernel_lb_addr *,
@@ -2345,7 +2345,7 @@ static void udf_put_super(struct super_block *sb)
 	sb->s_fs_info = NULL;
 }
 
-static int udf_sync_fs(struct super_block *sb, int wait)
+static int udf_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct udf_sb_info *sbi = UDF_SB(sb);
 
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 8254b8b3690f..e3f26b566f11 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -680,7 +680,7 @@ static void ufs_put_super_internal(struct super_block *sb)
 	UFSD("EXIT\n");
 }
 
-static int ufs_sync_fs(struct super_block *sb, int wait)
+static int ufs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
 	struct ufs_sb_private_info * uspi;
 	struct ufs_super_block_first * usb1;
@@ -720,7 +720,7 @@ static void delayed_sync_fs(struct work_struct *work)
 	sbi->work_queued = 0;
 	spin_unlock(&sbi->work_lock);
 
-	ufs_sync_fs(sbi->sb, 1);
+	ufs_sync_fs(sbi->sb, 1, NULL);
 }
 
 void ufs_mark_sb_dirty(struct super_block *sb)
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 8683f8f2666f..1ce4b936c716 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1089,7 +1089,8 @@ xfs_free_fsname(
 STATIC int
 xfs_fs_sync_fs(
 	struct super_block	*sb,
-	int			wait)
+	int			wait,
+	errseq_t		*since)
 {
 	struct xfs_mount	*mp = XFS_M(sb);
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 69b76f394954..258877074ff4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1821,7 +1821,7 @@ struct super_operations {
 	int (*drop_inode) (struct inode *);
 	void (*evict_inode) (struct inode *);
 	void (*put_super) (struct super_block *);
-	int (*sync_fs)(struct super_block *sb, int wait);
+	int (*sync_fs)(struct super_block *sb, int wait, errseq_t *since);
 	int (*freeze_super) (struct super_block *);
 	int (*freeze_fs) (struct super_block *);
 	int (*thaw_super) (struct super_block *);
@@ -2499,10 +2499,11 @@ extern const struct file_operations def_chr_fops;
  * Many legacy filesystems don't have a sync_fs op. For them, we just flush
  * the block device (if there is one).
  */
-static inline int call_sync_fs(struct super_block *sb, int wait)
+static inline int call_sync_fs(struct super_block *sb, int wait,
+			       errseq_t *since)
 {
 	if (sb->s_op->sync_fs)
-		return sb->s_op->sync_fs(sb, wait);
+		return sb->s_op->sync_fs(sb, wait, since);
 	return __sync_blockdev(sb->s_bdev, wait);
 }
 
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 03/11] vfs: add an errseq_t pointer to sync_filesystem
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 02/11] vfs: add a new errseq_t pointer to sync_fs prototype Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 04/11] vfs: add errseq_t pointer to __sync_filesystem Jeff Layton
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Allow sync_filesystem to take an errseq_t for the purposes of error
reporting. This value is just passed directly to the ->sync_fs op.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 drivers/staging/ncpfs/inode.c | 2 +-
 fs/adfs/super.c               | 2 +-
 fs/affs/super.c               | 2 +-
 fs/befs/linuxvfs.c            | 2 +-
 fs/block_dev.c                | 2 +-
 fs/btrfs/super.c              | 4 ++--
 fs/cachefiles/interface.c     | 2 +-
 fs/cifs/cifsfs.c              | 2 +-
 fs/coda/inode.c               | 2 +-
 fs/cramfs/inode.c             | 2 +-
 fs/debugfs/inode.c            | 2 +-
 fs/efs/super.c                | 2 +-
 fs/ext2/super.c               | 2 +-
 fs/ext4/super.c               | 4 ++--
 fs/fat/inode.c                | 2 +-
 fs/freevxfs/vxfs_super.c      | 2 +-
 fs/fuse/inode.c               | 2 +-
 fs/gfs2/super.c               | 2 +-
 fs/hfs/super.c                | 2 +-
 fs/hfsplus/super.c            | 2 +-
 fs/hpfs/super.c               | 2 +-
 fs/isofs/inode.c              | 2 +-
 fs/jffs2/super.c              | 2 +-
 fs/jfs/super.c                | 2 +-
 fs/minix/inode.c              | 2 +-
 fs/nfs/super.c                | 2 +-
 fs/nilfs2/super.c             | 2 +-
 fs/ntfs/super.c               | 2 +-
 fs/ocfs2/super.c              | 2 +-
 fs/openpromfs/inode.c         | 2 +-
 fs/overlayfs/super.c          | 2 +-
 fs/proc/root.c                | 2 +-
 fs/pstore/inode.c             | 2 +-
 fs/qnx4/inode.c               | 2 +-
 fs/qnx6/inode.c               | 2 +-
 fs/quota/dquot.c              | 2 +-
 fs/reiserfs/super.c           | 2 +-
 fs/romfs/super.c              | 2 +-
 fs/squashfs/super.c           | 2 +-
 fs/super.c                    | 4 ++--
 fs/sync.c                     | 4 ++--
 fs/sysv/inode.c               | 2 +-
 fs/tracefs/inode.c            | 2 +-
 fs/ubifs/super.c              | 2 +-
 fs/udf/super.c                | 2 +-
 fs/ufs/super.c                | 2 +-
 fs/xfs/xfs_super.c            | 2 +-
 include/linux/fs.h            | 2 +-
 48 files changed, 52 insertions(+), 52 deletions(-)

diff --git a/drivers/staging/ncpfs/inode.c b/drivers/staging/ncpfs/inode.c
index bb411610a071..1ba23c4b8ddd 100644
--- a/drivers/staging/ncpfs/inode.c
+++ b/drivers/staging/ncpfs/inode.c
@@ -103,7 +103,7 @@ static void destroy_inodecache(void)
 
 static int ncp_remount(struct super_block *sb, int *flags, char* data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NODIRATIME;
 	return 0;
 }
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index cfda2c7caedc..695cf6413612 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -212,7 +212,7 @@ static int parse_options(struct super_block *sb, char *options)
 
 static int adfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NODIRATIME;
 	return parse_options(sb, data);
 }
diff --git a/fs/affs/super.c b/fs/affs/super.c
index b85a10a930b4..bee2cdbd942c 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -570,7 +570,7 @@ affs_remount(struct super_block *sb, int *flags, char *data)
 
 	pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data);
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NODIRATIME;
 
 	memcpy(volume, sbi->s_volume, 32);
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index af2832aaeec5..ffa63673f883 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -951,7 +951,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
 static int
 befs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (!(*flags & SB_RDONLY))
 		return -EINVAL;
 	return 0;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 8f1d13a3f02b..46fd96344d08 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -468,7 +468,7 @@ int fsync_bdev(struct block_device *bdev)
 {
 	struct super_block *sb = get_super(bdev);
 	if (sb) {
-		int res = sync_filesystem(sb);
+		int res = sync_filesystem(sb, NULL);
 		drop_super(sb);
 		return res;
 	}
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4a404c07acee..4b3e6676be9d 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1732,7 +1732,7 @@ static inline void btrfs_remount_begin(struct btrfs_fs_info *fs_info,
 		wait_event(fs_info->transaction_wait,
 			   (atomic_read(&fs_info->defrag_running) == 0));
 		if (flags & SB_RDONLY)
-			sync_filesystem(fs_info->sb);
+			sync_filesystem(fs_info->sb, NULL);
 	}
 }
 
@@ -1763,7 +1763,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	u32 old_metadata_ratio = fs_info->metadata_ratio;
 	int ret;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	btrfs_remount_prepare(fs_info);
 
 	if (data) {
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c
index 222bc5d8b62c..777468fe8afd 100644
--- a/fs/cachefiles/interface.c
+++ b/fs/cachefiles/interface.c
@@ -391,7 +391,7 @@ static void cachefiles_sync_cache(struct fscache_cache *_cache)
 	 * written to disc */
 	cachefiles_begin_secure(cache, &saved_cred);
 	down_read(&cache->mnt->mnt_sb->s_umount);
-	ret = sync_filesystem(cache->mnt->mnt_sb);
+	ret = sync_filesystem(cache->mnt->mnt_sb, NULL);
 	up_read(&cache->mnt->mnt_sb->s_umount);
 	cachefiles_end_secure(cache, saved_cred);
 
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5a5a0158cc8f..82a4d2f5f4ed 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -574,7 +574,7 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)
 
 static int cifs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NODIRATIME;
 	return 0;
 }
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 97424cf206c0..4c5a87b1ac05 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -95,7 +95,7 @@ void coda_destroy_inodecache(void)
 
 static int coda_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NOATIME;
 	return 0;
 }
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 017b0ab19bc4..a9ad8f459bbd 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -504,7 +504,7 @@ static void cramfs_kill_sb(struct super_block *sb)
 
 static int cramfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 13b01351dd1c..129af6748d94 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -135,7 +135,7 @@ static int debugfs_remount(struct super_block *sb, int *flags, char *data)
 	int err;
 	struct debugfs_fs_info *fsi = sb->s_fs_info;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	err = debugfs_parse_options(data, &fsi->mount_opts);
 	if (err)
 		goto fail;
diff --git a/fs/efs/super.c b/fs/efs/super.c
index 6ffb7ba1547a..d558914da832 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -115,7 +115,7 @@ static void destroy_inodecache(void)
 
 static int efs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 0366897bf02e..0700f3264c7e 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -1326,7 +1326,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
 	struct ext2_mount_options new_opts;
 	int err;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	spin_lock(&sbi->s_lock);
 	new_opts.s_mount_opt = sbi->s_mount_opt;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e2b4956969b8..896ddf8c3421 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -5094,7 +5094,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 		}
 
 		if (*flags & SB_RDONLY) {
-			err = sync_filesystem(sb);
+			err = sync_filesystem(sb, NULL);
 			if (err < 0)
 				goto restore_opts;
 			err = dquot_suspend(sb, -1);
@@ -5590,7 +5590,7 @@ static int ext4_quota_off(struct super_block *sb, int type)
 	/* Force all delayed allocation blocks to be allocated.
 	 * Caller already holds s_umount sem */
 	if (test_opt(sb, DELALLOC))
-		sync_filesystem(sb);
+		sync_filesystem(sb, NULL);
 
 	if (!inode || !igrab(inode))
 		goto out;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index ffbbf0520d9e..1fd6b8516523 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -784,7 +784,7 @@ static int fat_remount(struct super_block *sb, int *flags, char *data)
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	*flags |= SB_NODIRATIME | (sbi->options.isvfat ? 0 : SB_NOATIME);
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	/* make sure we update state on remount. */
 	new_rdonly = *flags & SB_RDONLY;
diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c
index 48b24bb50d02..11362c61408a 100644
--- a/fs/freevxfs/vxfs_super.c
+++ b/fs/freevxfs/vxfs_super.c
@@ -115,7 +115,7 @@ vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp)
 
 static int vxfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ef309958e060..44c391b74008 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -140,7 +140,7 @@ static void fuse_evict_inode(struct inode *inode)
 
 static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (*flags & SB_MANDLOCK)
 		return -EINVAL;
 
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index f5a9f2fe7d7a..d65621c8580e 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1241,7 +1241,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
 	struct gfs2_tune *gt = &sdp->sd_tune;
 	int error;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	spin_lock(&gt->gt_spin);
 	args.ar_commit = gt->gt_logd_secs;
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index f8c9b745e33f..cdfee76200b6 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -113,7 +113,7 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int hfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NODIRATIME;
 	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
 		return 0;
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 93f0e96527cf..87952852bba2 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -328,7 +328,7 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
 		return 0;
 	if (!(*flags & SB_RDONLY)) {
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index f2c3ebcd309c..0db5184a5635 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -454,7 +454,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
 	int o;
 	struct hpfs_sb_info *sbi = hpfs_sb(s);
 
-	sync_filesystem(s);
+	sync_filesystem(s, NULL);
 
 	*flags |= SB_NOATIME;
 
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index ec3fba7d492f..941f481aeb13 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -113,7 +113,7 @@ static void destroy_inodecache(void)
 
 static int isofs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (!(*flags & SB_RDONLY))
 		return -EROFS;
 	return 0;
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index ce773153c1a0..bf438c3538c5 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -243,7 +243,7 @@ static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 	int err;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	err = jffs2_parse_options(c, data);
 	if (err)
 		return -EINVAL;
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 722b786731d6..4c071086bde3 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -463,7 +463,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
 	int flag = JFS_SBI(sb)->flag;
 	int ret;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (!parse_options(data, sb, &newLVSize, &flag))
 		return -EINVAL;
 
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 72e308c3e66b..286022e07afd 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -123,7 +123,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data)
 	struct minix_sb_info * sbi = minix_sb(sb);
 	struct minix_super_block * ms;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	ms = sbi->s_ms;
 	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
 		return 0;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 5e470e233c83..02985cde5ff9 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2252,7 +2252,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
 	struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;
 	u32 nfsvers = nfss->nfs_client->rpc_ops->version;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	/*
 	 * Userspace mount programs that send binary options generally send
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 4588ca0c7589..7d7bf435996d 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -1126,7 +1126,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data)
 	unsigned long old_mount_opt;
 	int err;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	old_sb_flags = sb->s_flags;
 	old_mount_opt = nilfs->ns_mount_opt;
 
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index bb7159f697f2..64ce51f19aa2 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -469,7 +469,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
 
 	ntfs_debug("Entering with remount options string: %s", opt);
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 #ifndef NTFS_RW
 	/* For read-only compiled driver, enforce read-only flag. */
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 5eac259e3899..1e58bff8655b 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -641,7 +641,7 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
 	struct ocfs2_super *osb = OCFS2_SB(sb);
 	u32 tmp;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	if (!ocfs2_parse_options(sb, data, &parsed_options, 1) ||
 	    !ocfs2_check_set_options(sb, &parsed_options)) {
diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 2200662a9bf1..e9ec9d24535a 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -368,7 +368,7 @@ static struct inode *openprom_iget(struct super_block *sb, ino_t ino)
 
 static int openprom_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_NOATIME;
 	return 0;
 }
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index f660e0e8430d..bc8e0c403fbf 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -289,7 +289,7 @@ static int ovl_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 	upper_sb = ofs->upper_mnt->mnt_sb;
 
 	down_read(&upper_sb->s_umount);
-	ret = sync_filesystem(upper_sb);
+	ret = sync_filesystem(upper_sb, NULL);
 	up_read(&upper_sb->s_umount);
 
 	return ret;
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 61b7340b357a..e9f7608d9b9b 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -82,7 +82,7 @@ int proc_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct pid_namespace *pid = sb->s_fs_info;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	return !proc_parse_options(data, pid);
 }
 
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 5fcb845b9fec..617562d45614 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -273,7 +273,7 @@ static int pstore_show_options(struct seq_file *m, struct dentry *root)
 
 static int pstore_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	parse_options(data);
 
 	return 0;
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 3d46fe302fcb..94028eccaa15 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -44,7 +44,7 @@ static int qnx4_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct qnx4_sb_info *qs;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	qs = qnx4_sb(sb);
 	qs->Version = QNX4_VERSION;
 	*flags |= SB_RDONLY;
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 4aeb26bcb4d0..1c1c31d780c6 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -55,7 +55,7 @@ static int qnx6_show_options(struct seq_file *seq, struct dentry *root)
 
 static int qnx6_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index e9cafa4766c5..f6f14d6d58c0 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -2340,7 +2340,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
 		 * the quota file since if blocksize < pagesize, invalidation
 		 * of the cache could fail because of other unrelated dirty
 		 * data */
-		sync_filesystem(sb);
+		sync_filesystem(sb, NULL);
 		invalidate_bdev(sb->s_bdev);
 	}
 
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 490f5bca544e..b1798819f650 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -1454,7 +1454,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
 	if (arg && !new_opts)
 		return -ENOMEM;
 
-	sync_filesystem(s);
+	sync_filesystem(s, NULL);
 	reiserfs_write_lock(s);
 
 #ifdef CONFIG_QUOTA
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 8f06fd1f3d69..afe6d8fbd4bf 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -450,7 +450,7 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
  */
 static int romfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 8a73b97217c8..074734c26006 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -372,7 +372,7 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
 static int squashfs_remount(struct super_block *sb, int *flags, char *data)
 {
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	*flags |= SB_RDONLY;
 	return 0;
 }
diff --git a/fs/super.c b/fs/super.c
index f16e3842faf7..4b4b56a501fd 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -426,7 +426,7 @@ void generic_shutdown_super(struct super_block *sb)
 
 	if (sb->s_root) {
 		shrink_dcache_for_umount(sb);
-		sync_filesystem(sb);
+		sync_filesystem(sb, NULL);
 		sb->s_flags &= ~SB_ACTIVE;
 
 		fsnotify_unmount_inodes(sb);
@@ -1466,7 +1466,7 @@ int freeze_super(struct super_block *sb)
 	sb_wait_write(sb, SB_FREEZE_PAGEFAULT);
 
 	/* All writers are done so after syncing there won't be dirty data */
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 
 	/* Now wait for internal filesystem counter */
 	sb->s_writers.frozen = SB_FREEZE_FS;
diff --git a/fs/sync.c b/fs/sync.c
index b512c85bc9c5..fc0b7bd51ae6 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -43,7 +43,7 @@ static int __sync_filesystem(struct super_block *sb, int wait)
  * superblock.  Filesystem data as well as the underlying block
  * device.  Takes the superblock lock.
  */
-int sync_filesystem(struct super_block *sb)
+int sync_filesystem(struct super_block *sb, errseq_t *since)
 {
 	int ret;
 
@@ -169,7 +169,7 @@ SYSCALL_DEFINE1(syncfs, int, fd)
 	sb = f.file->f_path.dentry->d_sb;
 
 	down_read(&sb->s_umount);
-	ret = sync_filesystem(sb);
+	ret = sync_filesystem(sb, NULL);
 	up_read(&sb->s_umount);
 
 	fdput(f);
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index f202f3d73772..6e166f65b03e 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -60,7 +60,7 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct sysv_sb_info *sbi = SYSV_SB(sb);
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (sbi->s_forced_ro)
 		*flags |= SB_RDONLY;
 	return 0;
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index bea8ad876bf9..d70303524079 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -230,7 +230,7 @@ static int tracefs_remount(struct super_block *sb, int *flags, char *data)
 	int err;
 	struct tracefs_fs_info *fsi = sb->s_fs_info;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	err = tracefs_parse_options(data, &fsi->mount_opts);
 	if (err)
 		goto fail;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 4faaf6f9fdbc..b797158931a7 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1847,7 +1847,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
 	int err;
 	struct ubifs_info *c = sb->s_fs_info;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags);
 
 	err = ubifs_parse_options(c, data, 1);
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 12cdc9d5acf0..761962ddc245 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -617,7 +617,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
 	int error = 0;
 	struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	if (lvidiu) {
 		int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev);
 		if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & SB_RDONLY))
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index e3f26b566f11..ac2d4d271826 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1303,7 +1303,7 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
 	unsigned new_mount_opt, ufstype;
 	unsigned flags;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	mutex_lock(&UFS_SB(sb)->s_lock);
 	uspi = UFS_SB(sb)->s_uspi;
 	flags = UFS_SB(sb)->s_flags;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 1ce4b936c716..9255de2767b4 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1282,7 +1282,7 @@ xfs_fs_remount(
 	if (error)
 		return error;
 
-	sync_filesystem(sb);
+	sync_filesystem(sb, NULL);
 	while ((p = strsep(&options, ",")) != NULL) {
 		int token;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 258877074ff4..d40fe9ead5fb 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2491,7 +2491,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
 	return false;
 }
 #endif
-extern int sync_filesystem(struct super_block *);
+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;
 
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 04/11] vfs: add errseq_t pointer to __sync_filesystem
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (2 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 03/11] vfs: add an errseq_t pointer to sync_filesystem Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 05/11] fs: track per-sb writeback errors and report them to syncfs Jeff Layton
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

...and pass that to the sync_fs operation. Also, have __sync_filesystem
return the error from sync_fs (if there is one).

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/sync.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/fs/sync.c b/fs/sync.c
index fc0b7bd51ae6..c876fa414cae 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -28,14 +28,16 @@
  * wait == 1 case since in that case write_inode() functions do
  * sync_dirty_buffer() and thus effectively write one block at a time.
  */
-static int __sync_filesystem(struct super_block *sb, int wait)
+static int __sync_filesystem(struct super_block *sb, int wait, errseq_t *since)
 {
+	int ret = 0;
+
 	if (wait)
 		sync_inodes_sb(sb);
 	else
 		writeback_inodes_sb(sb, WB_REASON_SYNC);
 
-	return call_sync_fs(sb, wait, NULL);
+	return call_sync_fs(sb, wait, since);
 }
 
 /*
@@ -59,10 +61,10 @@ int sync_filesystem(struct super_block *sb, errseq_t *since)
 	if (sb_rdonly(sb))
 		return 0;
 
-	ret = __sync_filesystem(sb, 0);
+	ret = __sync_filesystem(sb, 0, since);
 	if (ret < 0)
 		return ret;
-	return __sync_filesystem(sb, 1);
+	return __sync_filesystem(sb, 1, since);
 }
 EXPORT_SYMBOL(sync_filesystem);
 
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 05/11] fs: track per-sb writeback errors and report them to syncfs
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (3 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 04/11] vfs: add errseq_t pointer to __sync_filesystem Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 06/11] buffer: record blockdev write errors in super_block that backs them Jeff Layton
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Usually we suggest that applications call fsync when they want to
ensure that all data written to the file has made it to the backing
store, but that can be inefficient when there are a lot of open
files.

Calling syncfs on the filesystem may be more efficient, but the error
reporting doesn't currently work the way most people expect. If a single
inode on a filesystem reports a writeback error, syncfs won't return an
error. syncfs only returns an error if __sync_blockdev fails.

It would be better if it reported an error if there were any writeback
failures. Then applications could call syncfs to see if there are any
errors on any open files, and could then call fsync on all of the other
descriptors to figure out which one failed.

This patch implements a suggestion from Willy to remedy this. It adds a
new errseq_t to struct super_block, and has mapping_set_error also
record writeback errors there.

To report errors recorded there, we also need to keep an errseq_t for in
struct file to act as a cursor, but growing struct file for this purpose
is undesirable. We could just reuse f_wb_err, but someone could mix
calls to fsync and syncfs and that would break things.

As an alternative, this patch only has syncfs report errors recorded
in s_wb_err when the file has been opened with O_PATH. Any file opened
with O_PATH will not have its fsync field defined in its
file_operations so we can be sure that nothing else will be using its
f_wb_err field.

Note that calling syncfs on an O_PATH descriptor today will return
-EBADF, so this scheme gives userland a way to tell whether this
mechanism will work at runtime.

Cc: Andres Freund <andres@anarazel.de>
Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/open.c               | 6 +++---
 fs/sync.c               | 9 +++++++--
 include/linux/fs.h      | 3 +++
 include/linux/pagemap.h | 5 ++++-
 4 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index c5ee7cd60424..3e8c7b16abb8 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -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))
diff --git a/fs/sync.c b/fs/sync.c
index c876fa414cae..f2400d13a65f 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -162,16 +162,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);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index d40fe9ead5fb..fecd29325f36 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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;
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index b1bd2186e6d2..2de87c5a2718 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -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)
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 06/11] buffer: record blockdev write errors in super_block that backs them
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (4 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 05/11] fs: track per-sb writeback errors and report them to syncfs Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer Jeff Layton
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

When syncing out a block device (a'la __sync_blockdev), any error
encountered will only be recorded in the bd_inode's mapping. When the
blockdev contains a filesystem however, we'd like to also record the
error in the super_block that's stored there.

Change mark_buffer_write_io_error to record the error in the
corresponding super_block when a writeback error occurs and the block
device contains a mounted superblock.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/buffer.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/buffer.c b/fs/buffer.c
index 249b83fafe48..dae2a857d5bc 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1117,6 +1117,8 @@ void mark_buffer_write_io_error(struct buffer_head *bh)
 		mapping_set_error(bh->b_page->mapping, -EIO);
 	if (bh->b_assoc_map)
 		mapping_set_error(bh->b_assoc_map, -EIO);
+	if (bh->b_bdev->bd_super)
+		errseq_set(&bh->b_bdev->bd_super->s_wb_err, -EIO);
 }
 EXPORT_SYMBOL(mark_buffer_write_io_error);
 
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (5 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 06/11] buffer: record blockdev write errors in super_block that backs them Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 15:22   ` Matthew Wilcox
  2018-05-18 12:34 ` [RFC PATCH 08/11] xfs: " Jeff Layton
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

When ext4_sync_fs gets a non-NULL since pointer, use it to report
errors vs. the errseq_t in the super_block. This allows us to
properly report an error to sync_fs when any inode has failed writeback
since we last checked for it.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/ext4/super.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 896ddf8c3421..a5f41d31611f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4898,6 +4898,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 	}
 out:
 	ret2 = __sync_blockdev(sb->s_bdev, wait);
+	if (since)
+		ret2 = errseq_check_and_advance(&sb->s_wb_err, since);
 	return ret ? ret : ret2;
 }
 
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 08/11] xfs: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (6 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-21 23:01   ` Dave Chinner
  2018-05-18 12:34 ` [RFC PATCH 09/11] btrfs: " Jeff Layton
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/xfs/xfs_super.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 9255de2767b4..7dc847f48f9f 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1092,6 +1092,7 @@ xfs_fs_sync_fs(
 	int			wait,
 	errseq_t		*since)
 {
+	int			ret;
 	struct xfs_mount	*mp = XFS_M(sb);
 
 	/*
@@ -1110,7 +1111,13 @@ xfs_fs_sync_fs(
 		flush_delayed_work(&mp->m_log->l_work);
 	}
 out:
-	return __sync_blockdev(sb->s_bdev, 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;
 }
 
 STATIC int
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 09/11] btrfs: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (7 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 08/11] xfs: " Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 10/11] ext2: " Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 11/11] vfs: have call_sync_fs " Jeff Layton
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/btrfs/super.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4b3e6676be9d..eac785459f10 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1231,12 +1231,13 @@ int btrfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 	struct btrfs_trans_handle *trans;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
 	struct btrfs_root *root = fs_info->tree_root;
+	int ret = 0;
 
 	trace_btrfs_sync_fs(fs_info, wait);
 
 	if (!wait) {
 		filemap_flush(fs_info->btree_inode->i_mapping);
-		return 0;
+		goto out;
 	}
 
 	btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1);
@@ -1250,7 +1251,7 @@ int btrfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 			 * that need to go through commit
 			 */
 			if (fs_info->pending_changes == 0)
-				return 0;
+				goto out;
 			/*
 			 * A non-blocking test if the fs is frozen. We must not
 			 * start a new transaction here otherwise a deadlock
@@ -1260,13 +1261,22 @@ int btrfs_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 			if (sb_start_write_trylock(sb))
 				sb_end_write(sb);
 			else
-				return 0;
+				goto out;
 			trans = btrfs_start_transaction(root, 0);
 		}
-		if (IS_ERR(trans))
-			return PTR_ERR(trans);
+		if (IS_ERR(trans)) {
+			ret = PTR_ERR(trans);
+			goto out;
+		}
 	}
-	return btrfs_commit_transaction(trans);
+	ret = btrfs_commit_transaction(trans);
+out:
+	if (since) {
+		int ret2 = errseq_check_and_advance(&sb->s_wb_err, since);
+		if (ret == 0)
+			ret = ret2;
+	}
+	return ret;
 }
 
 static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 10/11] ext2: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (8 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 09/11] btrfs: " Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  2018-05-18 12:34 ` [RFC PATCH 11/11] vfs: have call_sync_fs " Jeff Layton
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/ext2/super.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 0700f3264c7e..51b548cfebb1 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -1264,6 +1264,7 @@ void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,
  */
 static int ext2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 {
+	int ret;
 	struct ext2_sb_info *sbi = EXT2_SB(sb);
 	struct ext2_super_block *es = EXT2_SB(sb)->s_es;
 
@@ -1280,7 +1281,13 @@ static int ext2_sync_fs(struct super_block *sb, int wait, errseq_t *since)
 	}
 	spin_unlock(&sbi->s_lock);
 	ext2_sync_super(sb, es, wait);
-	return __sync_blockdev(sb->s_bdev, 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;
 }
 
 static int ext2_freeze(struct super_block *sb)
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [RFC PATCH 11/11] vfs: have call_sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
                   ` (9 preceding siblings ...)
  2018-05-18 12:34 ` [RFC PATCH 10/11] ext2: " Jeff Layton
@ 2018-05-18 12:34 ` Jeff Layton
  10 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 12:34 UTC (permalink / raw)
  To: linux-fsdevel, linux-kernel; +Cc: viro, willy, andres

From: Jeff Layton <jlayton@redhat.com>

...on filesystems that don't define a ->sync_fs operation.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/fs.h | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

NB: I'm not sure this is something we really want to do. It's a bit
cavalier and some filesystems might not like it if they are tracking
errors in other ways. An alternative here is to add a simple_sync_fs
helper that does this and add that to a whole swath of different fs'.

diff --git a/include/linux/fs.h b/include/linux/fs.h
index fecd29325f36..1ff9d4d119cb 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2505,9 +2505,18 @@ extern const struct file_operations def_chr_fops;
 static inline int call_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, since);
-	return __sync_blockdev(sb->s_bdev, 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;
 }
 
 #ifdef CONFIG_BLOCK
-- 
2.17.0

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 ` [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer Jeff Layton
@ 2018-05-18 15:22   ` Matthew Wilcox
  2018-05-18 16:50     ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Matthew Wilcox @ 2018-05-18 15:22 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-fsdevel, linux-kernel, viro, andres

On Fri, May 18, 2018 at 08:34:11AM -0400, Jeff Layton wrote:
> From: Jeff Layton <jlayton@redhat.com>
> 
> When ext4_sync_fs gets a non-NULL since pointer, use it to report
> errors vs. the errseq_t in the super_block. This allows us to
> properly report an error to sync_fs when any inode has failed writeback
> since we last checked for it.
> 
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  fs/ext4/super.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 896ddf8c3421..a5f41d31611f 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -4898,6 +4898,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait, errseq_t *since)
>  	}
>  out:
>  	ret2 = __sync_blockdev(sb->s_bdev, wait);
> +	if (since)
> +		ret2 = errseq_check_and_advance(&sb->s_wb_err, since);
>  	return ret ? ret : ret2;
>  }

This is inconsistent with the ext2 and xfs implementations ...

I'm worried this is too much complexity to push down to the filesystems.
When should errors get reported through the return value; when should they
be reported through the errseq_t?

Can we hide all of this?  Maybe ext4 could do:

	errseq_set(&sb->s_wb_err, __sync_blockdev(sb->s_bdev, wait));
	return ret;

(we'd need to make calling errseq_set(x, 0) be a no-op instead of an error)

and then the caller is the one who takes care of calling
errseq_check_and_advance() so we don't have to pass 'since' into each
filesystem.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines
  2018-05-18 12:34 ` [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines Jeff Layton
@ 2018-05-18 15:56   ` Christoph Hellwig
  2018-05-18 17:56     ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2018-05-18 15:56 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-fsdevel, linux-kernel, viro, willy, andres

> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -1097,7 +1097,7 @@ xfs_fs_sync_fs(
>  	 * Doing anything during the async pass would be counterproductive.
>  	 */
>  	if (!wait)
> -		return 0;
> +		goto out;
>  
>  	xfs_log_force(mp, XFS_LOG_SYNC);
>  	if (laptop_mode) {
> @@ -1108,8 +1108,8 @@ xfs_fs_sync_fs(
>  		 */
>  		flush_delayed_work(&mp->m_log->l_work);
>  	}
> -
> -	return 0;
> +out:
> +	return __sync_blockdev(sb->s_bdev, wait);

XFS never uses the block device mapping for anything, so this is
not needed.

> +/*
> + * Many legacy filesystems don't have a sync_fs op. For them, we just flush
> + * the block device (if there is one).
> + */
> +static inline int call_sync_fs(struct super_block *sb, int wait)
> +{
> +	if (sb->s_op->sync_fs)
> +		return sb->s_op->sync_fs(sb, wait);
> +	return __sync_blockdev(sb->s_bdev, wait);
> +}

The proper name for this would be vfs_sync_fs.  And I don't think it
warrants an inline.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 15:22   ` Matthew Wilcox
@ 2018-05-18 16:50     ` Jeff Layton
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 16:50 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-fsdevel, linux-kernel, viro, andres

On Fri, 2018-05-18 at 08:22 -0700, Matthew Wilcox wrote:
> On Fri, May 18, 2018 at 08:34:11AM -0400, Jeff Layton wrote:
> > From: Jeff Layton <jlayton@redhat.com>
> > 
> > When ext4_sync_fs gets a non-NULL since pointer, use it to report
> > errors vs. the errseq_t in the super_block. This allows us to
> > properly report an error to sync_fs when any inode has failed writeback
> > since we last checked for it.
> > 
> > Signed-off-by: Jeff Layton <jlayton@redhat.com>
> > ---
> >  fs/ext4/super.c | 2 ++
> >  1 file changed, 2 insertions(+)
> > 
> > diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> > index 896ddf8c3421..a5f41d31611f 100644
> > --- a/fs/ext4/super.c
> > +++ b/fs/ext4/super.c
> > @@ -4898,6 +4898,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait, errseq_t *since)
> >  	}
> >  out:
> >  	ret2 = __sync_blockdev(sb->s_bdev, wait);
> > +	if (since)
> > +		ret2 = errseq_check_and_advance(&sb->s_wb_err, since);
> >  	return ret ? ret : ret2;
> >  }
> 
> This is inconsistent with the ext2 and xfs implementations ...
> 
> I'm worried this is too much complexity to push down to the filesystems.
> When should errors get reported through the return value; when should they
> be reported through the errseq_t?
> 
> Can we hide all of this?  Maybe ext4 could do:
> 
> 	errseq_set(&sb->s_wb_err, __sync_blockdev(sb->s_bdev, wait));
> 	return ret;
> 

I'm not sure I understand what you intend here. If __sync_blockdev
fails, then the error should have already been marked in sb->s_wb_err
(via patch #6). We wouldn't want to record that again at syncfs time.
Note that __sync_blockdev will return errors based on the legacy
AS_EIO/AS_ENOSPC flags.

We really do need to record it in the superblock as soon as possible
after an error occurs. If we want to allow userland to eventually be
able to scrape this value out of the kernel (as we discussed at LSF/MM)
then we can't assume that it'll be doing any sort of syncfs call
beforehand.

The main reason to push this down into the filesystems is to allow them
control over whether to report errors at syncfs time via the superblock
errseq_t or not. If we don't really care about allowing this to be an
opt-in thing, then we could just take the patch that I sent on April
17th:

    [PATCH] fs: track per-sb writeback errors and report them to syncfs

We'd also want patch #6 from this series, I think, but that's more or
less enough to implement this over all filesystems, assuming they use
mapping_set_error to record writeback errors. I'm fine with either
approach.

> (we'd need to make calling errseq_set(x, 0) be a no-op instead of an error)
> 
> and then the caller is the one who takes care of calling
> errseq_check_and_advance() so we don't have to pass 'since' into each
> filesystem.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines
  2018-05-18 15:56   ` Christoph Hellwig
@ 2018-05-18 17:56     ` Jeff Layton
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-18 17:56 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-fsdevel, linux-kernel, viro, willy, andres

On Fri, 2018-05-18 at 08:56 -0700, Christoph Hellwig wrote:
> > --- a/fs/xfs/xfs_super.c
> > +++ b/fs/xfs/xfs_super.c
> > @@ -1097,7 +1097,7 @@ xfs_fs_sync_fs(
> >  	 * Doing anything during the async pass would be counterproductive.
> >  	 */
> >  	if (!wait)
> > -		return 0;
> > +		goto out;
> >  
> >  	xfs_log_force(mp, XFS_LOG_SYNC);
> >  	if (laptop_mode) {
> > @@ -1108,8 +1108,8 @@ xfs_fs_sync_fs(
> >  		 */
> >  		flush_delayed_work(&mp->m_log->l_work);
> >  	}
> > -
> > -	return 0;
> > +out:
> > +	return __sync_blockdev(sb->s_bdev, wait);
> 
> XFS never uses the block device mapping for anything, so this is
> not needed.
> 

Thanks, I wasn't sure about xfs. I'll drop this hunk.

FWIW, I think pushing this call down into the sync_fs routines is still
probably the right thing to do, regardless of the state of the later
patches.

> > +/*
> > + * Many legacy filesystems don't have a sync_fs op. For them, we just flush
> > + * the block device (if there is one).
> > + */
> > +static inline int call_sync_fs(struct super_block *sb, int wait)
> > +{
> > +	if (sb->s_op->sync_fs)
> > +		return sb->s_op->sync_fs(sb, wait);
> > +	return __sync_blockdev(sb->s_bdev, wait);
> > +}
> 
> The proper name for this would be vfs_sync_fs.  And I don't think it
> warrants an inline.

I patterned the name after the call_mmap (and now-defunct call_fsync)
helpers. I'll rename it and change it to be non-inlined.

Thanks,
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 08/11] xfs: have sync_fs op report writeback errors when passed a since pointer
  2018-05-18 12:34 ` [RFC PATCH 08/11] xfs: " Jeff Layton
@ 2018-05-21 23:01   ` Dave Chinner
  2018-05-21 23:23     ` Jeff Layton
  0 siblings, 1 reply; 18+ messages in thread
From: Dave Chinner @ 2018-05-21 23:01 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-fsdevel, linux-kernel, viro, willy, andres

On Fri, May 18, 2018 at 08:34:12AM -0400, Jeff Layton wrote:
> From: Jeff Layton <jlayton@redhat.com>
> 
> Signed-off-by: Jeff Layton <jlayton@redhat.com>
> ---
>  fs/xfs/xfs_super.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 9255de2767b4..7dc847f48f9f 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -1092,6 +1092,7 @@ xfs_fs_sync_fs(
>  	int			wait,
>  	errseq_t		*since)
>  {
> +	int			ret;
>  	struct xfs_mount	*mp = XFS_M(sb);
>  
>  	/*
> @@ -1110,7 +1111,13 @@ xfs_fs_sync_fs(
>  		flush_delayed_work(&mp->m_log->l_work);
>  	}
>  out:
> -	return __sync_blockdev(sb->s_bdev, wait);

Where did this come from? XFS doesn't use the underlying blockdev
address space, so this does nothing at all and should not be here.

> +	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;
>  }

So to return errors correctly, xfs_fs_sync_fs() needs to capture
errors from the log force (i.e. metadata errors such as filesystem
shutdowns, journal IO errors, etc), then check for pending data IO
errors. i.e:


 STATIC int
 xfs_fs_sync_fs(
 	struct super_block      *sb,
 	int                     wait)
 {
 	struct xfs_mount        *mp = XFS_M(sb);
+	int			err;
 
 	/*
 	 * Doing anything during the async pass would be counterproductive.
 	 */
 	if (!wait)
 		return 0;
 
-	xfs_log_force(mp, XFS_LOG_SYNC);
+	err = xfs_log_force(mp, XFS_LOG_SYNC);
+	if (err)
+		return err;
+
 	if (laptop_mode) {
 		/*
 		 * The disk must be active because we're syncing.
 		 * We schedule log work now (now that the disk is
 		 * active) instead of later (when it might not be).
 		 */
 		flush_delayed_work(&mp->m_log->l_work);
 	}
 
-	return 0
+	return errseq_check_and_advance(&sb->s_wb_err, since);
 }

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [RFC PATCH 08/11] xfs: have sync_fs op report writeback errors when passed a since pointer
  2018-05-21 23:01   ` Dave Chinner
@ 2018-05-21 23:23     ` Jeff Layton
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Layton @ 2018-05-21 23:23 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-fsdevel, linux-kernel, viro, willy, andres

On Tue, 2018-05-22 at 09:01 +1000, Dave Chinner wrote:
> On Fri, May 18, 2018 at 08:34:12AM -0400, Jeff Layton wrote:
> > From: Jeff Layton <jlayton@redhat.com>
> > 
> > Signed-off-by: Jeff Layton <jlayton@redhat.com>
> > ---
> >  fs/xfs/xfs_super.c | 9 ++++++++-
> >  1 file changed, 8 insertions(+), 1 deletion(-)
> > 
> > diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> > index 9255de2767b4..7dc847f48f9f 100644
> > --- a/fs/xfs/xfs_super.c
> > +++ b/fs/xfs/xfs_super.c
> > @@ -1092,6 +1092,7 @@ xfs_fs_sync_fs(
> >  	int			wait,
> >  	errseq_t		*since)
> >  {
> > +	int			ret;
> >  	struct xfs_mount	*mp = XFS_M(sb);
> >  
> >  	/*
> > @@ -1110,7 +1111,13 @@ xfs_fs_sync_fs(
> >  		flush_delayed_work(&mp->m_log->l_work);
> >  	}
> >  out:
> > -	return __sync_blockdev(sb->s_bdev, wait);
> 
> Where did this come from? XFS doesn't use the underlying blockdev
> address space, so this does nothing at all and should not be here.
> 

An earlier patch that pushed this down into the sync_fs routines. We
call this today for all filesystems, and I wasn't sure about xfs.

Christoph already pointed out that it's not needed so it's removed from
my current branch.
 
> > +	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;
> >  }
> 
> So to return errors correctly, xfs_fs_sync_fs() needs to capture
> errors from the log force (i.e. metadata errors such as filesystem
> shutdowns, journal IO errors, etc), then check for pending data IO
> errors. i.e:
> 
> 
>  STATIC int
>  xfs_fs_sync_fs(
>  	struct super_block      *sb,
>  	int                     wait)
>  {
>  	struct xfs_mount        *mp = XFS_M(sb);
> +	int			err;
>  
>  	/*
>  	 * Doing anything during the async pass would be counterproductive.
>  	 */
>  	if (!wait)
>  		return 0;
>  
> -	xfs_log_force(mp, XFS_LOG_SYNC);
> +	err = xfs_log_force(mp, XFS_LOG_SYNC);
> +	if (err)
> +		return err;
> +
>  	if (laptop_mode) {
>  		/*
>  		 * The disk must be active because we're syncing.
>  		 * We schedule log work now (now that the disk is
>  		 * active) instead of later (when it might not be).
>  		 */
>  		flush_delayed_work(&mp->m_log->l_work);
>  	}
>  
> -	return 0
> +	return errseq_check_and_advance(&sb->s_wb_err, since);
>  }
> 

Ok, sounds good. I'll fix that too.

FWIW, we'll actually want to advance the cursor even if xfs_log_force
returns an error to ensure that we don't end up reporting errors twice,
but that's simple enough to do.

Thanks!
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2018-05-21 23:23 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-18 12:34 [RFC PATCH 00/11] vfs: have syncfs return an error when inode writeback fails Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 01/11] vfs: push __sync_blockdev calls down into sync_fs routines Jeff Layton
2018-05-18 15:56   ` Christoph Hellwig
2018-05-18 17:56     ` Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 02/11] vfs: add a new errseq_t pointer to sync_fs prototype Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 03/11] vfs: add an errseq_t pointer to sync_filesystem Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 04/11] vfs: add errseq_t pointer to __sync_filesystem Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 05/11] fs: track per-sb writeback errors and report them to syncfs Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 06/11] buffer: record blockdev write errors in super_block that backs them Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 07/11] ext4: have sync_fs op report writeback errors when passed a since pointer Jeff Layton
2018-05-18 15:22   ` Matthew Wilcox
2018-05-18 16:50     ` Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 08/11] xfs: " Jeff Layton
2018-05-21 23:01   ` Dave Chinner
2018-05-21 23:23     ` Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 09/11] btrfs: " Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 10/11] ext2: " Jeff Layton
2018-05-18 12:34 ` [RFC PATCH 11/11] vfs: have call_sync_fs " Jeff Layton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).