Skip to content

Commit 27ba5b6

Browse files
jankaratytso
authored andcommitted
jbd2: avoid infinite transaction commit loop
Commit 9f356e5 ("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: [email protected] Fixes: 9f356e5 ("jbd2: Account descriptor blocks into t_outstanding_credits") Reported-by: Alexander Coffin <[email protected]> Link: https://lore.kernel.org/all/CA+hUFcuGs04JHZ_WzA1zGN57+ehL2qmHOt5a7RMpo+rv6Vyxtw@mail.gmail.com Signed-off-by: Jan Kara <[email protected]> Reviewed-by: Zhang Yi <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent e3a00a2 commit 27ba5b6

File tree

1 file changed

+14
-7
lines changed

1 file changed

+14
-7
lines changed

fs/jbd2/transaction.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ static void sub_reserved_credits(journal_t *journal, int blocks)
191191
wake_up(&journal->j_wait_reserved);
192192
}
193193

194+
/* Maximum number of blocks for user transaction payload */
195+
static int jbd2_max_user_trans_buffers(journal_t *journal)
196+
{
197+
return journal->j_max_transaction_buffers -
198+
journal->j_transaction_overhead_buffers;
199+
}
200+
194201
/*
195202
* Wait until we can add credits for handle to the running transaction. Called
196203
* with j_state_lock held for reading. Returns 0 if handle joined the running
@@ -240,12 +247,12 @@ __must_hold(&journal->j_state_lock)
240247
* big to fit this handle? Wait until reserved credits are freed.
241248
*/
242249
if (atomic_read(&journal->j_reserved_credits) + total >
243-
journal->j_max_transaction_buffers) {
250+
jbd2_max_user_trans_buffers(journal)) {
244251
read_unlock(&journal->j_state_lock);
245252
jbd2_might_wait_for_commit(journal);
246253
wait_event(journal->j_wait_reserved,
247254
atomic_read(&journal->j_reserved_credits) + total <=
248-
journal->j_max_transaction_buffers);
255+
jbd2_max_user_trans_buffers(journal));
249256
__acquire(&journal->j_state_lock); /* fake out sparse */
250257
return 1;
251258
}
@@ -285,14 +292,14 @@ __must_hold(&journal->j_state_lock)
285292

286293
needed = atomic_add_return(rsv_blocks, &journal->j_reserved_credits);
287294
/* We allow at most half of a transaction to be reserved */
288-
if (needed > journal->j_max_transaction_buffers / 2) {
295+
if (needed > jbd2_max_user_trans_buffers(journal) / 2) {
289296
sub_reserved_credits(journal, rsv_blocks);
290297
atomic_sub(total, &t->t_outstanding_credits);
291298
read_unlock(&journal->j_state_lock);
292299
jbd2_might_wait_for_commit(journal);
293300
wait_event(journal->j_wait_reserved,
294301
atomic_read(&journal->j_reserved_credits) + rsv_blocks
295-
<= journal->j_max_transaction_buffers / 2);
302+
<= jbd2_max_user_trans_buffers(journal) / 2);
296303
__acquire(&journal->j_state_lock); /* fake out sparse */
297304
return 1;
298305
}
@@ -322,12 +329,12 @@ static int start_this_handle(journal_t *journal, handle_t *handle,
322329
* size and limit the number of total credits to not exceed maximum
323330
* transaction size per operation.
324331
*/
325-
if ((rsv_blocks > journal->j_max_transaction_buffers / 2) ||
326-
(rsv_blocks + blocks > journal->j_max_transaction_buffers)) {
332+
if (rsv_blocks > jbd2_max_user_trans_buffers(journal) / 2 ||
333+
rsv_blocks + blocks > jbd2_max_user_trans_buffers(journal)) {
327334
printk(KERN_ERR "JBD2: %s wants too many credits "
328335
"credits:%d rsv_credits:%d max:%d\n",
329336
current->comm, blocks, rsv_blocks,
330-
journal->j_max_transaction_buffers);
337+
jbd2_max_user_trans_buffers(journal));
331338
WARN_ON(1);
332339
return -ENOSPC;
333340
}

0 commit comments

Comments
 (0)