Skip to content

Commit 1ad0f42

Browse files
zhangyi089tytso
authored andcommitted
ext4: move pagecache_isize_extended() out of active handle
In ext4_alloc_file_blocks(), pagecache_isize_extended() is called under an active handle and may also hold folio lock if the block size is smaller than the folio size. This also breaks the "folio lock -> transaction start" lock ordering for the upcoming iomap buffered I/O path. Therefore, move pagecache_isize_extended() outside of an active handle. Additionally, it is unnecessary to update the file length during each iteration of the allocation loop. Instead, update the file length only to the position where the allocation is successful. Postpone updating the inode size until after the allocation loop completes or is interrupted due to an error. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260327102939.1095257-13-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent 116c0bd commit 1ad0f42

1 file changed

Lines changed: 39 additions & 23 deletions

File tree

fs/ext4/extents.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4582,7 +4582,7 @@ static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len,
45824582
ext4_lblk_t len_lblk;
45834583
struct ext4_map_blocks map;
45844584
unsigned int credits;
4585-
loff_t epos, old_size = i_size_read(inode);
4585+
loff_t epos = 0, old_size = i_size_read(inode);
45864586
unsigned int blkbits = inode->i_blkbits;
45874587
bool alloc_zero = false;
45884588

@@ -4647,44 +4647,60 @@ static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len,
46474647
ext4_journal_stop(handle);
46484648
break;
46494649
}
4650+
ext4_update_inode_fsync_trans(handle, inode, 1);
4651+
ret = ext4_journal_stop(handle);
4652+
if (unlikely(ret))
4653+
break;
4654+
46504655
/*
46514656
* allow a full retry cycle for any remaining allocations
46524657
*/
46534658
retries = 0;
4654-
epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret);
4655-
if (new_size) {
4656-
if (epos > new_size)
4657-
epos = new_size;
4658-
ext4_update_inode_size(inode, epos);
4659-
if (epos > old_size)
4660-
pagecache_isize_extended(inode, old_size, epos);
4661-
}
4662-
ret2 = ext4_mark_inode_dirty(handle, inode);
4663-
ext4_update_inode_fsync_trans(handle, inode, 1);
4664-
ret3 = ext4_journal_stop(handle);
4665-
ret2 = ret3 ? ret3 : ret2;
4666-
if (unlikely(ret2))
4667-
break;
46684659

46694660
if (alloc_zero &&
46704661
(map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN))) {
4671-
ret2 = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
4672-
map.m_len);
4673-
if (likely(!ret2))
4674-
ret2 = ext4_convert_unwritten_extents(NULL,
4662+
ret = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
4663+
map.m_len);
4664+
if (likely(!ret))
4665+
ret = ext4_convert_unwritten_extents(NULL,
46754666
inode, (loff_t)map.m_lblk << blkbits,
46764667
(loff_t)map.m_len << blkbits);
4677-
if (ret2)
4668+
if (ret)
46784669
break;
46794670
}
46804671

4681-
map.m_lblk += ret;
4682-
map.m_len = len_lblk = len_lblk - ret;
4672+
map.m_lblk += map.m_len;
4673+
map.m_len = len_lblk = len_lblk - map.m_len;
4674+
epos = EXT4_LBLK_TO_B(inode, map.m_lblk);
46834675
}
4676+
46844677
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
46854678
goto retry;
46864679

4687-
return ret > 0 ? ret2 : ret;
4680+
if (!epos || !new_size)
4681+
return ret;
4682+
4683+
/*
4684+
* Allocate blocks, update the file size to match the size of the
4685+
* already successfully allocated blocks.
4686+
*/
4687+
if (epos > new_size)
4688+
epos = new_size;
4689+
4690+
handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
4691+
if (IS_ERR(handle))
4692+
return ret ? ret : PTR_ERR(handle);
4693+
4694+
ext4_update_inode_size(inode, epos);
4695+
ret2 = ext4_mark_inode_dirty(handle, inode);
4696+
ext4_update_inode_fsync_trans(handle, inode, 1);
4697+
ret3 = ext4_journal_stop(handle);
4698+
ret2 = ret3 ? ret3 : ret2;
4699+
4700+
if (epos > old_size)
4701+
pagecache_isize_extended(inode, old_size, epos);
4702+
4703+
return ret ? ret : ret2;
46884704
}
46894705

46904706
static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len);

0 commit comments

Comments
 (0)