]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ext4: refactor the block allocation process of ext4_page_mkwrite()
authorZhang Yi <yi.zhang@huawei.com>
Mon, 7 Jul 2025 14:08:07 +0000 (22:08 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 14 Jul 2025 03:41:51 +0000 (23:41 -0400)
The block allocation process and error handling in ext4_page_mkwrite()
is complex now. Refactor it by introducing a new helper function,
ext4_block_page_mkwrite(). It will call ext4_block_write_begin() to
allocate blocks instead of directly calling block_page_mkwrite().
Preparing to implement retry logic in a subsequent patch to address
situations where the reserved journal credits are insufficient.
Additionally, this modification will help prevent potential deadlocks
that may occur when waiting for folio writeback while holding the
transaction handle.

Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20250707140814.542883-5-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/inode.c

index ceb68df19f1e6c61a73a11a4ceaa51d15df9fe55..f8946430304c95edd239586a59d697523e624a7f 100644 (file)
@@ -6603,6 +6603,53 @@ static int ext4_bh_unmapped(handle_t *handle, struct inode *inode,
        return !buffer_mapped(bh);
 }
 
+static int ext4_block_page_mkwrite(struct inode *inode, struct folio *folio,
+                                  get_block_t get_block)
+{
+       handle_t *handle;
+       loff_t size;
+       unsigned long len;
+       int ret;
+
+       handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE,
+                                   ext4_writepage_trans_blocks(inode));
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
+
+       folio_lock(folio);
+       size = i_size_read(inode);
+       /* Page got truncated from under us? */
+       if (folio->mapping != inode->i_mapping || folio_pos(folio) > size) {
+               ret = -EFAULT;
+               goto out_error;
+       }
+
+       len = folio_size(folio);
+       if (folio_pos(folio) + len > size)
+               len = size - folio_pos(folio);
+
+       ret = ext4_block_write_begin(handle, folio, 0, len, get_block);
+       if (ret)
+               goto out_error;
+
+       if (!ext4_should_journal_data(inode)) {
+               block_commit_write(folio, 0, len);
+               folio_mark_dirty(folio);
+       } else {
+               ret = ext4_journal_folio_buffers(handle, folio, len);
+               if (ret)
+                       goto out_error;
+       }
+       ext4_journal_stop(handle);
+       folio_wait_stable(folio);
+       return ret;
+
+out_error:
+       folio_unlock(folio);
+       ext4_journal_stop(handle);
+       return ret;
+}
+
 vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
 {
        struct vm_area_struct *vma = vmf->vma;
@@ -6614,8 +6661,7 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
        struct file *file = vma->vm_file;
        struct inode *inode = file_inode(file);
        struct address_space *mapping = inode->i_mapping;
-       handle_t *handle;
-       get_block_t *get_block;
+       get_block_t *get_block = ext4_get_block;
        int retries = 0;
 
        if (unlikely(IS_IMMUTABLE(inode)))
@@ -6683,46 +6729,9 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
        /* OK, we need to fill the hole... */
        if (ext4_should_dioread_nolock(inode))
                get_block = ext4_get_block_unwritten;
-       else
-               get_block = ext4_get_block;
 retry_alloc:
-       handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE,
-                                   ext4_writepage_trans_blocks(inode));
-       if (IS_ERR(handle)) {
-               ret = VM_FAULT_SIGBUS;
-               goto out;
-       }
-       /*
-        * Data journalling can't use block_page_mkwrite() because it
-        * will set_buffer_dirty() before do_journal_get_write_access()
-        * thus might hit warning messages for dirty metadata buffers.
-        */
-       if (!ext4_should_journal_data(inode)) {
-               err = block_page_mkwrite(vma, vmf, get_block);
-       } else {
-               folio_lock(folio);
-               size = i_size_read(inode);
-               /* Page got truncated from under us? */
-               if (folio->mapping != mapping || folio_pos(folio) > size) {
-                       ret = VM_FAULT_NOPAGE;
-                       goto out_error;
-               }
-
-               len = folio_size(folio);
-               if (folio_pos(folio) + len > size)
-                       len = size - folio_pos(folio);
-
-               err = ext4_block_write_begin(handle, folio, 0, len,
-                                            ext4_get_block);
-               if (!err) {
-                       ret = VM_FAULT_SIGBUS;
-                       if (ext4_journal_folio_buffers(handle, folio, len))
-                               goto out_error;
-               } else {
-                       folio_unlock(folio);
-               }
-       }
-       ext4_journal_stop(handle);
+       /* Start journal and allocate blocks */
+       err = ext4_block_page_mkwrite(inode, folio, get_block);
        if (err == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
                goto retry_alloc;
 out_ret:
@@ -6731,8 +6740,4 @@ out:
        filemap_invalidate_unlock_shared(mapping);
        sb_end_pagefault(inode->i_sb);
        return ret;
-out_error:
-       folio_unlock(folio);
-       ext4_journal_stop(handle);
-       goto out;
 }