ext4_journal: forcibly flush data to disk when stop journalling.
[lwext4.git] / lwext4 / ext4_journal.c
index 033bdc2158dbe063f730e162afccf3962bc2203b..d0032a0edfd51bac4bfffacf888b03bb1e246eb7 100644 (file)
@@ -39,6 +39,7 @@
 #include "ext4_types.h"
 #include "ext4_fs.h"
 #include "ext4_super.h"
+#include "ext4_journal.h"
 #include "ext4_errno.h"
 #include "ext4_blockdev.h"
 #include "ext4_crc32c.h"
@@ -209,6 +210,9 @@ int jbd_block_get(struct jbd_fs *jbd_fs,
 
        struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
        rc = ext4_block_get(bdev, block, fblock);
+       if (rc == EOK)
+               ext4_bcache_set_flag(block->buf, BC_FLUSH);
+
        return rc;
 }
 
@@ -226,6 +230,9 @@ int jbd_block_get_noread(struct jbd_fs *jbd_fs,
 
        struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
        rc = ext4_block_get_noread(bdev, block, fblock);
+       if (rc == EOK)
+               ext4_bcache_set_flag(block->buf, BC_FLUSH);
+
        return rc;
 }
 
@@ -761,6 +768,8 @@ int jbd_recover(struct jbd_fs *jbd_fs)
                           features_incompatible,
                           features_incompatible);
                jbd_fs->dirty = true;
+               r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                                 &jbd_fs->inode_ref.fs->sb);
        }
        jbd_destroy_revoke_tree(&info);
        return r;
@@ -777,6 +786,19 @@ void jbd_journal_write_sb(struct jbd_journal *journal)
 int jbd_journal_start(struct jbd_fs *jbd_fs,
                      struct jbd_journal *journal)
 {
+       int r;
+       uint32_t features_incompatible =
+                       ext4_get32(&jbd_fs->inode_ref.fs->sb,
+                                  features_incompatible);
+       features_incompatible |= EXT4_FINCOM_RECOVER;
+       ext4_set32(&jbd_fs->inode_ref.fs->sb,
+                       features_incompatible,
+                       features_incompatible);
+       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                       &jbd_fs->inode_ref.fs->sb);
+       if (r != EOK)
+               return r;
+
        journal->first = jbd_get32(&jbd_fs->sb, first);
        journal->start = journal->first;
        journal->last = journal->first;
@@ -794,6 +816,26 @@ int jbd_journal_start(struct jbd_fs *jbd_fs,
 
 int jbd_journal_stop(struct jbd_journal *journal)
 {
+
+       int r;
+       struct jbd_fs *jbd_fs = journal->jbd_fs;
+       uint32_t features_incompatible;
+
+       jbd_journal_commit_all(journal);
+       ext4_block_cache_flush(jbd_fs->inode_ref.fs->bdev);
+
+       features_incompatible =
+               ext4_get32(&jbd_fs->inode_ref.fs->sb,
+                          features_incompatible);
+       features_incompatible &= ~EXT4_FINCOM_RECOVER;
+       ext4_set32(&jbd_fs->inode_ref.fs->sb,
+                       features_incompatible,
+                       features_incompatible);
+       r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
+                       &jbd_fs->inode_ref.fs->sb);
+       if (r != EOK)
+               return r;
+
        journal->start = 0;
        journal->trans_id = 0;
        jbd_journal_write_sb(journal);
@@ -803,9 +845,14 @@ int jbd_journal_stop(struct jbd_journal *journal)
 static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
                                        struct jbd_trans *trans)
 {
-       uint32_t start_block = journal->last++;
+       uint32_t start_block;
+
+       start_block = journal->last++;
        trans->alloc_blocks++;
        wrap(&journal->jbd_fs->sb, journal->last);
+       if (journal->last == journal->start)
+               ext4_block_cache_flush(journal->jbd_fs->inode_ref.fs->bdev);
+
        return start_block;
 }
 
@@ -1096,7 +1143,8 @@ void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
        struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
        LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
                        tmp) {
-               ext4_block_set(fs->bdev, &jbd_buf->block);
+               struct ext4_block block = jbd_buf->block;
+               ext4_block_set(fs->bdev, &block);
        }
 }
 
@@ -1107,33 +1155,54 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
 {
        struct jbd_trans *trans = arg;
        struct jbd_journal *journal = trans->journal;
+       bool first_in_queue =
+               trans == TAILQ_FIRST(&journal->cp_queue);
        if (res != EOK)
                trans->error = res;
 
        trans->written_cnt++;
        if (trans->written_cnt == trans->data_cnt) {
                TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
-               journal->start = trans->start_iblock +
-                                trans->alloc_blocks;
-               journal->trans_id = trans->trans_id + 1;
-               jbd_journal_write_sb(journal);
-               jbd_write_sb(journal->jbd_fs);
+
+               if (first_in_queue) {
+                       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);
 
-               if ((trans = TAILQ_FIRST(&journal->cp_queue))) {
-                       jbd_journal_cp_trans(journal, trans);
+               if (first_in_queue) {
+                       while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
+                               if (!trans->data_cnt) {
+                                       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);
+                               } else {
+                                       journal->start = trans->start_iblock;
+                                       wrap(&journal->jbd_fs->sb, journal->start);
+                                       journal->trans_id = trans->trans_id;
+                                       break;
+                               }
+                       }
+                       jbd_journal_write_sb(journal);
+                       jbd_write_sb(journal->jbd_fs);
                }
        }
 }
 
-/*
- * XXX: one should disable cache writeback first.
- */
 void jbd_journal_commit_one(struct jbd_journal *journal)
 {
        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);
 
@@ -1151,14 +1220,31 @@ void jbd_journal_commit_one(struct jbd_journal *journal)
                        goto Finish;
 
                journal->alloc_trans_id++;
-               if (trans->data_cnt) {
+               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 {
                        TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
                                        trans_node);
-                       if (trans == TAILQ_FIRST(&journal->cp_queue)) {
+                       if (trans->data_cnt)
                                jbd_journal_cp_trans(journal, trans);
-                       }
-               } else
-                       jbd_journal_free_trans(journal, trans, false);
+
+               }
        }
 Finish:
        if (rc != EOK) {
@@ -1167,6 +1253,13 @@ Finish:
        }
 }
 
+void jbd_journal_commit_all(struct jbd_journal *journal)
+{
+       while (!TAILQ_EMPTY(&journal->trans_queue)) {
+               jbd_journal_commit_one(journal);
+       }
+}
+
 /**
  * @}
  */