diff --git a/fs/buffer.c b/fs/buffer.c index 9e1e2add541e..26ede47e7a81 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2756,10 +2756,13 @@ void submit_bh(blk_opf_t opf, struct buffer_head *bh) } EXPORT_SYMBOL(submit_bh); +extern struct buffer_head *g_bh; void write_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags) { lock_buffer(bh); if (!test_clear_buffer_dirty(bh)) { + if (g_bh == bh) + pr_err("%s(%d): skip buffer submit\n", current->comm, current->pid); unlock_buffer(bh); return; } diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a5010b5b8a8c..a366a6ee10fb 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2147,6 +2147,8 @@ void ext4_insert_dentry(struct inode *dir, * space. It will return -ENOSPC if no space is available, and -EIO * and -EEXIST if directory entry already exists. */ +struct buffer_head *g_bh; +int g_bh_dirty; static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, struct inode *dir, struct inode *inode, struct ext4_dir_entry_2 *de, @@ -2195,6 +2197,14 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, err = ext4_handle_dirty_dirblock(handle, dir, bh); if (err) ext4_std_error(dir->i_sb, err); + + smp_rmb(); + if (!g_bh && !strcmp(current->comm, "aa")) { + pr_err("assign g_bh, trace dir %llu buffer, add %s(%llu)\n", dir->i_ino, fname_name(fname), inode->i_ino); + g_bh = bh; + smp_wmb(); + } + return err ? err : err2; } diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 51bd38da21cd..21c48c0ee648 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -97,15 +97,17 @@ static inline bool __cp_buffer_busy(struct journal_head *jh) * Called under j-state_lock *only*. It will be unlocked if we have to wait * for a checkpoint to free up some space in the log. */ +extern int g_bh_dirty; void __jbd2_log_wait_for_space(journal_t *journal) __acquires(&journal->j_state_lock) __releases(&journal->j_state_lock) { int nblocks, space_left; + static int entered = 0; /* assert_spin_locked(&journal->j_state_lock); */ nblocks = journal->j_max_transaction_buffers; - while (jbd2_log_space_left(journal) < nblocks) { + while (jbd2_log_space_left(journal) < nblocks || (!strcmp(current->comm, "chmod") && g_bh_dirty && !entered)) { write_unlock(&journal->j_state_lock); mutex_lock_io(&journal->j_checkpoint_mutex); @@ -127,7 +129,7 @@ __releases(&journal->j_state_lock) } spin_lock(&journal->j_list_lock); space_left = jbd2_log_space_left(journal); - if (space_left < nblocks) { + if (space_left < nblocks || (!strcmp(current->comm, "chmod") && g_bh_dirty)) { int chkpt = journal->j_checkpoint_transactions != NULL; tid_t tid = 0; @@ -136,6 +138,7 @@ __releases(&journal->j_state_lock) spin_unlock(&journal->j_list_lock); write_unlock(&journal->j_state_lock); if (chkpt) { + entered = 1; jbd2_log_do_checkpoint(journal); } else if (jbd2_cleanup_journal_tail(journal) == 0) { /* We were able to recover space; yay! */ @@ -195,6 +198,8 @@ __flush_batch(journal_t *journal, int *batch_count) * The journal should be locked before calling this function. * Called with j_checkpoint_mutex held. */ +#include +extern struct buffer_head *g_bh; int jbd2_log_do_checkpoint(journal_t *journal) { struct journal_head *jh; @@ -202,6 +207,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) transaction_t *transaction; tid_t this_tid; int result, batch_count = 0; + int need_shutdown = 0; jbd2_debug(1, "Start checkpoint\n"); @@ -227,6 +233,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) if (transaction->t_chp_stats.cs_chp_time == 0) transaction->t_chp_stats.cs_chp_time = jiffies; this_tid = transaction->t_tid; + pr_err("%s(%d): commit %px\n", current->comm, current->pid, transaction); restart: /* * If someone cleaned up this transaction while we slept, we're @@ -237,6 +244,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) transaction->t_tid != this_tid) goto out; + smp_rmb(); /* checkpoint all of the transaction's buffers */ while (transaction->t_checkpoint_list) { jh = transaction->t_checkpoint_list; @@ -255,6 +263,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) transaction_t *t = jh->b_transaction; tid_t tid = t->t_tid; + pr_err("buffer block %llu\n", bh->b_blocknr); + transaction->t_chp_stats.cs_forced_to_close++; spin_unlock(&journal->j_list_lock); if (unlikely(journal->j_flags & JBD2_UNMOUNT)) @@ -285,6 +295,10 @@ int jbd2_log_do_checkpoint(journal_t *journal) spin_lock(&journal->j_list_lock); goto restart; } + if (g_bh == bh) { + pr_err("checkpoint: bh dirty bit should be cleared before flush\n"); + need_shutdown = 1; + } if (!buffer_dirty(bh)) { BUFFER_TRACE(bh, "remove from checkpoint"); if (__jbd2_journal_remove_checkpoint(jh)) @@ -311,11 +325,16 @@ int jbd2_log_do_checkpoint(journal_t *journal) spin_needbreak(&journal->j_list_lock)) goto unlock_and_flush; } + pr_err("commit %px done\n", transaction); if (batch_count) { unlock_and_flush: spin_unlock(&journal->j_list_lock); retry: + if (batch_count && need_shutdown) { + pr_err("Do access to bh\n"); + msleep(2000); + } if (batch_count) __flush_batch(journal, &batch_count); spin_lock(&journal->j_list_lock); @@ -357,6 +376,8 @@ int jbd2_log_do_checkpoint(journal_t *journal) out: spin_unlock(&journal->j_list_lock); result = jbd2_cleanup_journal_tail(journal); + if (need_shutdown) + panic("DONE"); return (result < 0) ? result : 0; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index b33155dd7001..d91cd4a73cda 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -348,6 +348,7 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, * The primary function for committing a transaction to the log. This * function is called by the journal thread to begin a complete commit. */ +extern struct buffer_head *g_bh; void jbd2_journal_commit_transaction(journal_t *journal) { struct transaction_stats_s stats; @@ -925,6 +926,8 @@ void jbd2_journal_commit_transaction(journal_t *journal) * As there are other places (journal_unmap_buffer()) adding buffers * to this list we have to be careful and hold the j_list_lock. */ + if (!strcmp(journal->j_sb_buffer->b_bdev->bd_disk->disk_name, "sda")) + pr_err("%s ---\n", __func__); spin_lock(&journal->j_list_lock); while (commit_transaction->t_forget) { transaction_t *cp_transaction; @@ -1023,6 +1026,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) if (buffer_jbddirty(bh)) { JBUFFER_TRACE(jh, "add to new checkpointing trans"); __jbd2_journal_insert_checkpoint(jh, commit_transaction); + smp_rmb(); + if (g_bh == bh) + pr_err("insert %px into trans %px\n", g_bh, commit_transaction); if (is_journal_aborted(journal)) clear_buffer_jbddirty(bh); } else { @@ -1150,6 +1156,8 @@ void jbd2_journal_commit_transaction(journal_t *journal) write_unlock(&journal->j_state_lock); wake_up(&journal->j_wait_done_commit); wake_up(&journal->j_fc_wait); + if (!strcmp(journal->j_sb_buffer->b_bdev->bd_disk->disk_name, "sda")) + pr_err("%s ====\n", __func__); /* * Calculate overall stats diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 15de1385012e..488de09b5de6 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -224,6 +224,7 @@ static void sub_reserved_credits(journal_t *journal, int blocks) * locking imbalance. Callers of add_transaction_credits will need to * make a similar accomodation. */ +extern int g_bh_dirty; static int add_transaction_credits(journal_t *journal, int blocks, int rsv_blocks) __must_hold(&journal->j_state_lock) @@ -231,6 +232,7 @@ __must_hold(&journal->j_state_lock) transaction_t *t = journal->j_running_transaction; int needed; int total = blocks + rsv_blocks; + static int entered = 0; /* * If the current transaction is locked down for commit, wait @@ -288,13 +290,14 @@ __must_hold(&journal->j_state_lock) * *before* starting to dirty potentially checkpointed buffers * in the new transaction. */ - if (jbd2_log_space_left(journal) < journal->j_max_transaction_buffers) { + if (jbd2_log_space_left(journal) < journal->j_max_transaction_buffers || (!strcmp(current->comm, "chmod") && g_bh_dirty && !entered)) { + entered = 1; atomic_sub(total, &t->t_outstanding_credits); read_unlock(&journal->j_state_lock); jbd2_might_wait_for_commit(journal); write_lock(&journal->j_state_lock); if (jbd2_log_space_left(journal) < - journal->j_max_transaction_buffers) + journal->j_max_transaction_buffers || (!strcmp(current->comm, "chmod") && g_bh_dirty)) __jbd2_log_wait_for_space(journal); write_unlock(&journal->j_state_lock); __acquire(&journal->j_state_lock); /* fake out sparse */ @@ -966,6 +969,9 @@ static void jbd2_freeze_jh_data(struct journal_head *jh) * part of the transaction, that is). * */ +#include +extern struct buffer_head *g_bh; + static int do_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy) @@ -976,6 +982,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, int error; char *frozen_buffer = NULL; unsigned long start_lock, time_lock; + int happened = 0; journal = transaction->t_journal; @@ -1078,6 +1085,11 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, */ JBUFFER_TRACE(jh, "Journalling dirty buffer"); set_buffer_jbddirty(bh); + smp_rmb(); + if (g_bh_dirty && g_bh == bh) { + pr_err("%s: clear bh dirty\n", __func__); + happened = 1; + } } __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); spin_unlock(&journal->j_list_lock); @@ -1086,6 +1098,12 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, } unlock_buffer(bh); + if (happened) { + pr_err("%s: wait checkpoint submit\n", __func__); + mdelay(5000); + pr_err("%s: checkpoint submit done\n", __func__); + } + /* * If there is already a copy-out version of this buffer, then we don't * need to make another one @@ -2066,8 +2084,14 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) jh->b_jlist = BJ_None; if (transaction && is_journal_aborted(transaction->t_journal)) clear_buffer_jbddirty(bh); - else if (test_clear_buffer_jbddirty(bh)) + else if (test_clear_buffer_jbddirty(bh)) { mark_buffer_dirty(bh); /* Expose it to the VM */ + smp_rmb(); + if (g_bh == bh) { + pr_err("mark buffer dirty\n"); + g_bh_dirty = 1; + } + } } /*