ext4: implement mmap path using iomap

Introduce ext4_iomap_page_mkwrite() to implement the mmap iomap path
for ext4. The heavy lifting is delegated to iomap_page_mkwrite(), which
only requires ext4_iomap_buffered_write_ops and
ext4_iomap_buffered_da_write_ops to allocate and map blocks.

Note that the lock ordering between folio lock and transaction start in
this path is reversed compared to the buffer_head buffered write path.
The lock ordering documentation in super.c has been updated accordingly.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Tested-by: syzbot@syzkaller.appspotmail.com
Link: https://patch.msgid.link/20261009103033.2920530-17-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
Zhang Yi 2026-10-09 18:30:17 +08:00 committed by Theodore Ts'o
parent a2e096bf59
commit e055578dc1
2 changed files with 37 additions and 3 deletions

View File

@ -4040,7 +4040,7 @@ static int ext4_iomap_buffered_do_write_begin(struct inode *inode,
return -ERANGE;
if (WARN_ON_ONCE(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
return -EINVAL;
if (WARN_ON_ONCE(!(flags & IOMAP_WRITE)))
if (WARN_ON_ONCE(!(flags & (IOMAP_WRITE | IOMAP_FAULT))))
return -EINVAL;
map_flags = delalloc ? EXT4_GET_BLOCKS_DELALLOC_RESERVE :
@ -4098,6 +4098,14 @@ static int ext4_iomap_buffered_da_write_end(struct inode *inode, loff_t offset,
if (iomap->type != IOMAP_DELALLOC || !(iomap->flags & IOMAP_F_NEW))
return 0;
/*
* iomap_page_mkwrite() will never fail in a way that requires delalloc
* extents that it allocated to be revoked. Hence never try to release
* them here.
*/
if (flags & IOMAP_FAULT)
return 0;
/* Nothing to do if we've written the entire delalloc extent */
start_byte = iomap_last_written_block(inode, offset, written);
end_byte = round_up(offset + length, i_blocksize(inode));
@ -7315,6 +7323,23 @@ static int ext4_block_page_mkwrite(struct inode *inode, struct folio *folio,
return ret;
}
static vm_fault_t ext4_iomap_page_mkwrite(struct vm_fault *vmf)
{
struct inode *inode = file_inode(vmf->vma->vm_file);
const struct iomap_ops *iomap_ops;
/*
* ext4_nonda_switch() could writeback this folio, so have to
* call it before lock folio.
*/
if (test_opt(inode->i_sb, DELALLOC) && !ext4_nonda_switch(inode->i_sb))
iomap_ops = &ext4_iomap_buffered_da_write_ops;
else
iomap_ops = &ext4_iomap_buffered_write_ops;
return iomap_page_mkwrite(vmf, iomap_ops, NULL);
}
vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
@ -7341,6 +7366,11 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
if (err)
goto out_ret;
if (ext4_inode_buffered_iomap(inode)) {
ret = ext4_iomap_page_mkwrite(vmf);
goto out;
}
/*
* On data journalling we skip straight to the transaction handle:
* there's no delalloc; page truncated will be checked later; the

View File

@ -100,8 +100,12 @@ static const struct fs_parameter_spec ext4_param_specs[];
* Lock ordering
*
* page fault path:
* mmap_lock -> sb_start_pagefault -> invalidate_lock (r) -> transaction start
* -> page lock -> i_data_sem (rw)
* - buffer_head path:
* mmap_lock -> sb_start_pagefault -> invalidate_lock (r) ->
* transaction start -> folio lock -> i_data_sem (rw)
* - iomap path:
* mmap_lock -> sb_start_pagefault -> invalidate_lock (r) ->
* folio lock -> transaction start -> i_data_sem (rw)
*
* buffered write path:
* sb_start_write -> i_rwsem (w) -> mmap_lock