[PATCH] mm: always respect QUEUE_FLAG_STABLE_WRITES on the block device

From: Ilya Dryomov
Date: Thu May 04 2023 - 06:57:26 EST


Commit 1cb039f3dc16 ("bdi: replace BDI_CAP_STABLE_WRITES with a queue
and a sb flag") introduced a regression for the raw block device use
case. Capturing QUEUE_FLAG_STABLE_WRITES flag in set_bdev_super() has
the effect of respecting it only when there is a filesystem mounted on
top of the block device. If a filesystem is not mounted, block devices
that do integrity checking return sporadic checksum errors.

Additionally, this commit made the corresponding sysfs knob writeable
for debugging purposes. However, because QUEUE_FLAG_STABLE_WRITES flag
is captured when the filesystem is mounted and isn't consulted after
that anywhere outside of swap code, changing it doesn't take immediate
effect even though dumping the knob shows the new value. With no way
to dump SB_I_STABLE_WRITES flag, this is needlessly confusing.

Resurrect the original stable writes behavior by changing
folio_wait_stable() to account for the case of a raw block device and
also:

- for the case of a filesystem, test QUEUE_FLAG_STABLE_WRITES flag
each time instead of capturing it in the superblock so that changes
are reflected immediately (thus aligning with the case of a raw block
device)
- retain SB_I_STABLE_WRITES flag for filesystems that need stable
writes independent of the underlying block device (currently just
NFS)

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 1cb039f3dc16 ("bdi: replace BDI_CAP_STABLE_WRITES with a queue and a sb flag")
Signed-off-by: Ilya Dryomov <idryomov@xxxxxxxxx>
---
fs/super.c | 2 --
mm/page-writeback.c | 12 +++++++++++-
2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 04bc62ab7dfe..6705b3506ae8 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1213,8 +1213,6 @@ static int set_bdev_super(struct super_block *s, void *data)
s->s_dev = s->s_bdev->bd_dev;
s->s_bdi = bdi_get(s->s_bdev->bd_disk->bdi);

- if (bdev_stable_writes(s->s_bdev))
- s->s_iflags |= SB_I_STABLE_WRITES;
return 0;
}

diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 516b1aa247e8..469bc57add8c 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -3169,7 +3169,17 @@ EXPORT_SYMBOL_GPL(folio_wait_writeback_killable);
*/
void folio_wait_stable(struct folio *folio)
{
- if (folio_inode(folio)->i_sb->s_iflags & SB_I_STABLE_WRITES)
+ struct inode *inode = folio_inode(folio);
+ struct super_block *sb = inode->i_sb;
+ bool stable_writes;
+
+ if (sb_is_blkdev_sb(sb))
+ stable_writes = bdev_stable_writes(I_BDEV(inode));
+ else
+ stable_writes = bdev_stable_writes(sb->s_bdev) ||
+ (sb->s_iflags & SB_I_STABLE_WRITES);
+
+ if (stable_writes)
folio_wait_writeback(folio);
}
EXPORT_SYMBOL_GPL(folio_wait_stable);
--
2.39.2