Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 101086
b: refs/heads/master
c: 530576b
h: refs/heads/master
v: v3
  • Loading branch information
Mingming Cao authored and Theodore Ts'o committed Jul 14, 2008
1 parent 5df9503 commit 2ec7a41
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 5 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 772cb7c83ba256a11c7bf99a11bef3858d23767c
refs/heads/master: 530576bbf379fc45cfb34f246257d8526db44567
62 changes: 58 additions & 4 deletions trunk/fs/jbd2/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh);
* new transaction and we can't block without protecting against other
* processes trying to touch the journal while it is in transition.
*
* Called under j_state_lock
*/

static transaction_t *
Expand Down Expand Up @@ -1656,12 +1655,43 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
return;
}

/*
* jbd2_journal_try_to_free_buffers() could race with
* jbd2_journal_commit_transaction(). The later might still hold the
* reference count to the buffers when inspecting them on
* t_syncdata_list or t_locked_list.
*
* jbd2_journal_try_to_free_buffers() will call this function to
* wait for the current transaction to finish syncing data buffers, before
* try to free that buffer.
*
* Called with journal->j_state_lock hold.
*/
static void jbd2_journal_wait_for_transaction_sync_data(journal_t *journal)
{
transaction_t *transaction;
tid_t tid;

spin_lock(&journal->j_state_lock);
transaction = journal->j_committing_transaction;

if (!transaction) {
spin_unlock(&journal->j_state_lock);
return;
}

tid = transaction->t_tid;
spin_unlock(&journal->j_state_lock);
jbd2_log_wait_commit(journal, tid);
}

/**
* int jbd2_journal_try_to_free_buffers() - try to free page buffers.
* @journal: journal for operation
* @page: to try and free
* @unused_gfp_mask: unused
* @gfp_mask: we use the mask to detect how hard should we try to release
* buffers. If __GFP_WAIT and __GFP_FS is set, we wait for commit code to
* release the buffers.
*
*
* For all the buffers on this page,
Expand Down Expand Up @@ -1690,9 +1720,11 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
* journal_try_to_free_buffer() is changing its state. But that
* cannot happen because we never reallocate freed data as metadata
* while the data is part of a transaction. Yes?
*
* Return 0 on failure, 1 on success
*/
int jbd2_journal_try_to_free_buffers(journal_t *journal,
struct page *page, gfp_t unused_gfp_mask)
struct page *page, gfp_t gfp_mask)
{
struct buffer_head *head;
struct buffer_head *bh;
Expand All @@ -1708,7 +1740,8 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal,
/*
* We take our own ref against the journal_head here to avoid
* having to add tons of locking around each instance of
* jbd2_journal_remove_journal_head() and jbd2_journal_put_journal_head().
* jbd2_journal_remove_journal_head() and
* jbd2_journal_put_journal_head().
*/
jh = jbd2_journal_grab_journal_head(bh);
if (!jh)
Expand All @@ -1721,7 +1754,28 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal,
if (buffer_jbd(bh))
goto busy;
} while ((bh = bh->b_this_page) != head);

ret = try_to_free_buffers(page);

/*
* There are a number of places where jbd2_journal_try_to_free_buffers()
* could race with jbd2_journal_commit_transaction(), the later still
* holds the reference to the buffers to free while processing them.
* try_to_free_buffers() failed to free those buffers. Some of the
* caller of releasepage() request page buffers to be dropped, otherwise
* treat the fail-to-free as errors (such as generic_file_direct_IO())
*
* So, if the caller of try_to_release_page() wants the synchronous
* behaviour(i.e make sure buffers are dropped upon return),
* let's wait for the current transaction to finish flush of
* dirty data buffers, then try to free those buffers again,
* with the journal locked.
*/
if (ret == 0 && (gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_FS)) {
jbd2_journal_wait_for_transaction_sync_data(journal);
ret = try_to_free_buffers(page);
}

busy:
return ret;
}
Expand Down

0 comments on commit 2ec7a41

Please sign in to comment.