ext4_journal: add trans parameter to jbd_trans_get_access routine.
[lwext4.git] / lwext4 / ext4_journal.c
index 80cd1a942c55481f79891ba829f7d6176a10c519..ada92ff754229942ce503210ec526c7f48b5798a 100644 (file)
@@ -107,16 +107,11 @@ RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
 #define jbd_free_revoke_entry(addr) free(addr)
 
-/**@brief  Data block lookup helper.*/
-int jbd_inode_bmap(struct jbd_fs *jbd_fs,
-                  ext4_lblk_t iblock,
-                  ext4_fsblk_t *fblock);
-
 /**@brief  Write jbd superblock to disk.
  * @param  jbd_fs jbd filesystem
  * @param  s jbd superblock
  * @return standard error code*/
-int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
+static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
 {
        int rc;
        struct ext4_fs *fs = jbd_fs->inode_ref.fs;
@@ -135,7 +130,7 @@ int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
  * @param  jbd_fs jbd filesystem
  * @param  s jbd superblock
  * @return standard error code*/
-int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
+static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
 {
        int rc;
        struct ext4_fs *fs = jbd_fs->inode_ref.fs;
@@ -254,7 +249,7 @@ int jbd_inode_bmap(struct jbd_fs *jbd_fs,
  * @param   block block descriptor
  * @param   fblock jbd logical block address
  * @return  standard error code*/
-int jbd_block_get(struct jbd_fs *jbd_fs,
+static int jbd_block_get(struct jbd_fs *jbd_fs,
                  struct ext4_block *block,
                  ext4_fsblk_t fblock)
 {
@@ -285,7 +280,7 @@ int jbd_block_get(struct jbd_fs *jbd_fs,
  * @param   block block descriptor
  * @param   fblock jbd logical block address
  * @return  standard error code*/
-int jbd_block_get_noread(struct jbd_fs *jbd_fs,
+static int jbd_block_get_noread(struct jbd_fs *jbd_fs,
                         struct ext4_block *block,
                         ext4_fsblk_t fblock)
 {
@@ -309,7 +304,7 @@ int jbd_block_get_noread(struct jbd_fs *jbd_fs,
  * @param   jbd_fs jbd filesystem
  * @param   block block descriptor
  * @return  standard error code*/
-int jbd_block_set(struct jbd_fs *jbd_fs,
+static int jbd_block_set(struct jbd_fs *jbd_fs,
                  struct ext4_block *block)
 {
        return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
@@ -320,7 +315,7 @@ int jbd_block_set(struct jbd_fs *jbd_fs,
  *         block tag size, not including UUID part.
  * @param  jbd_fs jbd filesystem
  * @return tag size in bytes*/
-int jbd_tag_bytes(struct jbd_fs *jbd_fs)
+static int jbd_tag_bytes(struct jbd_fs *jbd_fs)
 {
        int size;
 
@@ -465,6 +460,7 @@ jbd_write_block_tag(struct jbd_fs *jbd_fs,
        if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
                                     JBD_FEATURE_INCOMPAT_CSUM_V3)) {
                struct jbd_block_tag3 *tag = __tag;
+               memset(tag, 0, sizeof(struct jbd_block_tag3));
                jbd_set32(tag, blocknr, tag_info->block);
                if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
                                             JBD_FEATURE_INCOMPAT_64BIT))
@@ -488,6 +484,7 @@ jbd_write_block_tag(struct jbd_fs *jbd_fs,
 
        } else {
                struct jbd_block_tag *tag = __tag;
+               memset(tag, 0, sizeof(struct jbd_block_tag));
                jbd_set32(tag, blocknr, tag_info->block);
                if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
                                             JBD_FEATURE_INCOMPAT_64BIT))
@@ -774,9 +771,9 @@ static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
  * @param  recover_info  journal replay info
  * @param  action action needed to be taken
  * @return standard error code*/
-int jbd_iterate_log(struct jbd_fs *jbd_fs,
-                   struct recover_info *info,
-                   int action)
+static int jbd_iterate_log(struct jbd_fs *jbd_fs,
+                          struct recover_info *info,
+                          int action)
 {
        int r = EOK;
        bool log_end = false;
@@ -935,7 +932,7 @@ int jbd_recover(struct jbd_fs *jbd_fs)
        return r;
 }
 
-void jbd_journal_write_sb(struct jbd_journal *journal)
+static void jbd_journal_write_sb(struct jbd_journal *journal)
 {
        struct jbd_fs *jbd_fs = journal->jbd_fs;
        jbd_set32(&jbd_fs->sb, start, journal->start);
@@ -978,6 +975,46 @@ int jbd_journal_start(struct jbd_fs *jbd_fs,
        return jbd_write_sb(jbd_fs);
 }
 
+static void jbd_journal_flush_trans(struct jbd_trans *trans)
+{
+       struct jbd_buf *jbd_buf, *tmp;
+       struct jbd_journal *journal = trans->journal;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+       LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
+                       tmp) {
+               struct ext4_block block = jbd_buf->block;
+               ext4_block_flush_buf(fs->bdev, block.buf);
+       }
+}
+
+static void
+jbd_journal_skip_pure_revoke(struct jbd_journal *journal,
+                            struct jbd_trans *trans)
+{
+       journal->start = trans->start_iblock +
+               trans->alloc_blocks;
+       wrap(&journal->jbd_fs->sb, journal->start);
+       journal->trans_id = trans->trans_id + 1;
+       jbd_journal_free_trans(journal,
+                       trans, false);
+       jbd_journal_write_sb(journal);
+}
+
+static void jbd_journal_flush_all_trans(struct jbd_journal *journal)
+{
+       struct jbd_trans *trans;
+       while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
+               if (!trans->data_cnt) {
+                       TAILQ_REMOVE(&journal->cp_queue,
+                                       trans,
+                                       trans_node);
+                       jbd_journal_skip_pure_revoke(journal, trans);
+               } else
+                       jbd_journal_flush_trans(trans);
+
+       }
+}
+
 /**@brief  Stop accessing the journal.
  * @param  journal current journal session
  * @return standard error code*/
@@ -989,9 +1026,10 @@ int jbd_journal_stop(struct jbd_journal *journal)
 
        /* Commit all the transactions to the journal.*/
        jbd_journal_commit_all(journal);
+
        /* Make sure that journalled content have reached
         * the disk.*/
-       ext4_block_cache_flush(jbd_fs->inode_ref.fs->bdev);
+       jbd_journal_flush_all_trans(journal);
 
        features_incompatible =
                ext4_get32(&jbd_fs->inode_ref.fs->sb,
@@ -1027,7 +1065,7 @@ static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
        /* If there is no space left, flush all journalled
         * blocks to disk first.*/
        if (journal->last == journal->start)
-               ext4_block_cache_flush(journal->jbd_fs->inode_ref.fs->bdev);
+               jbd_journal_flush_all_trans(journal);
 
        return start_block;
 }
@@ -1054,18 +1092,36 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
                          int res,
                          void *arg);
 
-/**@brief  Add block to a transaction
+/**@brief  gain access to it before making any modications.
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @param  block descriptor
+ * @return standard error code.*/
+int jbd_trans_get_access(struct jbd_journal *journal,
+                        struct jbd_trans *trans,
+                        struct ext4_block *block)
+{
+       int r = EOK;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+
+       /* If the buffer has already been modified, we should
+        * flush dirty data in this buffer to disk.*/
+       if (ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
+           block->buf->end_write == jbd_trans_end_write &&
+           block->buf->end_write_arg != trans) {
+               r = ext4_block_flush_buf(fs->bdev, block->buf);
+       }
+       return r;
+}
+
+/**@brief  Add block to a transaction and mark it dirty.
  * @param  trans transaction
  * @param  block block descriptor
  * @return standard error code*/
-int jbd_trans_add_block(struct jbd_trans *trans,
-                       struct ext4_block *block)
+int jbd_trans_set_block_dirty(struct jbd_trans *trans,
+                             struct ext4_block *block)
 {
        struct jbd_buf *buf;
-       /* We do not need to add those unmodified buffer to
-        * a transaction. */
-       if (!ext4_bcache_test_flag(block->buf, BC_DIRTY))
-               return EOK;
 
        buf = calloc(1, sizeof(struct jbd_buf));
        if (!buf)
@@ -1078,10 +1134,12 @@ int jbd_trans_add_block(struct jbd_trans *trans,
        /* If the content reach the disk, notify us
         * so that we may do a checkpoint. */
        block->buf->end_write = jbd_trans_end_write;
-       block->buf->end_write_arg = trans;
+       block->buf->end_write_arg = buf;
 
        trans->data_cnt++;
        LIST_INSERT_HEAD(&trans->buf_list, buf, buf_node);
+
+       ext4_bcache_set_dirty(block->buf);
        return EOK;
 }
 
@@ -1116,8 +1174,12 @@ void jbd_journal_free_trans(struct jbd_journal *journal,
        struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
        LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
                          tmp) {
-               if (abort)
+               if (abort) {
+                       jbd_buf->block.buf->end_write = NULL;
+                       jbd_buf->block.buf->end_write_arg = NULL;
+                       ext4_bcache_clear_dirty(jbd_buf->block.buf);
                        ext4_block_set(fs->bdev, &jbd_buf->block);
+               }
 
                LIST_REMOVE(jbd_buf, buf_node);
                free(jbd_buf);
@@ -1173,12 +1235,24 @@ static int jbd_journal_prepare(struct jbd_journal *journal,
        uint32_t desc_iblock = 0;
        uint32_t data_iblock = 0;
        char *tag_start = NULL, *tag_ptr = NULL;
-       struct jbd_buf *jbd_buf;
+       struct jbd_buf *jbd_buf, *tmp;
        struct ext4_block desc_block, data_block;
+       struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
 
-       LIST_FOREACH(jbd_buf, &trans->buf_list, buf_node) {
+       LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node, tmp) {
                struct tag_info tag_info;
                bool uuid_exist = false;
+               if (!ext4_bcache_test_flag(jbd_buf->block.buf,
+                                          BC_DIRTY)) {
+                       /* The buffer has not been modified, just release
+                        * that jbd_buf. */
+                       jbd_buf->block.buf->end_write = NULL;
+                       jbd_buf->block.buf->end_write_arg = NULL;
+                       ext4_block_set(fs->bdev, &jbd_buf->block);
+                       LIST_REMOVE(jbd_buf, buf_node);
+                       free(jbd_buf);
+                       continue;
+               }
 again:
                if (!desc_iblock) {
                        struct jbd_bhdr *bhdr;
@@ -1362,17 +1436,25 @@ void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
 /**@brief  Update the start block of the journal when
  *         all the contents in a transaction reach the disk.*/
 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
-                         struct ext4_buf *buf __unused,
+                         struct ext4_buf *buf,
                          int res,
                          void *arg)
 {
-       struct jbd_trans *trans = arg;
+       struct jbd_buf *jbd_buf = arg;
+       struct jbd_trans *trans = jbd_buf->trans;
        struct jbd_journal *journal = trans->journal;
        bool first_in_queue =
                trans == TAILQ_FIRST(&journal->cp_queue);
        if (res != EOK)
                trans->error = res;
 
+       LIST_REMOVE(jbd_buf, buf_node);
+       free(jbd_buf);
+
+       /* Clear the end_write and end_write_arg fields. */
+       buf->end_write = NULL;
+       buf->end_write_arg = NULL;
+
        trans->written_cnt++;
        if (trans->written_cnt == trans->data_cnt) {
                TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
@@ -1391,12 +1473,8 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
                                        TAILQ_REMOVE(&journal->cp_queue,
                                                     trans,
                                                     trans_node);
-                                       journal->start = trans->start_iblock +
-                                               trans->alloc_blocks;
-                                       wrap(&journal->jbd_fs->sb, journal->start);
-                                       journal->trans_id = trans->trans_id + 1;
-                                       jbd_journal_free_trans(journal,
-                                                              trans, false);
+                                       jbd_journal_skip_pure_revoke(journal,
+                                                                    trans);
                                } else {
                                        journal->start = trans->start_iblock;
                                        wrap(&journal->jbd_fs->sb, journal->start);
@@ -1410,63 +1488,83 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
        }
 }
 
-/**@brief  Commit one transaction on transaction queue
- *         to the journal.
- * @param  journal current journal session.*/
-void jbd_journal_commit_one(struct jbd_journal *journal)
+/**@brief  Commit a transaction to the journal immediately.
+ * @param  journal current journal session
+ * @param  trans transaction
+ * @return standard error code*/
+int jbd_journal_commit_trans(struct jbd_journal *journal,
+                            struct jbd_trans *trans)
 {
        int rc = EOK;
        uint32_t last = journal->last;
-       struct jbd_trans *trans;
 
-       if ((trans = TAILQ_FIRST(&journal->trans_queue))) {
-               TAILQ_REMOVE(&journal->trans_queue, trans, trans_node);
+       trans->trans_id = journal->alloc_trans_id;
+       rc = jbd_journal_prepare(journal, trans);
+       if (rc != EOK)
+               goto Finish;
 
-               trans->trans_id = journal->alloc_trans_id;
-               rc = jbd_journal_prepare(journal, trans);
-               if (rc != EOK)
-                       goto Finish;
+       rc = jbd_journal_prepare_revoke(journal, trans);
+       if (rc != EOK)
+               goto Finish;
 
-               rc = jbd_journal_prepare_revoke(journal, trans);
-               if (rc != EOK)
-                       goto Finish;
+       if (LIST_EMPTY(&trans->buf_list) &&
+           LIST_EMPTY(&trans->revoke_list)) {
+               /* Since there are no entries in both buffer list
+                * and revoke entry list, we do not consider trans as
+                * complete transaction and just return EOK.*/
+               jbd_journal_free_trans(journal, trans, false);
+               goto Finish;
+       }
 
-               rc = jbd_trans_write_commit_block(trans);
-               if (rc != EOK)
-                       goto Finish;
-
-               journal->alloc_trans_id++;
-               if (TAILQ_EMPTY(&journal->cp_queue)) {
-                       if (trans->data_cnt) {
-                               journal->start = trans->start_iblock;
-                               wrap(&journal->jbd_fs->sb, journal->start);
-                               journal->trans_id = trans->trans_id;
-                               jbd_journal_write_sb(journal);
-                               jbd_write_sb(journal->jbd_fs);
-                               TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
-                                               trans_node);
-                               jbd_journal_cp_trans(journal, trans);
-                       } else {
-                               journal->start = trans->start_iblock +
-                                       trans->alloc_blocks;
-                               wrap(&journal->jbd_fs->sb, journal->start);
-                               journal->trans_id = trans->trans_id + 1;
-                               jbd_journal_write_sb(journal);
-                               jbd_journal_free_trans(journal, trans, false);
-                       }
-               } else {
+       rc = jbd_trans_write_commit_block(trans);
+       if (rc != EOK)
+               goto Finish;
+
+       journal->alloc_trans_id++;
+       if (TAILQ_EMPTY(&journal->cp_queue)) {
+               if (trans->data_cnt) {
+                       journal->start = trans->start_iblock;
+                       wrap(&journal->jbd_fs->sb, journal->start);
+                       journal->trans_id = trans->trans_id;
+                       jbd_journal_write_sb(journal);
+                       jbd_write_sb(journal->jbd_fs);
                        TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
                                        trans_node);
-                       if (trans->data_cnt)
-                               jbd_journal_cp_trans(journal, trans);
-
+                       jbd_journal_cp_trans(journal, trans);
+               } else {
+                       journal->start = trans->start_iblock +
+                               trans->alloc_blocks;
+                       wrap(&journal->jbd_fs->sb, journal->start);
+                       journal->trans_id = trans->trans_id + 1;
+                       jbd_journal_write_sb(journal);
+                       jbd_journal_free_trans(journal, trans, false);
                }
+       } else {
+               TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
+                               trans_node);
+               if (trans->data_cnt)
+                       jbd_journal_cp_trans(journal, trans);
+
        }
 Finish:
        if (rc != EOK) {
                journal->last = last;
                jbd_journal_free_trans(journal, trans, true);
        }
+       return rc;
+}
+
+/**@brief  Commit one transaction on transaction queue
+ *         to the journal.
+ * @param  journal current journal session.*/
+void jbd_journal_commit_one(struct jbd_journal *journal)
+{
+       struct jbd_trans *trans;
+
+       if ((trans = TAILQ_FIRST(&journal->trans_queue))) {
+               TAILQ_REMOVE(&journal->trans_queue, trans, trans_node);
+               jbd_journal_commit_trans(journal, trans);
+       }
 }
 
 /**@brief  Commit all the transactions on transaction queue