[PATCH -next v5 14/32] ext4: do not use data=ordered mode for inodes using buffered iomap path

Zhang Yi <[email protected]>
Newsgroups org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-ext4,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Zhang Yi <[email protected]>

The data=ordered mode introduces two fundamental conflicts with the
iomap buffered write path, leading to potential deadlocks.

1) Lock ordering conflict
   In the iomap writeback path, each folio is processed sequentially:
   the folio lock is acquired first, followed by starting a transaction
   to create block mappings.

   In data=ordered mode, writeback triggered by the journal commit
   process may attempt to acquire a folio lock that is already held by
   iomap background writeback process. Meanwhile, iomap, under that same
   folio lock, may start a new transaction to map other blocks on this
   folio and wait for the currently committing transaction to finish,
   resulting in a deadlock.

   Trans N commit                    background writeback(via iomap)

   journal_submit_data_buffers()
    ext4_journal_submit_inode_data_buffers()
     iomap_writepages()
                                     iomap_writepages()
                                     folio_lock()
      folio_lock()  -- wait
                                      iomap_writeback_folio()
                                       iomap_writeback_range()
                                        ext4_journal_start()
                                         start new transaction
                                          -- wait for trans N commit,
					     DEADLOCK
                                         ext4_map_blocks()

   Currently, in the buffer_head writeback path, this is handled by
   starting the transaction before taking any folio locks for writeback.

2) Partial folio submission not supported
   When block size < folio size, a folio may contain both mapped and
   unmapped blocks. In data=ordered mode, a deadlock can occur if the
   journal waits (pure JI_WAIT_DATA) for such a folio to be written back
   while background writeback has already started on it (with the
   writeback flag set). The problem is that mapping the remaining
   delalloc blocks can deadlock because the writeback flag is not
   cleared until the entire folio is processed and committed.

   T0: Assume we have a folio contains four blocks, from front to back,
       they are A, B, C, D. The block B and C are holes, and the last
       block D is written in delalloc mode (the block is not allocated
       yet).
   T1: The background writeback process starts to write back data, set
       writeback flag on the folio, allocates block D, and adds it to
       transaction N's order list of jbd2 in pure JI_WAIT_DATA mode.
   T2: This folio completes the writeback and clears the writeback flag.
   T3: Before transaction N commit, we buffered write block A to C.
   T4: Transaction N commit and folio writeback are running
       concurrently.

   Trans N commit                   background writeback(via iomap)

                                    iomap_writeback_folio()
                                     folio_start_writeback()
                                      -- set writeback flag
   jbd2_journal_finish_inode_data_buffers()
    __filemap_fdatawait_range()
     -- wait writeback flag to clear
                                      iomap_writeback_range()
                                       ext4_journal_start()
                                        start new transaction
                                         -- wait for trans N commit,
                                            DEADLOCK
                                       ext4_map_block() (B, C)

   Currently, in the buffer_head writeback path, this is handled by:

   1. Partial folio submission — already-allocated buffers can be
      submitted first. The writeback flag is cleared after I/O
      completes, preventing block allocation while the writeback flag is
      set.
   2. Allocation order — the transaction is started first, then blocks
      are allocated, the writeback flag is set, and finally the
      allocated buffers submission begins.

To support data=ordered mode, the iomap core would need two invasive
changes:
 - Acquire the transaction handle before locking any folio for
   writeback.
 - Support partial folio submission.

Both changes are complicated and risk performance regressions.
Therefore, we must avoid using data=ordered mode when converting to the
iomap path.

Currently, data=ordered mode is used in three scenarios:
 - Append write
 - Post-EOF partial block truncate-up followed by append write
 - Online defragmentation

We can address the first two without data=ordered mode:
 - For append write: always allocate unwritten blocks (i.e. always
   enable dioread_nolock), preserving the behavior of current
   extent-type inodes.
 - For post-EOF truncate-up + append write: postpone updating i_disksize
   until after the zeroed partial block has been written back.

Online defragmentation does not yet support iomap; this can be resolved
separately in the future.

Signed-off-by: Zhang Yi <[email protected]>
Reviewed-by: Jan Kara <[email protected]>
---
 fs/ext4/ext4_jbd2.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 2fbf48b3dfe2..be54e93bde0b 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -379,7 +379,12 @@ static inline int ext4_should_journal_data(struct inode *inode)
 
 static inline int ext4_should_order_data(struct inode *inode)
 {
-	return ext4_inode_journal_mode(inode) & EXT4_INODE_ORDERED_DATA_MODE;
+	/*
+	 * inodes using the iomap buffered I/O path do not use the
+	 * data=ordered mode.
+	 */
+	return !ext4_inode_buffered_iomap(inode) &&
+		(ext4_inode_journal_mode(inode) & EXT4_INODE_ORDERED_DATA_MODE);
 }
 
 static inline int ext4_should_writeback_data(struct inode *inode)
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.