]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/jbd2/transaction.c
jbd2: move the clearing of b_modified flag to the journal_unmap_buffer()
[mirror_ubuntu-bionic-kernel.git] / fs / jbd2 / transaction.c
index 8b08044b31209fbed735ccfbe2771154a2e9185a..f2ff141a4479ea738dcd7d58704c088c79e98875 100644 (file)
@@ -495,8 +495,10 @@ void jbd2_journal_free_reserved(handle_t *handle)
 EXPORT_SYMBOL(jbd2_journal_free_reserved);
 
 /**
- * int jbd2_journal_start_reserved(handle_t *handle) - start reserved handle
+ * int jbd2_journal_start_reserved() - start reserved handle
  * @handle: handle to start
+ * @type: for handle statistics
+ * @line_no: for handle statistics
  *
  * Start handle that has been previously reserved with jbd2_journal_reserve().
  * This attaches @handle to the running transaction (or creates one if there's
@@ -533,6 +535,7 @@ int jbd2_journal_start_reserved(handle_t *handle, unsigned int type,
         */
        ret = start_this_handle(journal, handle, GFP_NOFS);
        if (ret < 0) {
+               handle->h_journal = journal;
                jbd2_journal_free_reserved(handle);
                return ret;
        }
@@ -626,6 +629,7 @@ error_out:
  * int jbd2_journal_restart() - restart a handle .
  * @handle:  handle to restart
  * @nblocks: nr credits requested
+ * @gfp_mask: memory allocation flags (for start_this_handle)
  *
  * Restart a handle for a multi-transaction filesystem
  * operation.
@@ -1220,11 +1224,12 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
        struct journal_head *jh;
        char *committed_data = NULL;
 
-       JBUFFER_TRACE(jh, "entry");
        if (jbd2_write_access_granted(handle, bh, true))
                return 0;
 
        jh = jbd2_journal_add_journal_head(bh);
+       JBUFFER_TRACE(jh, "entry");
+
        /*
         * Do this first --- it can drop the journal lock, so we want to
         * make sure that obtaining the committed_data is done
@@ -1335,15 +1340,17 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
 
        if (is_handle_aborted(handle))
                return -EROFS;
-       if (!buffer_jbd(bh)) {
-               ret = -EUCLEAN;
-               goto out;
-       }
+       if (!buffer_jbd(bh))
+               return -EUCLEAN;
+
        /*
         * We don't grab jh reference here since the buffer must be part
         * of the running transaction.
         */
        jh = bh2jh(bh);
+       jbd_debug(5, "journal_head %p\n", jh);
+       JBUFFER_TRACE(jh, "entry");
+
        /*
         * This and the following assertions are unreliable since we may see jh
         * in inconsistent state unless we grab bh_state lock. But this is
@@ -1362,6 +1369,13 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
                if (jh->b_transaction == transaction &&
                    jh->b_jlist != BJ_Metadata) {
                        jbd_lock_bh_state(bh);
+                       if (jh->b_transaction == transaction &&
+                           jh->b_jlist != BJ_Metadata)
+                               pr_err("JBD2: assertion failure: h_type=%u "
+                                      "h_line_no=%u block_no=%llu jlist=%u\n",
+                                      handle->h_type, handle->h_line_no,
+                                      (unsigned long long) bh->b_blocknr,
+                                      jh->b_jlist);
                        J_ASSERT_JH(jh, jh->b_transaction != transaction ||
                                        jh->b_jlist == BJ_Metadata);
                        jbd_unlock_bh_state(bh);
@@ -1370,9 +1384,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
        }
 
        journal = transaction->t_journal;
-       jbd_debug(5, "journal_head %p\n", jh);
-       JBUFFER_TRACE(jh, "entry");
-
        jbd_lock_bh_state(bh);
 
        if (jh->b_modified == 0) {
@@ -1381,11 +1392,11 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
                 * of the transaction. This needs to be done
                 * once a transaction -bzzz
                 */
-               jh->b_modified = 1;
                if (handle->h_buffer_credits <= 0) {
                        ret = -ENOSPC;
                        goto out_unlock_bh;
                }
+               jh->b_modified = 1;
                handle->h_buffer_credits--;
        }
 
@@ -1570,14 +1581,21 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
                /* However, if the buffer is still owned by a prior
                 * (committing) transaction, we can't drop it yet... */
                JBUFFER_TRACE(jh, "belongs to older transaction");
-               /* ... but we CAN drop it from the new transaction if we
-                * have also modified it since the original commit. */
+               /* ... but we CAN drop it from the new transaction through
+                * marking the buffer as freed and set j_next_transaction to
+                * the new transaction, so that not only the commit code
+                * knows it should clear dirty bits when it is done with the
+                * buffer, but also the buffer can be checkpointed only
+                * after the new transaction commits. */
 
