return jbd_write_sb(jbd_fs);
}
+static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
+ struct ext4_buf *buf __unused,
+ int res,
+ void *arg);
+
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;
+ void *tmp_data = malloc(journal->block_size);
+ ext4_assert(tmp_data);
+
TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
tmp) {
- struct ext4_block block = jbd_buf->block;
- ext4_block_flush_buf(fs->bdev, block.buf);
+ struct ext4_buf *buf = jbd_buf->block_rec->buf;
+ /* The buffer in memory is still dirty. */
+ if (buf) {
+ if (jbd_buf->block_rec->trans != trans) {
+ int r;
+ struct ext4_block jbd_block = EXT4_BLOCK_ZERO();
+ ext4_assert(ext4_block_get(fs->bdev,
+ &jbd_block,
+ jbd_buf->jbd_lba) == EOK);
+ memcpy(tmp_data, jbd_block.data,
+ journal->block_size);
+ ext4_block_set(fs->bdev, &jbd_block);
+ r = ext4_blocks_set_direct(fs->bdev, tmp_data,
+ buf->lba, 1);
+ jbd_trans_end_write(fs->bdev->bc, buf, r, jbd_buf);
+ } else
+ ext4_block_flush_buf(fs->bdev, buf);
+
+ }
}
+
+ free(tmp_data);
}
static void
struct jbd_fs *jbd_fs = journal->jbd_fs;
uint32_t features_incompatible;
- /* Commit all the transactions to the journal.*/
- jbd_journal_commit_all(journal);
-
/* Make sure that journalled content have reached
* the disk.*/
jbd_journal_purge_cp_trans(journal, true);
return trans;
}
-static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
- struct ext4_buf *buf __unused,
- int res,
- void *arg);
-
/**@brief gain access to it before making any modications.
* @param journal current journal session
* @param trans transaction
&tmp);
}
+static void
+jbd_trans_change_ownership(struct jbd_block_rec *block_rec,
+ struct jbd_trans *new_trans,
+ struct ext4_buf *new_buf)
+{
+ LIST_REMOVE(block_rec, tbrec_node);
+ /* Now this block record belongs to this transaction. */
+ LIST_INSERT_HEAD(&new_trans->tbrec_list, block_rec, tbrec_node);
+ block_rec->trans = new_trans;
+ block_rec->buf = new_buf;
+}
+
static inline struct jbd_block_rec *
jbd_trans_insert_block_rec(struct jbd_trans *trans,
ext4_fsblk_t lba,
struct jbd_block_rec *block_rec;
block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
if (block_rec) {
- LIST_REMOVE(block_rec, tbrec_node);
- /* Data should be flushed to disk already. */
- ext4_assert(!block_rec->buf);
- /* Now this block record belongs to this transaction. */
- LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
- block_rec->trans = trans;
+ jbd_trans_change_ownership(block_rec, trans, buf);
return block_rec;
}
block_rec = calloc(1, sizeof(struct jbd_block_rec));
block_rec->lba = lba;
block_rec->buf = buf;
block_rec->trans = trans;
+ TAILQ_INIT(&block_rec->dirty_buf_queue);
LIST_INSERT_HEAD(&trans->tbrec_list, block_rec, tbrec_node);
RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
return block_rec;
}
+static void
+jbd_trans_finish_callback(struct jbd_journal *journal,
+ const struct jbd_trans *trans,
+ struct jbd_block_rec *block_rec,
+ bool abort)
+{
+ struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
+ if (block_rec->trans != trans)
+ return;
+
+ if (!abort) {
+ struct jbd_buf *jbd_buf, *tmp;
+ TAILQ_FOREACH_SAFE(jbd_buf,
+ &block_rec->dirty_buf_queue,
+ dirty_buf_node,
+ tmp) {
+ /* All we need is a fake ext4_buf. */
+ struct ext4_buf buf;
+
+ jbd_trans_end_write(fs->bdev->bc,
+ &buf,
+ EOK,
+ jbd_buf);
+ }
+ } else {
+ struct jbd_buf *jbd_buf;
+ struct ext4_block jbd_block = EXT4_BLOCK_ZERO(),
+ block = EXT4_BLOCK_ZERO();
+ jbd_buf = TAILQ_LAST(&block_rec->dirty_buf_queue,
+ jbd_buf_dirty);
+ if (jbd_buf) {
+ ext4_assert(ext4_block_get(fs->bdev,
+ &jbd_block,
+ jbd_buf->jbd_lba) == EOK);
+ ext4_assert(ext4_block_get_noread(fs->bdev,
+ &block,
+ block_rec->lba) == EOK);
+ memcpy(block.data, jbd_block.data,
+ journal->block_size);
+
+ jbd_trans_change_ownership(block_rec,
+ jbd_buf->trans, block.buf);
+
+ block.buf->end_write = jbd_trans_end_write;
+ block.buf->end_write_arg = jbd_buf;
+
+ ext4_bcache_set_flag(jbd_block.buf, BC_TMP);
+ ext4_bcache_set_dirty(block.buf);
+
+ ext4_block_set(fs->bdev, &jbd_block);
+ ext4_block_set(fs->bdev, &block);
+ return;
+ }
+ }
+}
+
static inline void
jbd_trans_remove_block_rec(struct jbd_journal *journal,
struct jbd_block_rec *block_rec,
{
struct jbd_buf *buf;
- if (!ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
- block->buf->end_write != jbd_trans_end_write) {
- struct jbd_block_rec *block_rec;
- buf = calloc(1, sizeof(struct jbd_buf));
- if (!buf)
- return ENOMEM;
+ struct jbd_block_rec *block_rec;
+ if (block->buf->end_write == jbd_trans_end_write) {
+ buf = block->buf->end_write_arg;
+ if (buf && buf->trans == trans)
+ return EOK;
+ }
+ buf = calloc(1, sizeof(struct jbd_buf));
+ if (!buf)
+ return ENOMEM;
- if ((block_rec = jbd_trans_insert_block_rec(trans,
+ if ((block_rec = jbd_trans_insert_block_rec(trans,
block->lb_id,
block->buf)) == NULL) {
- free(buf);
- return ENOMEM;
- }
+ free(buf);
+ return ENOMEM;
+ }
- buf->block_rec = block_rec;
- buf->trans = trans;
- buf->block = *block;
- ext4_bcache_inc_ref(block->buf);
+ TAILQ_INSERT_TAIL(&block_rec->dirty_buf_queue,
+ buf,
+ dirty_buf_node);
- /* 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 = buf;
+ buf->block_rec = block_rec;
+ buf->trans = trans;
+ buf->block = *block;
+ ext4_bcache_inc_ref(block->buf);
- trans->data_cnt++;
- TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
+ /* 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 = buf;
- ext4_bcache_set_dirty(block->buf);
- }
+ trans->data_cnt++;
+ TAILQ_INSERT_HEAD(&trans->buf_queue, buf, buf_node);
+
+ ext4_bcache_set_dirty(block->buf);
return EOK;
}
struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
TAILQ_FOREACH_SAFE(jbd_buf, &trans->buf_queue, buf_node,
tmp) {
+ block_rec = jbd_buf->block_rec;
if (abort) {
jbd_buf->block.buf->end_write = NULL;
jbd_buf->block.buf->end_write_arg = NULL;
ext4_block_set(fs->bdev, &jbd_buf->block);
}
+ TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+ jbd_buf,
+ dirty_buf_node);
+ jbd_trans_finish_callback(journal,
+ trans,
+ block_rec,
+ abort);
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
free(jbd_buf);
}
if (ext4_bcache_test_flag(jbd_buf->block.buf,
BC_DIRTY))
break;
+
+ TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+ jbd_buf,
+ dirty_buf_node);
+
+ jbd_buf->block.buf->end_write = NULL;
+ jbd_buf->block.buf->end_write_arg = NULL;
+ jbd_trans_finish_callback(journal,
+ trans,
+ jbd_buf->block_rec,
+ true);
/* The buffer has not been modified, just release
* that jbd_buf. */
jbd_buf->block_rec, trans);
trans->data_cnt--;
- jbd_buf->block.buf->end_write = NULL;
- jbd_buf->block.buf->end_write_arg = NULL;
ext4_block_set(fs->bdev, &jbd_buf->block);
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
free(jbd_buf);
bool uuid_exist = false;
if (!ext4_bcache_test_flag(jbd_buf->block.buf,
BC_DIRTY)) {
+ TAILQ_REMOVE(&jbd_buf->block_rec->dirty_buf_queue,
+ jbd_buf,
+ dirty_buf_node);
+
+ jbd_buf->block.buf->end_write = NULL;
+ jbd_buf->block.buf->end_write_arg = NULL;
+ jbd_trans_finish_callback(journal,
+ trans,
+ jbd_buf->block_rec,
+ true);
+
/* The buffer has not been modified, just release
* that jbd_buf. */
jbd_trans_remove_block_rec(journal,
jbd_buf->block_rec, trans);
trans->data_cnt--;
- jbd_buf->block.buf->end_write = NULL;
- jbd_buf->block.buf->end_write_arg = NULL;
ext4_block_set(fs->bdev, &jbd_buf->block);
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
free(jbd_buf);
memcpy(data_block.data, jbd_buf->block.data,
journal->block_size);
+ jbd_buf->jbd_lba = data_block.lb_id;
rc = jbd_block_set(journal->jbd_fs, &data_block);
if (rc != EOK)
return rc;
}
-/**@brief Submit the transaction to transaction queue.
- * @param journal current journal session
- * @param trans transaction*/
-void
-jbd_journal_submit_trans(struct jbd_journal *journal,
- struct jbd_trans *trans)
-{
- TAILQ_INSERT_TAIL(&journal->trans_queue,
- trans,
- trans_node);
-}
-
/**@brief Put references of block descriptors in a transaction.
* @param journal current journal session
* @param trans transaction*/
{
struct jbd_buf *jbd_buf = arg;
struct jbd_trans *trans = jbd_buf->trans;
+ struct jbd_block_rec *block_rec = jbd_buf->block_rec;
struct jbd_journal *journal = trans->journal;
bool first_in_queue =
trans == TAILQ_FIRST(&journal->cp_queue);
trans->error = res;
TAILQ_REMOVE(&trans->buf_queue, jbd_buf, buf_node);
- jbd_buf->block_rec->buf = NULL;
- free(jbd_buf);
+ TAILQ_REMOVE(&block_rec->dirty_buf_queue,
+ jbd_buf,
+ dirty_buf_node);
+
+ jbd_trans_finish_callback(journal,
+ trans,
+ jbd_buf->block_rec,
+ false);
+ if (block_rec->trans == trans) {
+ block_rec->buf = NULL;
+ /* Clear the end_write and end_write_arg fields. */
+ buf->end_write = NULL;
+ buf->end_write_arg = NULL;
+ }
- /* Clear the end_write and end_write_arg fields. */
- buf->end_write = NULL;
- buf->end_write_arg = NULL;
+ free(jbd_buf);
trans->written_cnt++;
if (trans->written_cnt == trans->data_cnt) {
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
- * to the journal.
- * @param journal current journal session.*/
-void jbd_journal_commit_all(struct jbd_journal *journal)
-{
- while (!TAILQ_EMPTY(&journal->trans_queue)) {
- jbd_journal_commit_one(journal);
- }
-}
-
/**
* @}
*/