iomap: Fix broken data integrity guarantees for O_SYNC writes
authorJan Kara <jack@suse.cz>
Wed, 30 Jul 2025 10:28:41 +0000 (12:28 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 11 Aug 2025 12:51:49 +0000 (14:51 +0200)
Commit d279c80e0bac ("iomap: inline iomap_dio_bio_opflags()") has broken
the logic in iomap_dio_bio_iter() in a way that when the device does
support FUA (or has no writeback cache) and the direct IO happens to
freshly allocated or unwritten extents, we will *not* issue fsync after
completing direct IO O_SYNC / O_DSYNC write because the
IOMAP_DIO_WRITE_THROUGH flag stays mistakenly set. Fix the problem by
clearing IOMAP_DIO_WRITE_THROUGH whenever we do not perform FUA write as
it was originally intended.

CC: John Garry <john.g.garry@oracle.com>
CC: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Fixes: d279c80e0bac ("iomap: inline iomap_dio_bio_opflags()")
CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/20250730102840.20470-2-jack@suse.cz
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/iomap/direct-io.c

index 6f25d4cfea9f7ee14dac511fac3a3ee3be9d3466..b84f6af2eb4c88cc28ff8ea7a6f471d4e2b4121c 100644 (file)
@@ -363,14 +363,14 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
                if (iomap->flags & IOMAP_F_SHARED)
                        dio->flags |= IOMAP_DIO_COW;
 
-               if (iomap->flags & IOMAP_F_NEW) {
+               if (iomap->flags & IOMAP_F_NEW)
                        need_zeroout = true;
-               } else if (iomap->type == IOMAP_MAPPED) {
-                       if (iomap_dio_can_use_fua(iomap, dio))
-                               bio_opf |= REQ_FUA;
-                       else
-                               dio->flags &= ~IOMAP_DIO_WRITE_THROUGH;
-               }
+               else if (iomap->type == IOMAP_MAPPED &&
+                        iomap_dio_can_use_fua(iomap, dio))
+                       bio_opf |= REQ_FUA;
+
+               if (!(bio_opf & REQ_FUA))
+                       dio->flags &= ~IOMAP_DIO_WRITE_THROUGH;
 
                /*
                 * We can only do deferred completion for pure overwrites that