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))
} 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))
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*/
/* 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,
/* 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;
}
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)
/* 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;
}
LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
tmp) {
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);
}
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;
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;
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);
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;