2 * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3 * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 /** @addtogroup lwext4
34 * @file ext4_journal.c
35 * @brief Journal handle functions
38 #include "ext4_config.h"
39 #include "ext4_types.h"
41 #include "ext4_super.h"
42 #include "ext4_errno.h"
43 #include "ext4_blockdev.h"
44 #include "ext4_crc32c.h"
45 #include "ext4_debug.h"
54 RB_ENTRY(revoke_entry) revoke_node;
58 uint32_t start_trans_id;
59 uint32_t last_trans_id;
60 uint32_t this_trans_id;
61 RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
65 struct recover_info *info;
67 uint32_t this_trans_id;
71 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
73 if (a->block > b->block)
75 else if (a->block < b->block)
80 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
81 jbd_revoke_entry_cmp, static inline)
83 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
84 #define jbd_free_revoke_entry(addr) free(addr)
86 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
88 ext4_fsblk_t *fblock);
90 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
93 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
96 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
100 offset = fblock * ext4_sb_get_block_size(&fs->sb);
101 return ext4_block_writebytes(fs->bdev, offset, s,
102 EXT4_SUPERBLOCK_SIZE);
105 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
108 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
111 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
115 offset = fblock * ext4_sb_get_block_size(&fs->sb);
116 return ext4_block_readbytes(fs->bdev, offset, s,
117 EXT4_SUPERBLOCK_SIZE);
120 static bool jbd_verify_sb(struct jbd_sb *sb)
122 struct jbd_bhdr *header = &sb->header;
123 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
126 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
127 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
133 static int jbd_write_sb(struct jbd_fs *jbd_fs)
137 rc = jbd_sb_write(jbd_fs, &jbd_fs->sb);
141 jbd_fs->dirty = false;
146 int jbd_get_fs(struct ext4_fs *fs,
147 struct jbd_fs *jbd_fs)
150 uint32_t journal_ino;
152 memset(jbd_fs, 0, sizeof(struct jbd_fs));
153 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
155 rc = ext4_fs_get_inode_ref(fs,
159 memset(jbd_fs, 0, sizeof(struct jbd_fs));
162 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
164 memset(jbd_fs, 0, sizeof(struct jbd_fs));
165 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
168 if (!jbd_verify_sb(&jbd_fs->sb)) {
169 memset(jbd_fs, 0, sizeof(struct jbd_fs));
170 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
177 int jbd_put_fs(struct jbd_fs *jbd_fs)
180 rc = jbd_write_sb(jbd_fs);
182 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
186 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
188 ext4_fsblk_t *fblock)
190 int rc = ext4_fs_get_inode_dblk_idx(
198 int jbd_block_get(struct jbd_fs *jbd_fs,
199 struct ext4_block *block,
202 /* TODO: journal device. */
204 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
205 rc = jbd_inode_bmap(jbd_fs, iblock,
210 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
211 rc = ext4_block_get(bdev, block, fblock);
215 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
216 struct ext4_block *block,
219 /* TODO: journal device. */
221 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
222 rc = jbd_inode_bmap(jbd_fs, iblock,
227 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
228 rc = ext4_block_get_noread(bdev, block, fblock);
232 int jbd_block_set(struct jbd_fs *jbd_fs,
233 struct ext4_block *block)
235 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
240 * helper functions to deal with 32 or 64bit block numbers.
242 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
246 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
247 JBD_FEATURE_INCOMPAT_CSUM_V3))
248 return sizeof(struct jbd_block_tag3);
250 size = sizeof(struct jbd_block_tag);
252 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
253 JBD_FEATURE_INCOMPAT_CSUM_V2))
254 size += sizeof(uint16_t);
256 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
257 JBD_FEATURE_INCOMPAT_64BIT))
260 return size - sizeof(uint32_t);
263 /**@brief: tag information. */
268 uint8_t uuid[UUID_SIZE];
273 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
276 int32_t remain_buf_size,
277 struct tag_info *tag_info)
280 tag_info->tag_bytes = tag_bytes;
281 tag_info->uuid_exist = false;
282 tag_info->last_tag = false;
284 if (remain_buf_size - tag_bytes < 0)
287 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
288 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
289 struct jbd_block_tag3 *tag = __tag;
290 tag_info->block = jbd_get32(tag, blocknr);
291 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
292 JBD_FEATURE_INCOMPAT_64BIT))
294 (uint64_t)jbd_get32(tag, blocknr_high) << 32;
296 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
299 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
300 if (remain_buf_size - tag_bytes < UUID_SIZE)
303 uuid_start = (char *)tag + tag_bytes;
304 tag_info->uuid_exist = true;
305 tag_info->tag_bytes += UUID_SIZE;
306 memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
309 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
310 tag_info->last_tag = true;
313 struct jbd_block_tag *tag = __tag;
314 tag_info->block = jbd_get32(tag, blocknr);
315 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
316 JBD_FEATURE_INCOMPAT_64BIT))
318 (uint64_t)jbd_get32(tag, blocknr_high) << 32;
320 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
323 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
324 if (remain_buf_size - tag_bytes < UUID_SIZE)
327 uuid_start = (char *)tag + tag_bytes;
328 tag_info->uuid_exist = true;
329 tag_info->tag_bytes += UUID_SIZE;
330 memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
333 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
334 tag_info->last_tag = true;
341 jbd_write_block_tag(struct jbd_fs *jbd_fs,
343 int32_t remain_buf_size,
344 struct tag_info *tag_info)
347 int tag_bytes = jbd_tag_bytes(jbd_fs);
349 tag_info->tag_bytes = tag_bytes;
351 if (remain_buf_size - tag_bytes < 0)
354 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
355 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
356 struct jbd_block_tag3 *tag = __tag;
357 jbd_set32(tag, blocknr, tag_info->block);
358 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
359 JBD_FEATURE_INCOMPAT_64BIT))
360 jbd_set32(tag, blocknr_high, tag_info->block >> 32);
362 if (!tag_info->uuid_exist) {
363 if (remain_buf_size - tag_bytes < UUID_SIZE)
366 uuid_start = (char *)tag + tag_bytes;
367 tag_info->tag_bytes += UUID_SIZE;
368 memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
369 jbd_set32(tag, flags,
370 jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
373 if (tag_info->last_tag)
374 jbd_set32(tag, flags,
375 jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
378 struct jbd_block_tag *tag = __tag;
379 jbd_set32(tag, blocknr, tag_info->block);
380 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
381 JBD_FEATURE_INCOMPAT_64BIT))
382 jbd_set32(tag, blocknr_high, tag_info->block >> 32);
384 if (!tag_info->uuid_exist) {
385 if (remain_buf_size - tag_bytes < UUID_SIZE)
388 uuid_start = (char *)tag + tag_bytes;
389 tag_info->tag_bytes += UUID_SIZE;
390 memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
391 jbd_set16(tag, flags,
392 jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
395 if (tag_info->last_tag)
396 jbd_set16(tag, flags,
397 jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
404 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
406 int32_t tag_tbl_size,
407 void (*func)(struct jbd_fs * jbd_fs,
413 ext4_fsblk_t block = 0;
414 char *tag_start, *tag_ptr;
415 int tag_bytes = jbd_tag_bytes(jbd_fs);
416 tag_start = __tag_start;
419 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
420 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
421 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
422 JBD_FEATURE_INCOMPAT_CSUM_V3))
423 tag_tbl_size -= sizeof(struct jbd_block_tail);
425 while (tag_tbl_size) {
426 struct tag_info tag_info;
427 int rc = jbd_extract_block_tag(jbd_fs,
436 func(jbd_fs, block, tag_info.uuid, arg);
438 if (tag_info.last_tag)
441 tag_ptr += tag_info.tag_bytes;
442 tag_tbl_size -= tag_info.tag_bytes;
446 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
451 uint32_t *iblock = arg;
452 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
459 static struct revoke_entry *
460 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
462 struct revoke_entry tmp = {
466 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
469 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
471 uint8_t *uuid __unused,
475 struct replay_arg *arg = __arg;
476 struct recover_info *info = arg->info;
477 uint32_t *this_block = arg->this_block;
478 struct revoke_entry *revoke_entry;
479 struct ext4_block journal_block, ext4_block;
480 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
484 revoke_entry = jbd_revoke_entry_lookup(info, block);
486 arg->this_trans_id < revoke_entry->trans_id)
490 "Replaying block in block_tag: %" PRIu64 "\n",
493 r = jbd_block_get(jbd_fs, &journal_block, *this_block);
498 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
500 jbd_block_set(jbd_fs, &journal_block);
504 memcpy(ext4_block.data,
506 jbd_get32(&jbd_fs->sb, blocksize));
508 ext4_bcache_set_dirty(ext4_block.buf);
509 ext4_block_set(fs->bdev, &ext4_block);
511 uint16_t mount_count, state;
512 mount_count = ext4_get16(&fs->sb, mount_count);
513 state = ext4_get16(&fs->sb, state);
516 journal_block.data + EXT4_SUPERBLOCK_OFFSET,
517 EXT4_SUPERBLOCK_SIZE);
519 /* Mark system as mounted */
520 ext4_set16(&fs->sb, state, state);
521 r = ext4_sb_write(fs->bdev, &fs->sb);
525 /*Update mount count*/
526 ext4_set16(&fs->sb, mount_count, mount_count);
529 jbd_block_set(jbd_fs, &journal_block);
534 static void jbd_add_revoke_block_tags(struct recover_info *info,
537 struct revoke_entry *revoke_entry;
539 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
540 revoke_entry = jbd_revoke_entry_lookup(info, block);
542 revoke_entry->trans_id = info->this_trans_id;
546 revoke_entry = jbd_alloc_revoke_entry();
547 ext4_assert(revoke_entry);
548 revoke_entry->block = block;
549 revoke_entry->trans_id = info->this_trans_id;
550 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
555 static void jbd_destroy_revoke_tree(struct recover_info *info)
557 while (!RB_EMPTY(&info->revoke_root)) {
558 struct revoke_entry *revoke_entry =
559 RB_MIN(jbd_revoke, &info->revoke_root);
560 ext4_assert(revoke_entry);
561 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
562 jbd_free_revoke_entry(revoke_entry);
566 /* Make sure we wrap around the log correctly! */
567 #define wrap(sb, var) \
569 if (var >= jbd_get32((sb), maxlen)) \
570 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
573 #define ACTION_SCAN 0
574 #define ACTION_REVOKE 1
575 #define ACTION_RECOVER 2
578 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
579 struct jbd_bhdr *header,
580 struct recover_info *info)
583 struct jbd_revoke_header *revoke_hdr =
584 (struct jbd_revoke_header *)header;
585 uint32_t i, nr_entries, record_len = 4;
586 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
587 JBD_FEATURE_INCOMPAT_64BIT))
590 nr_entries = (jbd_get32(revoke_hdr, count) -
591 sizeof(struct jbd_revoke_header)) /
594 blocks_entry = (char *)(revoke_hdr + 1);
596 for (i = 0;i < nr_entries;i++) {
597 if (record_len == 8) {
599 (uint64_t *)blocks_entry;
600 jbd_add_revoke_block_tags(info, to_be64(*blocks));
603 (uint32_t *)blocks_entry;
604 jbd_add_revoke_block_tags(info, to_be32(*blocks));
606 blocks_entry += record_len;
610 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
611 struct jbd_bhdr *header,
614 jbd_iterate_block_table(jbd_fs,
616 jbd_get32(&jbd_fs->sb, blocksize) -
617 sizeof(struct jbd_bhdr),
618 jbd_display_block_tags,
622 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
623 struct jbd_bhdr *header,
624 struct replay_arg *arg)
626 jbd_iterate_block_table(jbd_fs,
628 jbd_get32(&jbd_fs->sb, blocksize) -
629 sizeof(struct jbd_bhdr),
630 jbd_replay_block_tags,
634 int jbd_iterate_log(struct jbd_fs *jbd_fs,
635 struct recover_info *info,
639 bool log_end = false;
640 struct jbd_sb *sb = &jbd_fs->sb;
641 uint32_t start_trans_id, this_trans_id;
642 uint32_t start_block, this_block;
644 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
645 start_block = this_block = jbd_get32(sb, start);
647 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
651 struct ext4_block block;
652 struct jbd_bhdr *header;
653 if (action != ACTION_SCAN)
654 if (this_trans_id > info->last_trans_id) {
659 r = jbd_block_get(jbd_fs, &block, this_block);
663 header = (struct jbd_bhdr *)block.data;
664 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
665 jbd_block_set(jbd_fs, &block);
670 if (jbd_get32(header, sequence) != this_trans_id) {
671 if (action != ACTION_SCAN)
674 jbd_block_set(jbd_fs, &block);
679 switch (jbd_get32(header, blocktype)) {
680 case JBD_DESCRIPTOR_BLOCK:
681 ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
682 "trans_id: %" PRIu32"\n",
683 this_block, this_trans_id);
684 if (action == ACTION_RECOVER) {
685 struct replay_arg replay_arg;
686 replay_arg.info = info;
687 replay_arg.this_block = &this_block;
688 replay_arg.this_trans_id = this_trans_id;
690 jbd_replay_descriptor_block(jbd_fs,
691 header, &replay_arg);
693 jbd_debug_descriptor_block(jbd_fs,
694 header, &this_block);
697 case JBD_COMMIT_BLOCK:
698 ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
699 "trans_id: %" PRIu32"\n",
700 this_block, this_trans_id);
703 case JBD_REVOKE_BLOCK:
704 ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
705 "trans_id: %" PRIu32"\n",
706 this_block, this_trans_id);
707 if (action == ACTION_REVOKE) {
708 info->this_trans_id = this_trans_id;
709 jbd_build_revoke_tree(jbd_fs,
717 jbd_block_set(jbd_fs, &block);
719 wrap(sb, this_block);
720 if (this_block == start_block)
724 ext4_dbg(DEBUG_JBD, "End of journal.\n");
725 if (r == EOK && action == ACTION_SCAN) {
726 info->start_trans_id = start_trans_id;
727 if (this_trans_id > start_trans_id)
728 info->last_trans_id = this_trans_id - 1;
730 info->last_trans_id = this_trans_id;
736 int jbd_recover(struct jbd_fs *jbd_fs)
739 struct recover_info info;
740 struct jbd_sb *sb = &jbd_fs->sb;
744 RB_INIT(&info.revoke_root);
746 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
750 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
754 r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
756 jbd_set32(&jbd_fs->sb, start, 0);
757 jbd_fs->dirty = true;
759 jbd_destroy_revoke_tree(&info);
763 void jbd_journal_write_sb(struct jbd_journal *journal)
765 struct jbd_fs *jbd_fs = journal->jbd_fs;
766 jbd_set32(&jbd_fs->sb, start, journal->first);
767 jbd_set32(&jbd_fs->sb, sequence, journal->trans_id);
768 jbd_fs->dirty = true;
771 int jbd_journal_start(struct jbd_fs *jbd_fs,
772 struct jbd_journal *journal)
774 journal->first = jbd_get32(&jbd_fs->sb, first);
775 journal->start = journal->first;
776 journal->last = journal->first;
777 journal->trans_id = 1;
779 journal->block_size = jbd_get32(&jbd_fs->sb, blocksize);
781 TAILQ_INIT(&journal->trans_queue);
782 journal->jbd_fs = jbd_fs;
783 jbd_journal_write_sb(journal);
784 return jbd_write_sb(jbd_fs);
787 int jbd_journal_stop(struct jbd_journal *journal)
790 journal->trans_id = 0;
791 jbd_journal_write_sb(journal);
792 return jbd_write_sb(journal->jbd_fs);
795 static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal)
797 uint32_t start_block = journal->last++;
798 wrap(&journal->jbd_fs->sb, journal->last);
803 jbd_journal_new_trans(struct jbd_journal *journal)
805 struct jbd_trans *trans = calloc(1, sizeof(struct jbd_trans));
809 /* We will assign a trans_id to this transaction,
810 * once it has been committed.*/
811 trans->journal = journal;
816 int jbd_trans_add_block(struct jbd_trans *trans,
817 struct ext4_block *block)
819 struct jbd_buf *buf = calloc(1, sizeof(struct jbd_buf));
825 ext4_bcache_inc_ref(block->buf);
827 LIST_INSERT_HEAD(&trans->buf_list, buf, buf_node);
831 int jbd_trans_revoke_block(struct jbd_trans *trans,
834 struct jbd_revoke_rec *rec =
835 calloc(1, sizeof(struct jbd_revoke_rec));
840 LIST_INSERT_HEAD(&trans->revoke_list, rec, revoke_node);
844 void jbd_journal_free_trans(struct jbd_journal *journal,
845 struct jbd_trans *trans)
847 struct jbd_buf *jbd_buf, *tmp;
848 struct jbd_revoke_rec *rec, *tmp2;
849 LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
851 ext4_block_set(journal->jbd_fs->bdev, &jbd_buf->block);
852 LIST_REMOVE(jbd_buf, buf_node);
855 LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
857 LIST_REMOVE(rec, revoke_node);
864 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
865 struct ext4_buf *buf __unused,
869 struct jbd_trans *trans = arg;
873 static int jbd_trans_write_commit_block(struct jbd_trans *trans)
876 struct jbd_commit_header *header;
877 uint32_t commit_iblock = 0;
878 struct ext4_block commit_block;
879 struct jbd_journal *journal = trans->journal;
881 commit_iblock = jbd_journal_alloc_block(trans->journal);
882 rc = jbd_block_get_noread(journal->jbd_fs,
883 &commit_block, commit_iblock);
887 header = (struct jbd_commit_header *)commit_block.data;
888 header->header.magic = JBD_MAGIC_NUMBER;
889 header->header.blocktype = JBD_COMMIT_BLOCK;
890 header->header.sequence = trans->trans_id;
892 ext4_bcache_set_dirty(commit_block.buf);
893 rc = jbd_block_set(journal->jbd_fs, &commit_block);
900 static int jbd_journal_prepare(struct jbd_journal *journal,
901 struct jbd_trans *trans)
904 int32_t tag_tbl_size;
905 uint32_t desc_iblock = 0;
906 uint32_t data_iblock = 0;
907 char *tag_start = NULL, *tag_ptr = NULL;
908 struct jbd_buf *jbd_buf;
909 struct ext4_block desc_block, data_block;
911 LIST_FOREACH(jbd_buf, &trans->buf_list, buf_node) {
912 struct tag_info tag_info;
913 bool uuid_exist = false;
916 struct jbd_bhdr *bhdr;
917 desc_iblock = jbd_journal_alloc_block(journal);
918 rc = jbd_block_get_noread(journal->jbd_fs,
919 &desc_block, desc_iblock);
923 ext4_bcache_set_dirty(desc_block.buf);
925 bhdr = (struct jbd_bhdr *)desc_block.data;
926 bhdr->magic = JBD_MAGIC_NUMBER;
927 bhdr->blocktype = JBD_DESCRIPTOR_BLOCK;
928 bhdr->sequence = trans->trans_id;
930 tag_start = (char *)(bhdr + 1);
933 tag_tbl_size = journal->block_size -
934 sizeof(struct jbd_bhdr);
936 tag_info.block = jbd_buf->block.lb_id;
937 tag_info.uuid_exist = uuid_exist;
938 if (i == trans->data_cnt - 1)
939 tag_info.last_tag = true;
942 memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
945 rc = jbd_write_block_tag(journal->jbd_fs,
950 jbd_block_set(journal->jbd_fs, &desc_block);
955 data_iblock = jbd_journal_alloc_block(journal);
956 rc = jbd_block_get_noread(journal->jbd_fs,
957 &data_block, data_iblock);
961 ext4_bcache_set_dirty(data_block.buf);
963 memcpy(data_block.data, jbd_buf->block.data,
964 journal->block_size);
966 rc = jbd_block_set(journal->jbd_fs, &data_block);
970 tag_ptr += tag_info.tag_bytes;
971 tag_tbl_size -= tag_info.tag_bytes;
975 if (rc == EOK && desc_iblock)
976 jbd_block_set(journal->jbd_fs, &desc_block);
982 jbd_journal_prepare_revoke(struct jbd_journal *journal,
983 struct jbd_trans *trans)
986 int32_t tag_tbl_size;
987 uint32_t desc_iblock = 0;
988 char *blocks_entry = NULL;
989 struct jbd_revoke_rec *rec, *tmp;
990 struct ext4_block desc_block;
991 struct jbd_revoke_header *header = NULL;
992 int32_t record_len = 4;
994 if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
995 JBD_FEATURE_INCOMPAT_64BIT))
998 LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
1002 struct jbd_bhdr *bhdr;
1003 desc_iblock = jbd_journal_alloc_block(journal);
1004 rc = jbd_block_get_noread(journal->jbd_fs,
1005 &desc_block, desc_iblock);
1010 ext4_bcache_set_dirty(desc_block.buf);
1012 bhdr = (struct jbd_bhdr *)desc_block.data;
1013 bhdr->magic = JBD_MAGIC_NUMBER;
1014 bhdr->blocktype = JBD_REVOKE_BLOCK;
1015 bhdr->sequence = trans->trans_id;
1017 header = (struct jbd_revoke_header *)bhdr;
1018 blocks_entry = (char *)(header + 1);
1019 tag_tbl_size = journal->block_size -
1020 sizeof(struct jbd_revoke_header);
1023 if (tag_tbl_size < record_len) {
1024 header->count = journal->block_size - tag_tbl_size;
1025 jbd_block_set(journal->jbd_fs, &desc_block);
1030 if (record_len == 8) {
1032 (uint64_t *)blocks_entry;
1033 *blocks = to_be64(rec->lba);
1036 (uint32_t *)blocks_entry;
1037 *blocks = to_be32(rec->lba);
1039 blocks_entry += record_len;
1040 tag_tbl_size -= record_len;
1044 if (rc == EOK && desc_iblock) {
1046 header->count = journal->block_size - tag_tbl_size;
1048 jbd_block_set(journal->jbd_fs, &desc_block);
1055 jbd_journal_submit_trans(struct jbd_journal *journal,
1056 struct jbd_trans *trans)
1058 TAILQ_INSERT_TAIL(&journal->trans_queue,
1064 * XXX: one should disable cache writeback first.
1067 jbd_journal_commit_to_disk(struct jbd_journal *journal)
1070 uint32_t last = journal->last,
1071 trans_id = journal->trans_id,
1072 start = journal->start;
1073 struct jbd_trans *trans, *tmp;
1074 TAILQ_FOREACH_SAFE(trans, &journal->trans_queue,
1077 struct jbd_buf *jbd_buf;
1078 TAILQ_REMOVE(&journal->trans_queue, trans, trans_node);
1080 trans->trans_id = trans_id + 1;
1081 rc = jbd_journal_prepare(journal, trans);
1083 journal->last = last;
1084 jbd_journal_free_trans(journal, trans);
1087 rc = jbd_journal_prepare_revoke(journal, trans);
1089 journal->last = last;
1090 jbd_journal_free_trans(journal, trans);
1093 rc = jbd_trans_write_commit_block(trans);
1095 journal->last = last;
1096 jbd_journal_free_trans(journal, trans);
1099 LIST_FOREACH(jbd_buf, &trans->buf_list, buf_node) {
1100 struct ext4_block *block = &jbd_buf->block;
1101 block->buf->end_write = jbd_trans_end_write;
1102 block->buf->end_write_arg = trans;
1103 ext4_block_set(journal->jbd_fs->inode_ref.fs->bdev,
1106 if (trans->error != EOK) {
1107 journal->last = last;
1108 jbd_journal_free_trans(journal, trans);
1114 last = journal->last;
1115 jbd_journal_free_trans(journal, trans);
1118 journal->start = start;
1119 journal->trans_id = trans_id;
1120 jbd_journal_write_sb(journal);