Skip to content

Commit ec60dce

Browse files
Dmitry Monakhovgregkh
Dmitry Monakhov
authored andcommitted
jbd2: fix race between jbd2_journal_remove_checkpoint and ->j_commit_callback
commit 794446c upstream. The following race is possible: [kjournald2] other_task jbd2_journal_commit_transaction() j_state = T_FINISHED; spin_unlock(&journal->j_list_lock); ->jbd2_journal_remove_checkpoint() ->jbd2_journal_free_transaction(); ->kmem_cache_free(transaction) ->j_commit_callback(journal, transaction); -> USE_AFTER_FREE WARNING: at lib/list_debug.c:62 __list_del_entry+0x1c0/0x250() Hardware name: list_del corruption. prev->next should be ffff88019a4ec198, but was 6b6b6b6b6b6b6b6b Modules linked in: cpufreq_ondemand acpi_cpufreq freq_table mperf coretemp kvm_intel kvm crc32c_intel ghash_clmulni_intel microcode sg xhci_hcd button sd_mod crc_t10dif aesni_intel ablk_helper cryptd lrw aes_x86_64 xts gf128mul ahci libahci pata_acpi ata_generic dm_mirror dm_region_hash dm_log dm_mod Pid: 16400, comm: jbd2/dm-1-8 Tainted: G W 3.8.0-rc3+ #107 Call Trace: [<ffffffff8106fb0d>] warn_slowpath_common+0xad/0xf0 [<ffffffff8106fc06>] warn_slowpath_fmt+0x46/0x50 [<ffffffff813637e9>] ? ext4_journal_commit_callback+0x99/0xc0 [<ffffffff8148cae0>] __list_del_entry+0x1c0/0x250 [<ffffffff813637bf>] ext4_journal_commit_callback+0x6f/0xc0 [<ffffffff813ca336>] jbd2_journal_commit_transaction+0x23a6/0x2570 [<ffffffff8108aa42>] ? try_to_del_timer_sync+0x82/0xa0 [<ffffffff8108b491>] ? del_timer_sync+0x91/0x1e0 [<ffffffff813d3ecf>] kjournald2+0x19f/0x6a0 [<ffffffff810ad630>] ? wake_up_bit+0x40/0x40 [<ffffffff813d3d30>] ? bit_spin_lock+0x80/0x80 [<ffffffff810ac6be>] kthread+0x10e/0x120 [<ffffffff810ac5b0>] ? __init_kthread_worker+0x70/0x70 [<ffffffff818ff6ac>] ret_from_fork+0x7c/0xb0 [<ffffffff810ac5b0>] ? __init_kthread_worker+0x70/0x70 In order to demonstrace this issue one should mount ext4 with mount -o discard option on SSD disk. This makes callback longer and race window becomes wider. In order to fix this we should mark transaction as finished only after callbacks have completed Signed-off-by: Dmitry Monakhov <[email protected]> Signed-off-by: "Theodore Ts'o" <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent bf17096 commit ec60dce

File tree

2 files changed

+29
-22
lines changed

2 files changed

+29
-22
lines changed

fs/jbd2/commit.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
382382
int space_left = 0;
383383
int first_tag = 0;
384384
int tag_flag;
385-
int i, to_free = 0;
385+
int i;
386386
int tag_bytes = journal_tag_bytes(journal);
387387
struct buffer_head *cbh = NULL; /* For transactional checksums */
388388
__u32 crc32_sum = ~0;
@@ -1134,7 +1134,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
11341134
journal->j_stats.run.rs_blocks_logged += stats.run.rs_blocks_logged;
11351135
spin_unlock(&journal->j_history_lock);
11361136

1137-
commit_transaction->t_state = T_FINISHED;
1137+
commit_transaction->t_state = T_COMMIT_CALLBACK;
11381138
J_ASSERT(commit_transaction == journal->j_committing_transaction);
11391139
journal->j_commit_sequence = commit_transaction->t_tid;
11401140
journal->j_committing_transaction = NULL;
@@ -1149,38 +1149,44 @@ void jbd2_journal_commit_transaction(journal_t *journal)
11491149
journal->j_average_commit_time*3) / 4;
11501150
else
11511151
journal->j_average_commit_time = commit_time;
1152+
11521153
write_unlock(&journal->j_state_lock);
11531154

1154-
if (commit_transaction->t_checkpoint_list == NULL &&
1155-
commit_transaction->t_checkpoint_io_list == NULL) {
1156-
__jbd2_journal_drop_transaction(journal, commit_transaction);
1157-
to_free = 1;
1155+
if (journal->j_checkpoint_transactions == NULL) {
1156+
journal->j_checkpoint_transactions = commit_transaction;
1157+
commit_transaction->t_cpnext = commit_transaction;
1158+
commit_transaction->t_cpprev = commit_transaction;
11581159
} else {
1159-
if (journal->j_checkpoint_transactions == NULL) {
1160-
journal->j_checkpoint_transactions = commit_transaction;
1161-
commit_transaction->t_cpnext = commit_transaction;
1162-
commit_transaction->t_cpprev = commit_transaction;
1163-
} else {
1164-
commit_transaction->t_cpnext =
1165-
journal->j_checkpoint_transactions;
1166-
commit_transaction->t_cpprev =
1167-
commit_transaction->t_cpnext->t_cpprev;
1168-
commit_transaction->t_cpnext->t_cpprev =
1169-
commit_transaction;
1170-
commit_transaction->t_cpprev->t_cpnext =
1160+
commit_transaction->t_cpnext =
1161+
journal->j_checkpoint_transactions;
1162+
commit_transaction->t_cpprev =
1163+
commit_transaction->t_cpnext->t_cpprev;
1164+
commit_transaction->t_cpnext->t_cpprev =
1165+
commit_transaction;
1166+
commit_transaction->t_cpprev->t_cpnext =
11711167
commit_transaction;
1172-
}
11731168
}
11741169
spin_unlock(&journal->j_list_lock);
1175-
1170+
/* Drop all spin_locks because commit_callback may be block.
1171+
* __journal_remove_checkpoint() can not destroy transaction
1172+
* under us because it is not marked as T_FINISHED yet */
11761173
if (journal->j_commit_callback)
11771174
journal->j_commit_callback(journal, commit_transaction);
11781175

11791176
trace_jbd2_end_commit(journal, commit_transaction);
11801177
jbd_debug(1, "JBD2: commit %d complete, head %d\n",
11811178
journal->j_commit_sequence, journal->j_tail_sequence);
1182-
if (to_free)
1183-
jbd2_journal_free_transaction(commit_transaction);
11841179

1180+
write_lock(&journal->j_state_lock);
1181+
spin_lock(&journal->j_list_lock);
1182+
commit_transaction->t_state = T_FINISHED;
1183+
/* Recheck checkpoint lists after j_list_lock was dropped */
1184+
if (commit_transaction->t_checkpoint_list == NULL &&
1185+
commit_transaction->t_checkpoint_io_list == NULL) {
1186+
__jbd2_journal_drop_transaction(journal, commit_transaction);
1187+
jbd2_journal_free_transaction(commit_transaction);
1188+
}
1189+
spin_unlock(&journal->j_list_lock);
1190+
write_unlock(&journal->j_state_lock);
11851191
wake_up(&journal->j_wait_done_commit);
11861192
}

include/linux/jbd2.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ struct transaction_s
480480
T_COMMIT,
481481
T_COMMIT_DFLUSH,
482482
T_COMMIT_JFLUSH,
483+
T_COMMIT_CALLBACK,
483484
T_FINISHED
484485
} t_state;
485486

0 commit comments

Comments
 (0)