X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=lwext4%2Fext4_journal.c;h=86366a1bf044a5792dea5395908bc37a6c7f104c;hb=ea7ce7dc4a1cb5af01175324c5da8dceb3f8652d;hp=dca62d17b8f9651f559d4988dfe81447cf0580ea;hpb=b84fde1edbeef2a5309f56dbb40b7e67f4c2ab79;p=lwext4.git diff --git a/lwext4/ext4_journal.c b/lwext4/ext4_journal.c index dca62d1..86366a1 100644 --- a/lwext4/ext4_journal.c +++ b/lwext4/ext4_journal.c @@ -1266,16 +1266,43 @@ int jbd_journal_start(struct jbd_fs *jbd_fs, 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 @@ -1343,9 +1370,6 @@ int jbd_journal_stop(struct jbd_journal *journal) 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); @@ -1415,11 +1439,6 @@ jbd_journal_new_trans(struct jbd_journal *journal) 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 @@ -1458,6 +1477,18 @@ jbd_trans_block_rec_lookup(struct jbd_journal *journal, &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, @@ -1466,12 +1497,7 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans, 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)); @@ -1481,11 +1507,68 @@ jbd_trans_insert_block_rec(struct jbd_trans *trans, 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, @@ -1511,35 +1594,41 @@ int jbd_trans_set_block_dirty(struct jbd_trans *trans, { 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; } @@ -1606,6 +1695,7 @@ void jbd_journal_free_trans(struct jbd_journal *journal, 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; @@ -1613,6 +1703,13 @@ void jbd_journal_free_trans(struct jbd_journal *journal, 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); } @@ -1691,6 +1788,17 @@ static int jbd_journal_prepare(struct jbd_journal *journal, 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. */ @@ -1698,8 +1806,6 @@ static int jbd_journal_prepare(struct jbd_journal *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); @@ -1710,14 +1816,23 @@ static int jbd_journal_prepare(struct jbd_journal *journal, 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); @@ -1791,6 +1906,7 @@ again: 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) @@ -1901,18 +2017,6 @@ again: 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*/ @@ -1936,6 +2040,7 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused, { 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); @@ -1943,12 +2048,22 @@ static void jbd_trans_end_write(struct ext4_bcache *bc __unused, 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) { @@ -2038,29 +2153,6 @@ Finish: 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); - } -} - /** * @} */