Skip to content

Commit ffb21ea

Browse files
zhangyi089gregkh
authored andcommitted
ext4: restart handle if credits are insufficient during allocating blocks
commit e2c4c49 upstream. After large folios are supported on ext4, writing back a sufficiently large and discontinuous folio may consume a significant number of journal credits, placing considerable strain on the journal. For example, in a 20GB filesystem with 1K block size and 1MB journal size, writing back a 2MB folio could require thousands of credits in the worst-case scenario (when each block is discontinuous and distributed across different block groups), potentially exceeding the journal size. This issue can also occur in ext4_write_begin() and ext4_page_mkwrite() when delalloc is not enabled. Fix this by ensuring that there are sufficient journal credits before allocating an extent in mpage_map_one_extent() and ext4_block_write_begin(). If there are not enough credits, return -EAGAIN, exit the current mapping loop, restart a new handle and a new transaction, and allocating blocks on this folio again in the next iteration. Suggested-by: Jan Kara <[email protected]> Signed-off-by: Zhang Yi <[email protected]> Reviewed-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent ab13e8c commit ffb21ea

1 file changed

Lines changed: 36 additions & 5 deletions

File tree

fs/ext4/inode.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,26 @@ static void ext4_update_bh_state(struct buffer_head *bh, unsigned long flags)
877877
} while (unlikely(!try_cmpxchg(&bh->b_state, &old_state, new_state)));
878878
}
879879

880+
/*
881+
* Make sure that the current journal transaction has enough credits to map
882+
* one extent. Return -EAGAIN if it cannot extend the current running
883+
* transaction.
884+
*/
885+
static inline int ext4_journal_ensure_extent_credits(handle_t *handle,
886+
struct inode *inode)
887+
{
888+
int credits;
889+
int ret;
890+
891+
/* Called from ext4_da_write_begin() which has no handle started? */
892+
if (!handle)
893+
return 0;
894+
895+
credits = ext4_chunk_trans_blocks(inode, 1);
896+
ret = __ext4_journal_ensure_credits(handle, credits, credits, 0);
897+
return ret <= 0 ? ret : -EAGAIN;
898+
}
899+
880900
static int _ext4_get_block(struct inode *inode, sector_t iblock,
881901
struct buffer_head *bh, int flags)
882902
{
@@ -1175,7 +1195,9 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio,
11751195
clear_buffer_new(bh);
11761196
if (!buffer_mapped(bh)) {
11771197
WARN_ON(bh->b_size != blocksize);
1178-
err = get_block(inode, block, bh, 1);
1198+
err = ext4_journal_ensure_extent_credits(handle, inode);
1199+
if (!err)
1200+
err = get_block(inode, block, bh, 1);
11791201
if (err)
11801202
break;
11811203
if (buffer_new(bh)) {
@@ -1374,8 +1396,9 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
13741396
ext4_orphan_del(NULL, inode);
13751397
}
13761398

1377-
if (ret == -ENOSPC &&
1378-
ext4_should_retry_alloc(inode->i_sb, &retries))
1399+
if (ret == -EAGAIN ||
1400+
(ret == -ENOSPC &&
1401+
ext4_should_retry_alloc(inode->i_sb, &retries)))
13791402
goto retry_journal;
13801403
folio_put(folio);
13811404
return ret;
@@ -2324,6 +2347,11 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
23242347
int get_blocks_flags;
23252348
int err, dioread_nolock;
23262349

2350+
/* Make sure transaction has enough credits for this extent */
2351+
err = ext4_journal_ensure_extent_credits(handle, inode);
2352+
if (err < 0)
2353+
return err;
2354+
23272355
trace_ext4_da_write_pages_extent(inode, map);
23282356
/*
23292357
* Call ext4_map_blocks() to allocate any delayed allocation blocks, or
@@ -2451,7 +2479,7 @@ static int mpage_map_and_submit_extent(handle_t *handle,
24512479
* In the case of ENOSPC, if ext4_count_free_blocks()
24522480
* is non-zero, a commit should free up blocks.
24532481
*/
2454-
if ((err == -ENOMEM) ||
2482+
if ((err == -ENOMEM) || (err == -EAGAIN) ||
24552483
(err == -ENOSPC && ext4_count_free_clusters(sb))) {
24562484
/*
24572485
* We may have already allocated extents for
@@ -2957,6 +2985,8 @@ static int ext4_do_writepages(struct mpage_da_data *mpd)
29572985
ret = 0;
29582986
continue;
29592987
}
2988+
if (ret == -EAGAIN)
2989+
ret = 0;
29602990
/* Fatal error - ENOMEM, EIO... */
29612991
if (ret)
29622992
break;
@@ -6751,7 +6781,8 @@ vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf)
67516781
retry_alloc:
67526782
/* Start journal and allocate blocks */
67536783
err = ext4_block_page_mkwrite(inode, folio, get_block);
6754-
if (err == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
6784+
if (err == -EAGAIN ||
6785+
(err == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)))
67556786
goto retry_alloc;
67566787
out_ret:
67576788
ret = vmf_fs_error(err);

0 commit comments

Comments
 (0)