-               if (jh->b_next_transaction) {
-                       J_ASSERT(jh->b_next_transaction == transaction);
+               set_buffer_freed(bh);
+
+               if (!jh->b_next_transaction) {
                        spin_lock(&journal->j_list_lock);
-                       jh->b_next_transaction = NULL;
+                       jh->b_next_transaction = transaction;
                        spin_unlock(&journal->j_list_lock);
+               } else {
+                       J_ASSERT(jh->b_next_transaction == transaction);
 
                        /*
                         * only drop a reference if this transaction modified
@@ -2213,14 +2231,16 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
                        return -EBUSY;
                }
                /*
-                * OK, buffer won't be reachable after truncate. We just set
-                * j_next_transaction to the running transaction (if there is
-                * one) and mark buffer as freed so that commit code knows it
-                * should clear dirty bits when it is done with the buffer.
+                * OK, buffer won't be reachable after truncate. We just clear
+                * b_modified to not confuse transaction credit accounting, and
+                * set j_next_transaction to the running transaction (if there
+                * is one) and mark buffer as freed so that commit code knows
+                * it should clear dirty bits when it is done with the buffer.
                 */
                set_buffer_freed(bh);
                if (journal->j_running_transaction && buffer_jbddirty(bh))
                        jh->b_next_transaction = journal->j_running_transaction;
+               jh->b_modified = 0;
                jbd2_journal_put_journal_head(jh);
                spin_unlock(&journal->j_list_lock);
                jbd_unlock_bh_state(bh);
@@ -2485,7 +2505,7 @@ void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh)
  * File inode in the inode list of the handle's transaction
  */
 static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode,
-                                  unsigned long flags)
+               unsigned long flags, loff_t start_byte, loff_t end_byte)
 {
        transaction_t *transaction = handle->h_transaction;
        journal_t *journal;
@@ -2497,26 +2517,17 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode,
        jbd_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino,
                        transaction->t_tid);
 
-       /*
-        * First check whether inode isn't already on the transaction's
-        * lists without taking the lock. Note that this check is safe
-        * without the lock as we cannot race with somebody removing inode
-        * from the transaction. The reason is that we remove inode from the
-        * transaction only in journal_release_jbd_inode() and when we commit
-        * the transaction. We are guarded from the first case by holding
-        * a reference to the inode. We are safe against the second case
-        * because if jinode->i_transaction == transaction, commit code
-        * cannot touch the transaction because we hold reference to it,
-        * and if jinode->i_next_transaction == transaction, commit code
-        * will only file the inode where we want it.
-        */
-       if ((jinode->i_transaction == transaction ||
-           jinode->i_next_transaction == transaction) &&
-           (jinode->i_flags & flags) == flags)
-               return 0;
-
        spin_lock(&journal->j_list_lock);
        jinode->i_flags |= flags;
+
+       if (jinode->i_dirty_end) {
+               jinode->i_dirty_start = min(jinode->i_dirty_start, start_byte);
+               jinode->i_dirty_end = max(jinode->i_dirty_end, end_byte);
+       } else {
+               jinode->i_dirty_start = start_byte;
+               jinode->i_dirty_end = end_byte;
+       }
+
        /* Is inode already attached where we need it? */
        if (jinode->i_transaction == transaction ||
            jinode->i_next_transaction == transaction)
@@ -2551,12 +2562,28 @@ done:
 int jbd2_journal_inode_add_write(handle_t *handle, struct jbd2_inode *jinode)
 {
        return jbd2_journal_file_inode(handle, jinode,
-                                      JI_WRITE_DATA | JI_WAIT_DATA);
+                       JI_WRITE_DATA | JI_WAIT_DATA, 0, LLONG_MAX);
 }
 
 int jbd2_journal_inode_add_wait(handle_t *handle, struct jbd2_inode *jinode)
 {
-       return jbd2_journal_file_inode(handle, jinode, JI_WAIT_DATA);
+       return jbd2_journal_file_inode(handle, jinode, JI_WAIT_DATA, 0,
+                       LLONG_MAX);
+}
+
+int jbd2_journal_inode_ranged_write(handle_t *handle,
+               struct jbd2_inode *jinode, loff_t start_byte, loff_t length)
+{
+       return jbd2_journal_file_inode(handle, jinode,
+                       JI_WRITE_DATA | JI_WAIT_DATA, start_byte,
+                       start_byte + length - 1);
+}
+
+int jbd2_journal_inode_ranged_wait(handle_t *handle, struct jbd2_inode *jinode,
+               loff_t start_byte, loff_t length)
+{
+       return jbd2_journal_file_inode(handle, jinode, JI_WAIT_DATA,
+                       start_byte, start_byte + length - 1);
 }
 
 /*