When large folios are enabled, the blocks-per-folio calculation in ext4_da_writepages_trans_blocks() can overflow the journal transaction limits, causing the writeback path to fail with errors like:
JBD2: kworker/u8:0 wants too many credits credits:416 rsv_credits:21 max:334
This occurs with small block sizes (1KB) and large folios (32MB), where the calculation results in 32768 blocks per folio. The transaction credit calculation then requests more credits than the journal can handle, leading to the following warning and writeback failure:
WARNING: CPU: 1 PID: 43 at fs/jbd2/transaction.c:334 start_this_handle+0x4c0/0x4e0 EXT4-fs (loop0): ext4_do_writepages: jbd2_start: 9223372036854775807 pages, ino 14; err -28
Call trace leading to the issue: ext4_do_writepages() ext4_da_writepages_trans_blocks() bpp = ext4_journal_blocks_per_folio() // Returns 32768 for 32MB folio with 1KB blocks ext4_meta_trans_blocks(inode, MAX_WRITEPAGES_EXTENT_LEN + bpp - 1, bpp) // With bpp=32768, lblocks=34815, pextents=32768 // Returns credits=415, but with overhead becomes 416 > max 334 ext4_journal_start_with_reserve() jbd2_journal_start_reserved() start_this_handle() // Fails with warning when credits:416 > max:334
The issue was introduced by commit d6bf294773a47 ("ext4/jbd2: convert jbd2_journal_blocks_per_page() to support large folio"), which added support for large folios but didn't account for the journal credit limits.
Fix this by capping the blocks-per-folio value at 8192 in the writeback path. This is the value we'd get with 32MB folios and 4KB blocks, or 8MB folios with 1KB blocks, which is reasonable and safe for typical journal configurations.
Fixes: d6bf294773a4 ("ext4/jbd2: convert jbd2_journal_blocks_per_page() to support large folio") Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin sashal@kernel.org --- fs/ext4/inode.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index be9a4cba35fd5..860e59a176c97 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2070,6 +2070,14 @@ static int mpage_submit_folio(struct mpage_da_data *mpd, struct folio *folio) */ #define MAX_WRITEPAGES_EXTENT_LEN 2048
+/* + * Maximum blocks per folio to avoid JBD2 credit overflow. + * This is the value we'd get with 32MB folios and 4KB blocks, + * or 8MB folios with 1KB blocks, which is reasonable and safe + * for typical journal configurations. + */ +#define MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK 8192 + /* * mpage_add_bh_to_extent - try to add bh to extent of blocks to map * @@ -2481,6 +2489,18 @@ static int ext4_da_writepages_trans_blocks(struct inode *inode) { int bpp = ext4_journal_blocks_per_folio(inode);
+ /* + * With large folios, blocks per folio can get excessively large, + * especially with small block sizes. For example, with 32MB folios + * (order 11) and 1KB blocks, we get 32768 blocks per folio. This + * leads to credit requests that overflow the journal's transaction + * limit. + * + * Limit the value to avoid excessive credit requests. + */ + if (bpp > MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK) + bpp = MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK; + return ext4_meta_trans_blocks(inode, MAX_WRITEPAGES_EXTENT_LEN + bpp - 1, bpp); } @@ -2559,6 +2579,13 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) handle_t *handle = NULL; int bpp = ext4_journal_blocks_per_folio(mpd->inode);
+ /* + * With large folios, blocks per folio can get excessively large, + * especially with small block sizes. Cap it to avoid credit overflow. + */ + if (bpp > MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK) + bpp = MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK; + if (mpd->wbc->sync_mode == WB_SYNC_ALL || mpd->wbc->tagged_writepages) tag = PAGECACHE_TAG_TOWRITE; else @@ -6179,6 +6206,13 @@ int ext4_writepage_trans_blocks(struct inode *inode) int bpp = ext4_journal_blocks_per_folio(inode); int ret;
+ /* + * With large folios, blocks per folio can get excessively large, + * especially with small block sizes. Cap it to avoid credit overflow. + */ + if (bpp > MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK) + bpp = MAX_BLOCKS_PER_FOLIO_FOR_WRITEBACK; + ret = ext4_meta_trans_blocks(inode, bpp, bpp);
/* Account for data blocks for journalled mode */