+++ /dev/null
-From 27ba5b67312a944576addc4df44ac3b709aabede Mon Sep 17 00:00:00 2001
-From: Jan Kara <jack@suse.cz>
-Date: Mon, 24 Jun 2024 19:01:19 +0200
-Subject: jbd2: avoid infinite transaction commit loop
-
-From: Jan Kara <jack@suse.cz>
-
-commit 27ba5b67312a944576addc4df44ac3b709aabede upstream.
-
-Commit 9f356e5a4f12 ("jbd2: Account descriptor blocks into
-t_outstanding_credits") started to account descriptor blocks into
-transactions outstanding credits. However it didn't appropriately
-decrease the maximum amount of credits available to userspace. Thus if
-the filesystem requests a transaction smaller than
-j_max_transaction_buffers but large enough that when descriptor blocks
-are added the size exceeds j_max_transaction_buffers, we confuse
-add_transaction_credits() into thinking previous handles have grown the
-transaction too much and enter infinite journal commit loop in
-start_this_handle() -> add_transaction_credits() trying to create
-transaction with enough credits available.
-
-Fix the problem by properly accounting for transaction space reserved
-for descriptor blocks when verifying requested transaction handle size.
-
-CC: stable@vger.kernel.org
-Fixes: 9f356e5a4f12 ("jbd2: Account descriptor blocks into t_outstanding_credits")
-Reported-by: Alexander Coffin <alex.coffin@maticrobots.com>
-Link: https://lore.kernel.org/all/CA+hUFcuGs04JHZ_WzA1zGN57+ehL2qmHOt5a7RMpo+rv6Vyxtw@mail.gmail.com
-Signed-off-by: Jan Kara <jack@suse.cz>
-Reviewed-by: Zhang Yi <yi.zhang@huawei.com>
-Link: https://patch.msgid.link/20240624170127.3253-3-jack@suse.cz
-Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- fs/jbd2/transaction.c | 21 ++++++++++++++-------
- 1 file changed, 14 insertions(+), 7 deletions(-)
-
---- a/fs/jbd2/transaction.c
-+++ b/fs/jbd2/transaction.c
-@@ -213,6 +213,13 @@ static void sub_reserved_credits(journal
- wake_up(&journal->j_wait_reserved);
- }
-
-+/* Maximum number of blocks for user transaction payload */
-+static int jbd2_max_user_trans_buffers(journal_t *journal)
-+{
-+ return journal->j_max_transaction_buffers -
-+ journal->j_transaction_overhead_buffers;
-+}
-+
- /*
- * Wait until we can add credits for handle to the running transaction. Called
- * with j_state_lock held for reading. Returns 0 if handle joined the running
-@@ -262,12 +269,12 @@ __must_hold(&journal->j_state_lock)
- * big to fit this handle? Wait until reserved credits are freed.
- */
- if (atomic_read(&journal->j_reserved_credits) + total >
-- journal->j_max_transaction_buffers) {
-+ jbd2_max_user_trans_buffers(journal)) {
- read_unlock(&journal->j_state_lock);
- jbd2_might_wait_for_commit(journal);
- wait_event(journal->j_wait_reserved,
- atomic_read(&journal->j_reserved_credits) + total <=
-- journal->j_max_transaction_buffers);
-+ jbd2_max_user_trans_buffers(journal));
- __acquire(&journal->j_state_lock); /* fake out sparse */
- return 1;
- }
-@@ -307,14 +314,14 @@ __must_hold(&journal->j_state_lock)
-
- needed = atomic_add_return(rsv_blocks, &journal->j_reserved_credits);
- /* We allow at most half of a transaction to be reserved */
-- if (needed > journal->j_max_transaction_buffers / 2) {
-+ if (needed > jbd2_max_user_trans_buffers(journal) / 2) {
- sub_reserved_credits(journal, rsv_blocks);
- atomic_sub(total, &t->t_outstanding_credits);
- read_unlock(&journal->j_state_lock);
- jbd2_might_wait_for_commit(journal);
- wait_event(journal->j_wait_reserved,
- atomic_read(&journal->j_reserved_credits) + rsv_blocks
-- <= journal->j_max_transaction_buffers / 2);
-+ <= jbd2_max_user_trans_buffers(journal) / 2);
- __acquire(&journal->j_state_lock); /* fake out sparse */
- return 1;
- }
-@@ -344,12 +351,12 @@ static int start_this_handle(journal_t *
- * size and limit the number of total credits to not exceed maximum
- * transaction size per operation.
- */
-- if ((rsv_blocks > journal->j_max_transaction_buffers / 2) ||
-- (rsv_blocks + blocks > journal->j_max_transaction_buffers)) {
-+ if (rsv_blocks > jbd2_max_user_trans_buffers(journal) / 2 ||
-+ rsv_blocks + blocks > jbd2_max_user_trans_buffers(journal)) {
- printk(KERN_ERR "JBD2: %s wants too many credits "
- "credits:%d rsv_credits:%d max:%d\n",
- current->comm, blocks, rsv_blocks,
-- journal->j_max_transaction_buffers);
-+ jbd2_max_user_trans_buffers(journal));
- WARN_ON(1);
- return -ENOSPC;
- }