[PATCH v6 8/9] ovl: report failed upper writeback in syncfs()
From: Josef Bacik
Date: Tue Nov 10 2026 - 09:38:41 EST
If somebody else writes the pages of an upper inode, like the flusher of
the upper filesystem, and that fails, syncfs() on the overlay returns 0,
unless the mount is volatile or has fsync=off. mapping_set_error() records
the error in the mapping and in s_wb_err of the upper filesystem, and
syncfs() looks at s_wb_err of the overlay. That is not new.
With fsync=relaxed the error also comes back at the wrong time. AS_EIO
stays set in the upper mapping, and the next syncfs() that has to wait for
the file finds it in filemap_write_and_wait(), after a write that may have
succeeded.
An overlay inode stays dirty while its upper inode has pages that are dirty
or under writeback, so overlayfs still tracks the inode when the writeback
fails. Check AS_EIO and AS_ENOSPC of the upper mapping before the overlay
inode becomes clean and record the error in s_wb_err of the overlay.
A cursor for wb_err of the mapping would not do. Checking it marks the
error as seen, and an fsync() after a reopen would not get it any more.
This is best effort:
- Whoever checks the flags first gets the error, for example an fsync(),
a truncate or direct I/O.
- The flags belong to the upper inode. An error from before the overlay
inode was dirtied is reported as well.
- The flags are not ordered against the page cache tags.
- btrfs records the error after it has ended writeback.
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
fs/overlayfs/super.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index f38c6dd2ae47..a0fb85405f68 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -368,7 +368,7 @@ static enum ovl_write_mode ovl_write_mode(struct inode *inode,
static int ovl_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct inode *upper = ovl_inode_upper(inode);
- int ret = 0;
+ int ret = 0, err;
if (!upper)
return 0;
@@ -386,8 +386,22 @@ static int ovl_write_inode(struct inode *inode, struct writeback_control *wbc)
else
ovl_flush_lazytime(inode->i_sb, upper);
- if (ovl_upper_unfinished(upper))
+ if (ovl_upper_unfinished(upper)) {
ovl_mark_inode_dirty(inode);
+ return ret;
+ }
+
+ /*
+ * The overlay inode becomes clean, and syncfs() will not look at
+ * @upper. If somebody else has written its pages and that has failed,
+ * find out now. filemap_write_and_wait() clears the same flags, so the
+ * error is reported once. fsync() goes by wb_err of the mapping, which
+ * is left alone. An error that the write above has returned is already
+ * recorded.
+ */
+ err = filemap_check_errors(upper->i_mapping);
+ if (err && !ret)
+ errseq_set(&inode->i_sb->s_wb_err, err);
return ret;
}
--
2.55.0