fs/jbd/journal.c | 17 +++++++++++++++++ fs/jbd/transaction.c | 31 ++++++++++++++++--------------- include/linux/jbd.h | 8 ++++---- 3 files changed, 37 insertions(+), 19 deletions(-) diff -puN fs/jbd/transaction.c~ext3-035-journal_try_to_free_buffers-race-fix fs/jbd/transaction.c --- 25/fs/jbd/transaction.c~ext3-035-journal_try_to_free_buffers-race-fix 2003-06-02 22:39:03.000000000 -0700 +++ 25-akpm/fs/jbd/transaction.c 2003-06-02 22:39:03.000000000 -0700 @@ -1553,10 +1553,8 @@ void journal_unfile_buffer(journal_t *jo * Called from journal_try_to_free_buffers(). * * Called under jbd_lock_bh_state(bh) - * - * Returns non-zero iff we were able to free the journal_head. */ -static inline int +static void __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh) { struct journal_head *jh; @@ -1589,10 +1587,8 @@ __journal_try_to_free_buffer(journal_t * } } spin_unlock(&journal->j_list_lock); - return !buffer_jbd(bh); - out: - return 0; + return; } @@ -1642,18 +1638,23 @@ int journal_try_to_free_buffers(journal_ head = page_buffers(page); bh = head; do { - jbd_lock_bh_state(bh); + struct journal_head *jh; + /* - * We don't have to worry about the buffer being pulled off its - * journal_head in here, because __try_to_free_cp_buf runs - * under jbd_lock_bh_state() + * We take our own ref against the journal_head here to avoid + * having to add tons of locking around each instance of + * journal_remove_journal_head() and journal_put_journal_head(). */ - if (buffer_jbd(bh) && - !__journal_try_to_free_buffer(journal, bh)) { - jbd_unlock_bh_state(bh); - goto busy; - } + jh = journal_grab_journal_head(bh); + if (!jh) + continue; + + jbd_lock_bh_state(bh); + __journal_try_to_free_buffer(journal, bh); + journal_put_journal_head(jh); jbd_unlock_bh_state(bh); + if (buffer_jbd(bh)) + goto busy; } while ((bh = bh->b_this_page) != head); ret = try_to_free_buffers(page); busy: diff -puN fs/jbd/journal.c~ext3-035-journal_try_to_free_buffers-race-fix fs/jbd/journal.c --- 25/fs/jbd/journal.c~ext3-035-journal_try_to_free_buffers-race-fix 2003-06-02 22:39:03.000000000 -0700 +++ 25-akpm/fs/jbd/journal.c 2003-06-02 22:39:03.000000000 -0700 @@ -1679,6 +1679,23 @@ repeat: return bh->b_private; } +/* + * Grab a ref against this buffer_head's journal_head. If it ended up not + * having a journal_head, return NULL + */ +struct journal_head *journal_grab_journal_head(struct buffer_head *bh) +{ + struct journal_head *jh = NULL; + + jbd_lock_bh_journal_head(bh); + if (buffer_jbd(bh)) { + jh = bh2jh(bh); + jh->b_jcount++; + } + jbd_unlock_bh_journal_head(bh); + return jh; +} + static void __journal_remove_journal_head(struct buffer_head *bh) { struct journal_head *jh = bh2jh(bh); diff -puN include/linux/jbd.h~ext3-035-journal_try_to_free_buffers-race-fix include/linux/jbd.h --- 25/include/linux/jbd.h~ext3-035-journal_try_to_free_buffers-race-fix 2003-06-02 22:39:03.000000000 -0700 +++ 25-akpm/include/linux/jbd.h 2003-06-02 22:39:03.000000000 -0700 @@ -943,10 +943,10 @@ extern int journal_force_commit(journ /* * journal_head management */ -extern struct journal_head - *journal_add_journal_head(struct buffer_head *bh); -extern void journal_remove_journal_head(struct buffer_head *bh); -extern void journal_put_journal_head(struct journal_head *jh); +struct journal_head *journal_add_journal_head(struct buffer_head *bh); +struct journal_head *journal_grab_journal_head(struct buffer_head *bh); +void journal_remove_journal_head(struct buffer_head *bh); +void journal_put_journal_head(struct journal_head *jh); /* * handle management _