mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-05-02 18:17:50 -04:00
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o:
"This merge window saw the the following new featuers added to ext4:
- Direct I/O via iomap (required the iomap-for-next branch from
Darrick as a prereq).
- Support for using dioread-nolock where the block size < page size.
- Support for encryption for file systems where the block size < page
size.
- Rework of journal credits handling so a revoke-heavy workload will
not cause the journal to run out of space.
- Replace bit-spinlocks with spinlocks in jbd2
Also included were some bug fixes and cleanups, mostly to clean up
corner cases from fuzzed file systems and error path handling"
* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (59 commits)
ext4: work around deleting a file with i_nlink == 0 safely
ext4: add more paranoia checking in ext4_expand_extra_isize handling
jbd2: make jbd2_handle_buffer_credits() handle reserved handles
ext4: fix a bug in ext4_wait_for_tail_page_commit
ext4: bio_alloc with __GFP_DIRECT_RECLAIM never fails
ext4: code cleanup for get_next_id
ext4: fix leak of quota reservations
ext4: remove unused variable warning in parse_options()
ext4: Enable encryption for subpage-sized blocks
fs/buffer.c: support fscrypt in block_read_full_page()
ext4: Add error handling for io_end_vec struct allocation
jbd2: Fine tune estimate of necessary descriptor blocks
jbd2: Provide trace event for handle restarts
ext4: Reserve revoke credits for freed blocks
jbd2: Make credit checking more strict
jbd2: Rename h_buffer_credits to h_total_credits
jbd2: Reserve space for revoke descriptor blocks
jbd2: Drop jbd2_space_needed()
jbd2: Account descriptor blocks into t_outstanding_credits
jbd2: Factor out common parts of stopping and restarting a handle
...
This commit is contained in:
@@ -62,6 +62,28 @@ void jbd2_journal_free_transaction(transaction_t *transaction)
|
||||
kmem_cache_free(transaction_cache, transaction);
|
||||
}
|
||||
|
||||
/*
|
||||
* Base amount of descriptor blocks we reserve for each transaction.
|
||||
*/
|
||||
static int jbd2_descriptor_blocks_per_trans(journal_t *journal)
|
||||
{
|
||||
int tag_space = journal->j_blocksize - sizeof(journal_header_t);
|
||||
int tags_per_block;
|
||||
|
||||
/* Subtract UUID */
|
||||
tag_space -= 16;
|
||||
if (jbd2_journal_has_csum_v2or3(journal))
|
||||
tag_space -= sizeof(struct jbd2_journal_block_tail);
|
||||
/* Commit code leaves a slack space of 16 bytes at the end of block */
|
||||
tags_per_block = (tag_space - 16) / journal_tag_bytes(journal);
|
||||
/*
|
||||
* Revoke descriptors are accounted separately so we need to reserve
|
||||
* space for commit block and normal transaction descriptor blocks.
|
||||
*/
|
||||
return 1 + DIV_ROUND_UP(journal->j_max_transaction_buffers,
|
||||
tags_per_block);
|
||||
}
|
||||
|
||||
/*
|
||||
* jbd2_get_transaction: obtain a new transaction_t object.
|
||||
*
|
||||
@@ -88,7 +110,9 @@ static void jbd2_get_transaction(journal_t *journal,
|
||||
spin_lock_init(&transaction->t_handle_lock);
|
||||
atomic_set(&transaction->t_updates, 0);
|
||||
atomic_set(&transaction->t_outstanding_credits,
|
||||
jbd2_descriptor_blocks_per_trans(journal) +
|
||||
atomic_read(&journal->j_reserved_credits));
|
||||
atomic_set(&transaction->t_outstanding_revokes, 0);
|
||||
atomic_set(&transaction->t_handle_count, 0);
|
||||
INIT_LIST_HEAD(&transaction->t_inode_list);
|
||||
INIT_LIST_HEAD(&transaction->t_private_list);
|
||||
@@ -258,12 +282,13 @@ static int add_transaction_credits(journal_t *journal, int blocks,
|
||||
* *before* starting to dirty potentially checkpointed buffers
|
||||
* in the new transaction.
|
||||
*/
|
||||
if (jbd2_log_space_left(journal) < jbd2_space_needed(journal)) {
|
||||
if (jbd2_log_space_left(journal) < journal->j_max_transaction_buffers) {
|
||||
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) < jbd2_space_needed(journal))
|
||||
if (jbd2_log_space_left(journal) <
|
||||
journal->j_max_transaction_buffers)
|
||||
__jbd2_log_wait_for_space(journal);
|
||||
write_unlock(&journal->j_state_lock);
|
||||
return 1;
|
||||
@@ -299,12 +324,12 @@ static int start_this_handle(journal_t *journal, handle_t *handle,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
transaction_t *transaction, *new_transaction = NULL;
|
||||
int blocks = handle->h_buffer_credits;
|
||||
int blocks = handle->h_total_credits;
|
||||
int rsv_blocks = 0;
|
||||
unsigned long ts = jiffies;
|
||||
|
||||
if (handle->h_rsv_handle)
|
||||
rsv_blocks = handle->h_rsv_handle->h_buffer_credits;
|
||||
rsv_blocks = handle->h_rsv_handle->h_total_credits;
|
||||
|
||||
/*
|
||||
* Limit the number of reserved credits to 1/2 of maximum transaction
|
||||
@@ -405,6 +430,7 @@ repeat:
|
||||
update_t_max_wait(transaction, ts);
|
||||
handle->h_transaction = transaction;
|
||||
handle->h_requested_credits = blocks;
|
||||
handle->h_revoke_credits_requested = handle->h_revoke_credits;
|
||||
handle->h_start_jiffies = jiffies;
|
||||
atomic_inc(&transaction->t_updates);
|
||||
atomic_inc(&transaction->t_handle_count);
|
||||
@@ -431,15 +457,15 @@ static handle_t *new_handle(int nblocks)
|
||||
handle_t *handle = jbd2_alloc_handle(GFP_NOFS);
|
||||
if (!handle)
|
||||
return NULL;
|
||||
handle->h_buffer_credits = nblocks;
|
||||
handle->h_total_credits = nblocks;
|
||||
handle->h_ref = 1;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks,
|
||||
gfp_t gfp_mask, unsigned int type,
|
||||
unsigned int line_no)
|
||||
int revoke_records, gfp_t gfp_mask,
|
||||
unsigned int type, unsigned int line_no)
|
||||
{
|
||||
handle_t *handle = journal_current_handle();
|
||||
int err;
|
||||
@@ -453,6 +479,8 @@ handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks,
|
||||
return handle;
|
||||
}
|
||||
|
||||
nblocks += DIV_ROUND_UP(revoke_records,
|
||||
journal->j_revoke_records_per_block);
|
||||
handle = new_handle(nblocks);
|
||||
if (!handle)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@@ -468,6 +496,7 @@ handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks,
|
||||
rsv_handle->h_journal = journal;
|
||||
handle->h_rsv_handle = rsv_handle;
|
||||
}
|
||||
handle->h_revoke_credits = revoke_records;
|
||||
|
||||
err = start_this_handle(journal, handle, gfp_mask);
|
||||
if (err < 0) {
|
||||
@@ -508,16 +537,21 @@ EXPORT_SYMBOL(jbd2__journal_start);
|
||||
*/
|
||||
handle_t *jbd2_journal_start(journal_t *journal, int nblocks)
|
||||
{
|
||||
return jbd2__journal_start(journal, nblocks, 0, GFP_NOFS, 0, 0);
|
||||
return jbd2__journal_start(journal, nblocks, 0, 0, GFP_NOFS, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(jbd2_journal_start);
|
||||
|
||||
void jbd2_journal_free_reserved(handle_t *handle)
|
||||
static void __jbd2_journal_unreserve_handle(handle_t *handle)
|
||||
{
|
||||
journal_t *journal = handle->h_journal;
|
||||
|
||||
WARN_ON(!handle->h_reserved);
|
||||
sub_reserved_credits(journal, handle->h_buffer_credits);
|
||||
sub_reserved_credits(journal, handle->h_total_credits);
|
||||
}
|
||||
|
||||
void jbd2_journal_free_reserved(handle_t *handle)
|
||||
{
|
||||
__jbd2_journal_unreserve_handle(handle);
|
||||
jbd2_free_handle(handle);
|
||||
}
|
||||
EXPORT_SYMBOL(jbd2_journal_free_reserved);
|
||||
@@ -571,7 +605,7 @@ int jbd2_journal_start_reserved(handle_t *handle, unsigned int type,
|
||||
handle->h_line_no = line_no;
|
||||
trace_jbd2_handle_start(journal->j_fs_dev->bd_dev,
|
||||
handle->h_transaction->t_tid, type,
|
||||
line_no, handle->h_buffer_credits);
|
||||
line_no, handle->h_total_credits);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(jbd2_journal_start_reserved);
|
||||
@@ -580,6 +614,7 @@ EXPORT_SYMBOL(jbd2_journal_start_reserved);
|
||||
* int jbd2_journal_extend() - extend buffer credits.
|
||||
* @handle: handle to 'extend'
|
||||
* @nblocks: nr blocks to try to extend by.
|
||||
* @revoke_records: number of revoke records to try to extend by.
|
||||
*
|
||||
* Some transactions, such as large extends and truncates, can be done
|
||||
* atomically all at once or in several stages. The operation requests
|
||||
@@ -596,7 +631,7 @@ EXPORT_SYMBOL(jbd2_journal_start_reserved);
|
||||
* return code < 0 implies an error
|
||||
* return code > 0 implies normal transaction-full status.
|
||||
*/
|
||||
int jbd2_journal_extend(handle_t *handle, int nblocks)
|
||||
int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records)
|
||||
{
|
||||
transaction_t *transaction = handle->h_transaction;
|
||||
journal_t *journal;
|
||||
@@ -618,6 +653,12 @@ int jbd2_journal_extend(handle_t *handle, int nblocks)
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
nblocks += DIV_ROUND_UP(
|
||||
handle->h_revoke_credits_requested + revoke_records,
|
||||
journal->j_revoke_records_per_block) -
|
||||
DIV_ROUND_UP(
|
||||
handle->h_revoke_credits_requested,
|
||||
journal->j_revoke_records_per_block);
|
||||
spin_lock(&transaction->t_handle_lock);
|
||||
wanted = atomic_add_return(nblocks,
|
||||
&transaction->t_outstanding_credits);
|
||||
@@ -629,22 +670,16 @@ int jbd2_journal_extend(handle_t *handle, int nblocks)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (wanted + (wanted >> JBD2_CONTROL_BLOCKS_SHIFT) >
|
||||
jbd2_log_space_left(journal)) {
|
||||
jbd_debug(3, "denied handle %p %d blocks: "
|
||||
"insufficient log space\n", handle, nblocks);
|
||||
atomic_sub(nblocks, &transaction->t_outstanding_credits);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
trace_jbd2_handle_extend(journal->j_fs_dev->bd_dev,
|
||||
transaction->t_tid,
|
||||
handle->h_type, handle->h_line_no,
|
||||
handle->h_buffer_credits,
|
||||
handle->h_total_credits,
|
||||
nblocks);
|
||||
|
||||
handle->h_buffer_credits += nblocks;
|
||||
handle->h_total_credits += nblocks;
|
||||
handle->h_requested_credits += nblocks;
|
||||
handle->h_revoke_credits += revoke_records;
|
||||
handle->h_revoke_credits_requested += revoke_records;
|
||||
result = 0;
|
||||
|
||||
jbd_debug(3, "extended handle %p by %d\n", handle, nblocks);
|
||||
@@ -655,11 +690,55 @@ error_out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void stop_this_handle(handle_t *handle)
|
||||
{
|
||||
transaction_t *transaction = handle->h_transaction;
|
||||
journal_t *journal = transaction->t_journal;
|
||||
int revokes;
|
||||
|
||||
J_ASSERT(journal_current_handle() == handle);
|
||||
J_ASSERT(atomic_read(&transaction->t_updates) > 0);
|
||||
current->journal_info = NULL;
|
||||
/*
|
||||
* Subtract necessary revoke descriptor blocks from handle credits. We
|
||||
* take care to account only for revoke descriptor blocks the
|
||||
* transaction will really need as large sequences of transactions with
|
||||
* small numbers of revokes are relatively common.
|
||||
*/
|
||||
revokes = handle->h_revoke_credits_requested - handle->h_revoke_credits;
|
||||
if (revokes) {
|
||||
int t_revokes, revoke_descriptors;
|
||||
int rr_per_blk = journal->j_revoke_records_per_block;
|
||||
|
||||
WARN_ON_ONCE(DIV_ROUND_UP(revokes, rr_per_blk)
|
||||
> handle->h_total_credits);
|
||||
t_revokes = atomic_add_return(revokes,
|
||||
&transaction->t_outstanding_revokes);
|
||||
revoke_descriptors =
|
||||
DIV_ROUND_UP(t_revokes, rr_per_blk) -
|
||||
DIV_ROUND_UP(t_revokes - revokes, rr_per_blk);
|
||||
handle->h_total_credits -= revoke_descriptors;
|
||||
}
|
||||
atomic_sub(handle->h_total_credits,
|
||||
&transaction->t_outstanding_credits);
|
||||
if (handle->h_rsv_handle)
|
||||
__jbd2_journal_unreserve_handle(handle->h_rsv_handle);
|
||||
if (atomic_dec_and_test(&transaction->t_updates))
|
||||
wake_up(&journal->j_wait_updates);
|
||||
|
||||
rwsem_release(&journal->j_trans_commit_map, _THIS_IP_);
|
||||
/*
|
||||
* Scope of the GFP_NOFS context is over here and so we can restore the
|
||||
* original alloc context.
|
||||
*/
|
||||
memalloc_nofs_restore(handle->saved_alloc_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* int jbd2_journal_restart() - restart a handle .
|
||||
* @handle: handle to restart
|
||||
* @nblocks: nr credits requested
|
||||
* @revoke_records: number of revoke record credits requested
|
||||
* @gfp_mask: memory allocation flags (for start_this_handle)
|
||||
*
|
||||
* Restart a handle for a multi-transaction filesystem
|
||||
@@ -672,56 +751,48 @@ error_out:
|
||||
* credits. We preserve reserved handle if there's any attached to the
|
||||
* passed in handle.
|
||||
*/
|
||||
int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask)
|
||||
int jbd2__journal_restart(handle_t *handle, int nblocks, int revoke_records,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
transaction_t *transaction = handle->h_transaction;
|
||||
journal_t *journal;
|
||||
tid_t tid;
|
||||
int need_to_start, ret;
|
||||
int need_to_start;
|
||||
int ret;
|
||||
|
||||
/* If we've had an abort of any type, don't even think about
|
||||
* actually doing the restart! */
|
||||
if (is_handle_aborted(handle))
|
||||
return 0;
|
||||
journal = transaction->t_journal;
|
||||
tid = transaction->t_tid;
|
||||
|
||||
/*
|
||||
* First unlink the handle from its current transaction, and start the
|
||||
* commit on that.
|
||||
*/
|
||||
J_ASSERT(atomic_read(&transaction->t_updates) > 0);
|
||||
J_ASSERT(journal_current_handle() == handle);
|
||||
|
||||
read_lock(&journal->j_state_lock);
|
||||
spin_lock(&transaction->t_handle_lock);
|
||||
atomic_sub(handle->h_buffer_credits,
|
||||
&transaction->t_outstanding_credits);
|
||||
if (handle->h_rsv_handle) {
|
||||
sub_reserved_credits(journal,
|
||||
handle->h_rsv_handle->h_buffer_credits);
|
||||
}
|
||||
if (atomic_dec_and_test(&transaction->t_updates))
|
||||
wake_up(&journal->j_wait_updates);
|
||||
tid = transaction->t_tid;
|
||||
spin_unlock(&transaction->t_handle_lock);
|
||||
handle->h_transaction = NULL;
|
||||
current->journal_info = NULL;
|
||||
|
||||
jbd_debug(2, "restarting handle %p\n", handle);
|
||||
stop_this_handle(handle);
|
||||
handle->h_transaction = NULL;
|
||||
|
||||
/*
|
||||
* TODO: If we use READ_ONCE / WRITE_ONCE for j_commit_request we can
|
||||
* get rid of pointless j_state_lock traffic like this.
|
||||
*/
|
||||
read_lock(&journal->j_state_lock);
|
||||
need_to_start = !tid_geq(journal->j_commit_request, tid);
|
||||
read_unlock(&journal->j_state_lock);
|
||||
if (need_to_start)
|
||||
jbd2_log_start_commit(journal, tid);
|
||||
|
||||
rwsem_release(&journal->j_trans_commit_map, _THIS_IP_);
|
||||
handle->h_buffer_credits = nblocks;
|
||||
/*
|
||||
* Restore the original nofs context because the journal restart
|
||||
* is basically the same thing as journal stop and start.
|
||||
* start_this_handle will start a new nofs context.
|
||||
*/
|
||||
memalloc_nofs_restore(handle->saved_alloc_context);
|
||||
handle->h_total_credits = nblocks +
|
||||
DIV_ROUND_UP(revoke_records,
|
||||
journal->j_revoke_records_per_block);
|
||||
handle->h_revoke_credits = revoke_records;
|
||||
ret = start_this_handle(journal, handle, gfp_mask);
|
||||
trace_jbd2_handle_restart(journal->j_fs_dev->bd_dev,
|
||||
ret ? 0 : handle->h_transaction->t_tid,
|
||||
handle->h_type, handle->h_line_no,
|
||||
handle->h_total_credits);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(jbd2__journal_restart);
|
||||
@@ -729,7 +800,7 @@ EXPORT_SYMBOL(jbd2__journal_restart);
|
||||
|
||||
int jbd2_journal_restart(handle_t *handle, int nblocks)
|
||||
{
|
||||
return jbd2__journal_restart(handle, nblocks, GFP_NOFS);
|
||||
return jbd2__journal_restart(handle, nblocks, 0, GFP_NOFS);
|
||||
}
|
||||
EXPORT_SYMBOL(jbd2_journal_restart);
|
||||
|
||||
@@ -879,7 +950,7 @@ repeat:
|
||||
|
||||
start_lock = jiffies;
|
||||
lock_buffer(bh);
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
|
||||
/* If it takes too long to lock the buffer, trace it */
|
||||
time_lock = jbd2_time_diff(start_lock, jiffies);
|
||||
@@ -929,7 +1000,7 @@ repeat:
|
||||
|
||||
error = -EROFS;
|
||||
if (is_handle_aborted(handle)) {
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
goto out;
|
||||
}
|
||||
error = 0;
|
||||
@@ -993,7 +1064,7 @@ repeat:
|
||||
*/
|
||||
if (buffer_shadow(bh)) {
|
||||
JBUFFER_TRACE(jh, "on shadow: sleep");
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
wait_on_bit_io(&bh->b_state, BH_Shadow, TASK_UNINTERRUPTIBLE);
|
||||
goto repeat;
|
||||
}
|
||||
@@ -1014,7 +1085,7 @@ repeat:
|
||||
JBUFFER_TRACE(jh, "generate frozen data");
|
||||
if (!frozen_buffer) {
|
||||
JBUFFER_TRACE(jh, "allocate memory for buffer");
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
frozen_buffer = jbd2_alloc(jh2bh(jh)->b_size,
|
||||
GFP_NOFS | __GFP_NOFAIL);
|
||||
goto repeat;
|
||||
@@ -1033,7 +1104,7 @@ attach_next:
|
||||
jh->b_next_transaction = transaction;
|
||||
|
||||
done:
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
|
||||
/*
|
||||
* If we are about to journal a buffer, then any revoke pending on it is
|
||||
@@ -1172,7 +1243,7 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
|
||||
* that case: the transaction must have deleted the buffer for it to be
|
||||
* reused here.
|
||||
*/
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
J_ASSERT_JH(jh, (jh->b_transaction == transaction ||
|
||||
jh->b_transaction == NULL ||
|
||||
(jh->b_transaction == journal->j_committing_transaction &&
|
||||
@@ -1207,7 +1278,7 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
|
||||
jh->b_next_transaction = transaction;
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
}
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
|
||||
/*
|
||||
* akpm: I added this. ext3_alloc_branch can pick up new indirect
|
||||
@@ -1275,13 +1346,13 @@ repeat:
|
||||
committed_data = jbd2_alloc(jh2bh(jh)->b_size,
|
||||
GFP_NOFS|__GFP_NOFAIL);
|
||||
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
if (!jh->b_committed_data) {
|
||||
/* Copy out the current buffer contents into the
|
||||
* preserved, committed copy. */
|
||||
JBUFFER_TRACE(jh, "generate b_committed data");
|
||||
if (!committed_data) {
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
@@ -1289,7 +1360,7 @@ repeat:
|
||||
committed_data = NULL;
|
||||
memcpy(jh->b_committed_data, bh->b_data, bh->b_size);
|
||||
}
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
out:
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
if (unlikely(committed_data))
|
||||
@@ -1390,16 +1461,16 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
|
||||
*/
|
||||
if (jh->b_transaction != transaction &&
|
||||
jh->b_next_transaction != transaction) {
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
J_ASSERT_JH(jh, jh->b_transaction == transaction ||
|
||||
jh->b_next_transaction == transaction);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
}
|
||||
if (jh->b_modified == 1) {
|
||||
/* If it's in our transaction it must be in BJ_Metadata list. */
|
||||
if (jh->b_transaction == transaction &&
|
||||
jh->b_jlist != BJ_Metadata) {
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
if (jh->b_transaction == transaction &&
|
||||
jh->b_jlist != BJ_Metadata)
|
||||
pr_err("JBD2: assertion failure: h_type=%u "
|
||||
@@ -1409,13 +1480,13 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
|
||||
jh->b_jlist);
|
||||
J_ASSERT_JH(jh, jh->b_transaction != transaction ||
|
||||
jh->b_jlist == BJ_Metadata);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
journal = transaction->t_journal;
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
|
||||
if (jh->b_modified == 0) {
|
||||
/*
|
||||
@@ -1423,12 +1494,12 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
|
||||
* of the transaction. This needs to be done
|
||||
* once a transaction -bzzz
|
||||
*/
|
||||
if (handle->h_buffer_credits <= 0) {
|
||||
if (WARN_ON_ONCE(jbd2_handle_buffer_credits(handle) <= 0)) {
|
||||
ret = -ENOSPC;
|
||||
goto out_unlock_bh;
|
||||
}
|
||||
jh->b_modified = 1;
|
||||
handle->h_buffer_credits--;
|
||||
handle->h_total_credits--;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1501,7 +1572,7 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
|
||||
__jbd2_journal_file_buffer(jh, transaction, BJ_Metadata);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
out_unlock_bh:
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
out:
|
||||
JBUFFER_TRACE(jh, "exit");
|
||||
return ret;
|
||||
@@ -1539,18 +1610,20 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
|
||||
|
||||
BUFFER_TRACE(bh, "entry");
|
||||
|
||||
jbd_lock_bh_state(bh);
|
||||
jh = jbd2_journal_grab_journal_head(bh);
|
||||
if (!jh) {
|
||||
__bforget(bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!buffer_jbd(bh))
|
||||
goto not_jbd;
|
||||
jh = bh2jh(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
|
||||
/* Critical error: attempting to delete a bitmap buffer, maybe?
|
||||
* Don't do any jbd operations, and return an error. */
|
||||
if (!J_EXPECT_JH(jh, !jh->b_committed_data,
|
||||
"inconsistent data on disk")) {
|
||||
err = -EIO;
|
||||
goto not_jbd;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* keep track of whether or not this transaction modified us */
|
||||
@@ -1598,10 +1671,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
|
||||
__jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
|
||||
} else {
|
||||
__jbd2_journal_unfile_buffer(jh);
|
||||
if (!buffer_jbd(bh)) {
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
goto not_jbd;
|
||||
}
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
}
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
} else if (jh->b_transaction) {
|
||||
@@ -1643,7 +1713,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
|
||||
if (!jh->b_cp_transaction) {
|
||||
JBUFFER_TRACE(jh, "belongs to none transaction");
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
goto not_jbd;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1653,7 +1723,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
|
||||
if (!buffer_dirty(bh)) {
|
||||
__jbd2_journal_remove_checkpoint(jh);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
goto not_jbd;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1666,20 +1736,15 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
|
||||
__jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
}
|
||||
|
||||
jbd_unlock_bh_state(bh);
|
||||
__brelse(bh);
|
||||
drop:
|
||||
__brelse(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
if (drop_reserve) {
|
||||
/* no need to reserve log space for this block -bzzz */
|
||||
handle->h_buffer_credits++;
|
||||
handle->h_total_credits++;
|
||||
}
|
||||
return err;
|
||||
|
||||
not_jbd:
|
||||
jbd_unlock_bh_state(bh);
|
||||
__bforget(bh);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1706,45 +1771,34 @@ int jbd2_journal_stop(handle_t *handle)
|
||||
tid_t tid;
|
||||
pid_t pid;
|
||||
|
||||
if (--handle->h_ref > 0) {
|
||||
jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
|
||||
handle->h_ref);
|
||||
if (is_handle_aborted(handle))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
if (!transaction) {
|
||||
/*
|
||||
* Handle is already detached from the transaction so
|
||||
* there is nothing to do other than decrease a refcount,
|
||||
* or free the handle if refcount drops to zero
|
||||
* Handle is already detached from the transaction so there is
|
||||
* nothing to do other than free the handle.
|
||||
*/
|
||||
if (--handle->h_ref > 0) {
|
||||
jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
|
||||
handle->h_ref);
|
||||
return err;
|
||||
} else {
|
||||
if (handle->h_rsv_handle)
|
||||
jbd2_free_handle(handle->h_rsv_handle);
|
||||
goto free_and_exit;
|
||||
}
|
||||
memalloc_nofs_restore(handle->saved_alloc_context);
|
||||
goto free_and_exit;
|
||||
}
|
||||
journal = transaction->t_journal;
|
||||
|
||||
J_ASSERT(journal_current_handle() == handle);
|
||||
tid = transaction->t_tid;
|
||||
|
||||
if (is_handle_aborted(handle))
|
||||
err = -EIO;
|
||||
else
|
||||
J_ASSERT(atomic_read(&transaction->t_updates) > 0);
|
||||
|
||||
if (--handle->h_ref > 0) {
|
||||
jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
|
||||
handle->h_ref);
|
||||
return err;
|
||||
}
|
||||
|
||||
jbd_debug(4, "Handle %p going down\n", handle);
|
||||
trace_jbd2_handle_stats(journal->j_fs_dev->bd_dev,
|
||||
transaction->t_tid,
|
||||
handle->h_type, handle->h_line_no,
|
||||
tid, handle->h_type, handle->h_line_no,
|
||||
jiffies - handle->h_start_jiffies,
|
||||
handle->h_sync, handle->h_requested_credits,
|
||||
(handle->h_requested_credits -
|
||||
handle->h_buffer_credits));
|
||||
handle->h_total_credits));
|
||||
|
||||
/*
|
||||
* Implement synchronous transaction batching. If the handle
|
||||
@@ -1804,19 +1858,13 @@ int jbd2_journal_stop(handle_t *handle)
|
||||
|
||||
if (handle->h_sync)
|
||||
transaction->t_synchronous_commit = 1;
|
||||
current->journal_info = NULL;
|
||||
atomic_sub(handle->h_buffer_credits,
|
||||
&transaction->t_outstanding_credits);
|
||||
|
||||
/*
|
||||
* If the handle is marked SYNC, we need to set another commit
|
||||
* going! We also want to force a commit if the current
|
||||
* transaction is occupying too much of the log, or if the
|
||||
* transaction is too old now.
|
||||
* going! We also want to force a commit if the transaction is too
|
||||
* old now.
|
||||
*/
|
||||
if (handle->h_sync ||
|
||||
(atomic_read(&transaction->t_outstanding_credits) >
|
||||
journal->j_max_transaction_buffers) ||
|
||||
time_after_eq(jiffies, transaction->t_expires)) {
|
||||
/* Do this even for aborted journals: an abort still
|
||||
* completes the commit thread, it just doesn't write
|
||||
@@ -1825,7 +1873,7 @@ int jbd2_journal_stop(handle_t *handle)
|
||||
jbd_debug(2, "transaction too old, requesting commit for "
|
||||
"handle %p\n", handle);
|
||||
/* This is non-blocking */
|
||||
jbd2_log_start_commit(journal, transaction->t_tid);
|
||||
jbd2_log_start_commit(journal, tid);
|
||||
|
||||
/*
|
||||
* Special case: JBD2_SYNC synchronous updates require us
|
||||
@@ -1836,31 +1884,19 @@ int jbd2_journal_stop(handle_t *handle)
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we drop t_updates, if it goes to zero the transaction
|
||||
* could start committing on us and eventually disappear. So
|
||||
* once we do this, we must not dereference transaction
|
||||
* pointer again.
|
||||
* Once stop_this_handle() drops t_updates, the transaction could start
|
||||
* committing on us and eventually disappear. So we must not
|
||||
* dereference transaction pointer again after calling
|
||||
* stop_this_handle().
|
||||
*/
|
||||
tid = transaction->t_tid;
|
||||
if (atomic_dec_and_test(&transaction->t_updates)) {
|
||||
wake_up(&journal->j_wait_updates);
|
||||
if (journal->j_barrier_count)
|
||||
wake_up(&journal->j_wait_transaction_locked);
|
||||
}
|
||||
|
||||
rwsem_release(&journal->j_trans_commit_map, _THIS_IP_);
|
||||
stop_this_handle(handle);
|
||||
|
||||
if (wait_for_commit)
|
||||
err = jbd2_log_wait_commit(journal, tid);
|
||||
|
||||
if (handle->h_rsv_handle)
|
||||
jbd2_journal_free_reserved(handle->h_rsv_handle);
|
||||
free_and_exit:
|
||||
/*
|
||||
* Scope of the GFP_NOFS context is over here and so we can restore the
|
||||
* original alloc context.
|
||||
*/
|
||||
memalloc_nofs_restore(handle->saved_alloc_context);
|
||||
if (handle->h_rsv_handle)
|
||||
jbd2_free_handle(handle->h_rsv_handle);
|
||||
jbd2_free_handle(handle);
|
||||
return err;
|
||||
}
|
||||
@@ -1878,7 +1914,7 @@ free_and_exit:
|
||||
*
|
||||
* j_list_lock is held.
|
||||
*
|
||||
* jbd_lock_bh_state(jh2bh(jh)) is held.
|
||||
* jh->b_state_lock is held.
|
||||
*/
|
||||
|
||||
static inline void
|
||||
@@ -1902,7 +1938,7 @@ __blist_add_buffer(struct journal_head **list, struct journal_head *jh)
|
||||
*
|
||||
* Called with j_list_lock held, and the journal may not be locked.
|
||||
*
|
||||
* jbd_lock_bh_state(jh2bh(jh)) is held.
|
||||
* jh->b_state_lock is held.
|
||||
*/
|
||||
|
||||
static inline void
|
||||
@@ -1934,7 +1970,7 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh)
|
||||
transaction_t *transaction;
|
||||
struct buffer_head *bh = jh2bh(jh);
|
||||
|
||||
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
|
||||
lockdep_assert_held(&jh->b_state_lock);
|
||||
transaction = jh->b_transaction;
|
||||
if (transaction)
|
||||
assert_spin_locked(&transaction->t_journal->j_list_lock);
|
||||
@@ -1971,17 +2007,15 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh)
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove buffer from all transactions.
|
||||
* Remove buffer from all transactions. The caller is responsible for dropping
|
||||
* the jh reference that belonged to the transaction.
|
||||
*
|
||||
* Called with bh_state lock and j_list_lock
|
||||
*
|
||||
* jh and bh may be already freed when this function returns.
|
||||
*/
|
||||
static void __jbd2_journal_unfile_buffer(struct journal_head *jh)
|
||||
{
|
||||
__jbd2_journal_temp_unlink_buffer(jh);
|
||||
jh->b_transaction = NULL;
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
}
|
||||
|
||||
void jbd2_journal_unfile_buffer(journal_t *journal, struct journal_head *jh)
|
||||
@@ -1990,18 +2024,19 @@ void jbd2_journal_unfile_buffer(journal_t *journal, struct journal_head *jh)
|
||||
|
||||
/* Get reference so that buffer cannot be freed before we unlock it */
|
||||
get_bh(bh);
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
spin_lock(&journal->j_list_lock);
|
||||
__jbd2_journal_unfile_buffer(jh);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
__brelse(bh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from jbd2_journal_try_to_free_buffers().
|
||||
*
|
||||
* Called under jbd_lock_bh_state(bh)
|
||||
* Called under jh->b_state_lock
|
||||
*/
|
||||
static void
|
||||
__journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
|
||||
@@ -2088,10 +2123,10 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal,
|
||||
if (!jh)
|
||||
continue;
|
||||
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
__journal_try_to_free_buffer(journal, bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
jbd_unlock_bh_state(bh);
|
||||
if (buffer_jbd(bh))
|
||||
goto busy;
|
||||
} while ((bh = bh->b_this_page) != head);
|
||||
@@ -2112,7 +2147,7 @@ busy:
|
||||
*
|
||||
* Called under j_list_lock.
|
||||
*
|
||||
* Called under jbd_lock_bh_state(bh).
|
||||
* Called under jh->b_state_lock.
|
||||
*/
|
||||
static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)
|
||||
{
|
||||
@@ -2133,6 +2168,7 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)
|
||||
} else {
|
||||
JBUFFER_TRACE(jh, "on running transaction");
|
||||
__jbd2_journal_unfile_buffer(jh);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
}
|
||||
return may_free;
|
||||
}
|
||||
@@ -2199,18 +2235,15 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
|
||||
* holding the page lock. --sct
|
||||
*/
|
||||
|
||||
if (!buffer_jbd(bh))
|
||||
jh = jbd2_journal_grab_journal_head(bh);
|
||||
if (!jh)
|
||||
goto zap_buffer_unlocked;
|
||||
|
||||
/* OK, we have data buffer in journaled mode */
|
||||
write_lock(&journal->j_state_lock);
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
spin_lock(&journal->j_list_lock);
|
||||
|
||||
jh = jbd2_journal_grab_journal_head(bh);
|
||||
if (!jh)
|
||||
goto zap_buffer_no_jh;
|
||||
|
||||
/*
|
||||
* We cannot remove the buffer from checkpoint lists until the
|
||||
* transaction adding inode to orphan list (let's call it T)
|
||||
@@ -2289,10 +2322,10 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
|
||||
* for commit and try again.
|
||||
*/
|
||||
if (partial_page) {
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
write_unlock(&journal->j_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
return -EBUSY;
|
||||
}
|
||||
/*
|
||||
@@ -2304,10 +2337,10 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
|
||||
set_buffer_freed(bh);
|
||||
if (journal->j_running_transaction && buffer_jbddirty(bh))
|
||||
jh->b_next_transaction = journal->j_running_transaction;
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
write_unlock(&journal->j_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
return 0;
|
||||
} else {
|
||||
/* Good, the buffer belongs to the running transaction.
|
||||
@@ -2331,11 +2364,10 @@ zap_buffer:
|
||||
* here.
|
||||
*/
|
||||
jh->b_modified = 0;
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
zap_buffer_no_jh:
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
jbd_unlock_bh_state(bh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
write_unlock(&journal->j_state_lock);
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
zap_buffer_unlocked:
|
||||
clear_buffer_dirty(bh);
|
||||
J_ASSERT_BH(bh, !buffer_jbddirty(bh));
|
||||
@@ -2422,7 +2454,7 @@ void __jbd2_journal_file_buffer(struct journal_head *jh,
|
||||
int was_dirty = 0;
|
||||
struct buffer_head *bh = jh2bh(jh);
|
||||
|
||||
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
|
||||
lockdep_assert_held(&jh->b_state_lock);
|
||||
assert_spin_locked(&transaction->t_journal->j_list_lock);
|
||||
|
||||
J_ASSERT_JH(jh, jh->b_jlist < BJ_Types);
|
||||
@@ -2484,11 +2516,11 @@ void __jbd2_journal_file_buffer(struct journal_head *jh,
|
||||
void jbd2_journal_file_buffer(struct journal_head *jh,
|
||||
transaction_t *transaction, int jlist)
|
||||
{
|
||||
jbd_lock_bh_state(jh2bh(jh));
|
||||
spin_lock(&jh->b_state_lock);
|
||||
spin_lock(&transaction->t_journal->j_list_lock);
|
||||
__jbd2_journal_file_buffer(jh, transaction, jlist);
|
||||
spin_unlock(&transaction->t_journal->j_list_lock);
|
||||
jbd_unlock_bh_state(jh2bh(jh));
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2498,23 +2530,25 @@ void jbd2_journal_file_buffer(struct journal_head *jh,
|
||||
* buffer on that transaction's metadata list.
|
||||
*
|
||||
* Called under j_list_lock
|
||||
* Called under jbd_lock_bh_state(jh2bh(jh))
|
||||
* Called under jh->b_state_lock
|
||||
*
|
||||
* jh and bh may be already free when this function returns
|
||||
* When this function returns true, there's no next transaction to refile to
|
||||
* and the caller has to drop jh reference through
|
||||
* jbd2_journal_put_journal_head().
|
||||
*/
|
||||
void __jbd2_journal_refile_buffer(struct journal_head *jh)
|
||||
bool __jbd2_journal_refile_buffer(struct journal_head *jh)
|
||||
{
|
||||
int was_dirty, jlist;
|
||||
struct buffer_head *bh = jh2bh(jh);
|
||||
|
||||
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
|
||||
lockdep_assert_held(&jh->b_state_lock);
|
||||
if (jh->b_transaction)
|
||||
assert_spin_locked(&jh->b_transaction->t_journal->j_list_lock);
|
||||
|
||||
/* If the buffer is now unused, just drop it. */
|
||||
if (jh->b_next_transaction == NULL) {
|
||||
__jbd2_journal_unfile_buffer(jh);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2542,6 +2576,7 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
|
||||
|
||||
if (was_dirty)
|
||||
set_buffer_jbddirty(bh);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2552,16 +2587,15 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
|
||||
*/
|
||||
void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh)
|
||||
{
|
||||
struct buffer_head *bh = jh2bh(jh);
|
||||
bool drop;
|
||||
|
||||
/* Get reference so that buffer cannot be freed before we unlock it */
|
||||
get_bh(bh);
|
||||
jbd_lock_bh_state(bh);
|
||||
spin_lock(&jh->b_state_lock);
|
||||
spin_lock(&journal->j_list_lock);
|
||||
__jbd2_journal_refile_buffer(jh);
|
||||
jbd_unlock_bh_state(bh);
|
||||
drop = __jbd2_journal_refile_buffer(jh);
|
||||
spin_unlock(&jh->b_state_lock);
|
||||
spin_unlock(&journal->j_list_lock);
|
||||
__brelse(bh);
|
||||
if (drop)
|
||||
jbd2_journal_put_journal_head(jh);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user