Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[platform/kernel/linux-starfive.git] / fs / jbd2 / transaction.c
index b25ebdc..27b9f9d 100644 (file)
@@ -63,6 +63,28 @@ void jbd2_journal_free_transaction(transaction_t *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.
  *
  * Simply initialise a new transaction. Initialize it in
@@ -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);
+       jbd_debug(2, "restarting handle %p\n", handle);
+       stop_this_handle(handle);
        handle->h_transaction = NULL;
-       current->journal_info = NULL;
 
-       jbd_debug(2, "restarting handle %p\n", handle);
+       /*
+        * 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);
 }
 
 /*