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_journal.h"
43 #include "ext4_errno.h"
44 #include "ext4_blockdev.h"
45 #include "ext4_crc32c.h"
46 #include "ext4_debug.h"
52 /**@brief Revoke entry during journal replay.*/
54 /**@brief Block number not to be replayed.*/
57 /**@brief For any transaction id smaller
58 * than trans_id, records of @block
59 * in those transactions should not
63 /**@brief Revoke tree node.*/
64 RB_ENTRY(revoke_entry) revoke_node;
67 /**@brief Valid journal replay information.*/
69 /**@brief Starting transaction id.*/
70 uint32_t start_trans_id;
72 /**@brief Ending transaction id.*/
73 uint32_t last_trans_id;
75 /**@brief Used as internal argument.*/
76 uint32_t this_trans_id;
78 /**@brief RB-Tree storing revoke entries.*/
79 RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
82 /**@brief Journal replay internal arguments.*/
84 /**@brief Journal replay information.*/
85 struct recover_info *info;
87 /**@brief Current block we are on.*/
90 /**@brief Current trans_id we are on.*/
91 uint32_t this_trans_id;
95 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
97 if (a->block > b->block)
99 else if (a->block < b->block)
105 jbd_block_rec_cmp(struct jbd_block_rec *a, struct jbd_block_rec *b)
109 else if (a->lba < b->lba)
114 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
115 jbd_revoke_entry_cmp, static inline)
116 RB_GENERATE_INTERNAL(jbd_block, jbd_block_rec, block_rec_node,
117 jbd_block_rec_cmp, static inline)
119 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
120 #define jbd_free_revoke_entry(addr) free(addr)
122 /**@brief Write jbd superblock to disk.
123 * @param jbd_fs jbd filesystem
124 * @param s jbd superblock
125 * @return standard error code*/
126 static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
129 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
132 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
136 offset = fblock * ext4_sb_get_block_size(&fs->sb);
137 return ext4_block_writebytes(fs->bdev, offset, s,
138 EXT4_SUPERBLOCK_SIZE);
141 /**@brief Read jbd superblock from disk.
142 * @param jbd_fs jbd filesystem
143 * @param s jbd superblock
144 * @return standard error code*/
145 static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
148 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
151 rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
155 offset = fblock * ext4_sb_get_block_size(&fs->sb);
156 return ext4_block_readbytes(fs->bdev, offset, s,
157 EXT4_SUPERBLOCK_SIZE);
160 /**@brief Verify jbd superblock.
161 * @param sb jbd superblock
162 * @return true if jbd superblock is valid */
163 static bool jbd_verify_sb(struct jbd_sb *sb)
165 struct jbd_bhdr *header = &sb->header;
166 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
169 if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
170 jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
176 /**@brief Write back dirty jbd superblock to disk.
177 * @param jbd_fs jbd filesystem
178 * @return standard error code*/
179 static int jbd_write_sb(struct jbd_fs *jbd_fs)
183 rc = jbd_sb_write(jbd_fs, &jbd_fs->sb);
187 jbd_fs->dirty = false;
192 /**@brief Get reference to jbd filesystem.
193 * @param fs Filesystem to load journal of
194 * @param jbd_fs jbd filesystem
195 * @return standard error code*/
196 int jbd_get_fs(struct ext4_fs *fs,
197 struct jbd_fs *jbd_fs)
200 uint32_t journal_ino;
202 memset(jbd_fs, 0, sizeof(struct jbd_fs));
203 /* See if there is journal inode on this filesystem.*/
204 /* FIXME: detection on existance ofbkejournal bdev is
206 journal_ino = ext4_get32(&fs->sb, journal_inode_number);
208 rc = ext4_fs_get_inode_ref(fs,
212 memset(jbd_fs, 0, sizeof(struct jbd_fs));
215 rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
217 memset(jbd_fs, 0, sizeof(struct jbd_fs));
218 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
221 if (!jbd_verify_sb(&jbd_fs->sb)) {
222 memset(jbd_fs, 0, sizeof(struct jbd_fs));
223 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
230 /**@brief Put reference of jbd filesystem.
231 * @param jbd_fs jbd filesystem
232 * @return standard error code*/
233 int jbd_put_fs(struct jbd_fs *jbd_fs)
236 rc = jbd_write_sb(jbd_fs);
238 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
242 /**@brief Data block lookup helper.
243 * @param jbd_fs jbd filesystem
244 * @param iblock block index
245 * @param fblock logical block address
246 * @return standard error code*/
247 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
249 ext4_fsblk_t *fblock)
251 int rc = ext4_fs_get_inode_dblk_idx(
259 /**@brief jbd block get function (through cache).
260 * @param jbd_fs jbd filesystem
261 * @param block block descriptor
262 * @param fblock jbd logical block address
263 * @return standard error code*/
264 static int jbd_block_get(struct jbd_fs *jbd_fs,
265 struct ext4_block *block,
268 /* TODO: journal device. */
270 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
272 /* Lookup the logical block address of
274 rc = jbd_inode_bmap(jbd_fs, iblock,
279 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
280 rc = ext4_block_get(bdev, block, fblock);
282 /* If succeeded, mark buffer as BC_FLUSH to indicate
283 * that data should be written to disk immediately.*/
285 ext4_bcache_set_flag(block->buf, BC_FLUSH);
290 /**@brief jbd block get function (through cache, don't read).
291 * @param jbd_fs jbd filesystem
292 * @param block block descriptor
293 * @param fblock jbd logical block address
294 * @return standard error code*/
295 static int jbd_block_get_noread(struct jbd_fs *jbd_fs,
296 struct ext4_block *block,
299 /* TODO: journal device. */
301 ext4_lblk_t iblock = (ext4_lblk_t)fblock;
302 rc = jbd_inode_bmap(jbd_fs, iblock,
307 struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
308 rc = ext4_block_get_noread(bdev, block, fblock);
310 ext4_bcache_set_flag(block->buf, BC_FLUSH);
315 /**@brief jbd block set procedure (through cache).
316 * @param jbd_fs jbd filesystem
317 * @param block block descriptor
318 * @return standard error code*/
319 static int jbd_block_set(struct jbd_fs *jbd_fs,
320 struct ext4_block *block)
322 return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
326 /**@brief helper functions to calculate
327 * block tag size, not including UUID part.
328 * @param jbd_fs jbd filesystem
329 * @return tag size in bytes*/
330 static int jbd_tag_bytes(struct jbd_fs *jbd_fs)
334 /* It is very easy to deal with the case which
335 * JBD_FEATURE_INCOMPAT_CSUM_V3 is enabled.*/
336 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
337 JBD_FEATURE_INCOMPAT_CSUM_V3))
338 return sizeof(struct jbd_block_tag3);
340 size = sizeof(struct jbd_block_tag);
342 /* If JBD_FEATURE_INCOMPAT_CSUM_V2 is enabled,
343 * add 2 bytes to size.*/
344 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
345 JBD_FEATURE_INCOMPAT_CSUM_V2))
346 size += sizeof(uint16_t);
348 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
349 JBD_FEATURE_INCOMPAT_64BIT))
352 /* If block number is 4 bytes in size,
353 * minus 4 bytes from size */
354 return size - sizeof(uint32_t);
357 /**@brief Tag information. */
359 /**@brief Tag size in bytes, including UUID part.*/
362 /**@brief block number stored in this tag.*/
365 /**@brief whether UUID part exists or not.*/
368 /**@brief UUID content if UUID part exists.*/
369 uint8_t uuid[UUID_SIZE];
371 /**@brief Is this the last tag? */
375 /**@brief Extract information from a block tag.
376 * @param __tag pointer to the block tag
377 * @param tag_bytes block tag size of this jbd filesystem
378 * @param remaining size in buffer containing the block tag
379 * @param tag_info information of this tag.
380 * @return EOK when succeed, otherwise return EINVAL.*/
382 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
385 int32_t remain_buf_size,
386 struct tag_info *tag_info)
389 tag_info->tag_bytes = tag_bytes;
390 tag_info->uuid_exist = false;
391 tag_info->last_tag = false;
393 /* See whether it is possible to hold a valid block tag.*/
394 if (remain_buf_size - tag_bytes < 0)
397 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
398 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
399 struct jbd_block_tag3 *tag = __tag;
400 tag_info->block = jbd_get32(tag, blocknr);
401 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
402 JBD_FEATURE_INCOMPAT_64BIT))
404 (uint64_t)jbd_get32(tag, blocknr_high) << 32;
406 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
409 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
410 /* See whether it is possible to hold UUID part.*/
411 if (remain_buf_size - tag_bytes < UUID_SIZE)
414 uuid_start = (char *)tag + tag_bytes;
415 tag_info->uuid_exist = true;
416 tag_info->tag_bytes += UUID_SIZE;
417 memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
420 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
421 tag_info->last_tag = true;
424 struct jbd_block_tag *tag = __tag;
425 tag_info->block = jbd_get32(tag, blocknr);
426 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
427 JBD_FEATURE_INCOMPAT_64BIT))
429 (uint64_t)jbd_get32(tag, blocknr_high) << 32;
431 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
434 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
435 /* See whether it is possible to hold UUID part.*/
436 if (remain_buf_size - tag_bytes < UUID_SIZE)
439 uuid_start = (char *)tag + tag_bytes;
440 tag_info->uuid_exist = true;
441 tag_info->tag_bytes += UUID_SIZE;
442 memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
445 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
446 tag_info->last_tag = true;
452 /**@brief Write information to a block tag.
453 * @param __tag pointer to the block tag
454 * @param remaining size in buffer containing the block tag
455 * @param tag_info information of this tag.
456 * @return EOK when succeed, otherwise return EINVAL.*/
458 jbd_write_block_tag(struct jbd_fs *jbd_fs,
460 int32_t remain_buf_size,
461 struct tag_info *tag_info)
464 int tag_bytes = jbd_tag_bytes(jbd_fs);
466 tag_info->tag_bytes = tag_bytes;
468 /* See whether it is possible to hold a valid block tag.*/
469 if (remain_buf_size - tag_bytes < 0)
472 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
473 JBD_FEATURE_INCOMPAT_CSUM_V3)) {
474 struct jbd_block_tag3 *tag = __tag;
475 memset(tag, 0, sizeof(struct jbd_block_tag3));
476 jbd_set32(tag, blocknr, tag_info->block);
477 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
478 JBD_FEATURE_INCOMPAT_64BIT))
479 jbd_set32(tag, blocknr_high, tag_info->block >> 32);
481 if (tag_info->uuid_exist) {
482 /* See whether it is possible to hold UUID part.*/
483 if (remain_buf_size - tag_bytes < UUID_SIZE)
486 uuid_start = (char *)tag + tag_bytes;
487 tag_info->tag_bytes += UUID_SIZE;
488 memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
490 jbd_set32(tag, flags,
491 jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
493 if (tag_info->last_tag)
494 jbd_set32(tag, flags,
495 jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
498 struct jbd_block_tag *tag = __tag;
499 memset(tag, 0, sizeof(struct jbd_block_tag));
500 jbd_set32(tag, blocknr, tag_info->block);
501 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
502 JBD_FEATURE_INCOMPAT_64BIT))
503 jbd_set32(tag, blocknr_high, tag_info->block >> 32);
505 if (tag_info->uuid_exist) {
506 /* See whether it is possible to hold UUID part.*/
507 if (remain_buf_size - tag_bytes < UUID_SIZE)
510 uuid_start = (char *)tag + tag_bytes;
511 tag_info->tag_bytes += UUID_SIZE;
512 memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
514 jbd_set16(tag, flags,
515 jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
517 if (tag_info->last_tag)
518 jbd_set16(tag, flags,
519 jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
525 /**@brief Iterate all block tags in a block.
526 * @param jbd_fs jbd filesystem
527 * @param __tag_start pointer to the block
528 * @param tag_tbl_size size of the block
529 * @param func callback routine to indicate that
530 * a block tag is found
531 * @param arg additional argument to be passed to func */
533 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
535 int32_t tag_tbl_size,
536 void (*func)(struct jbd_fs * jbd_fs,
542 char *tag_start, *tag_ptr;
543 int tag_bytes = jbd_tag_bytes(jbd_fs);
544 tag_start = __tag_start;
547 /* Cut off the size of block tail storing checksum. */
548 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
549 JBD_FEATURE_INCOMPAT_CSUM_V2) ||
550 JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
551 JBD_FEATURE_INCOMPAT_CSUM_V3))
552 tag_tbl_size -= sizeof(struct jbd_block_tail);
554 while (tag_tbl_size) {
555 struct tag_info tag_info;
556 int rc = jbd_extract_block_tag(jbd_fs,
565 func(jbd_fs, tag_info.block, tag_info.uuid, arg);
567 /* Stop the iteration when we reach the last tag. */
568 if (tag_info.last_tag)
571 tag_ptr += tag_info.tag_bytes;
572 tag_tbl_size -= tag_info.tag_bytes;
576 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
581 uint32_t *iblock = arg;
582 ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
589 static struct revoke_entry *
590 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
592 struct revoke_entry tmp = {
596 return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
599 /**@brief Replay a block in a transaction.
600 * @param jbd_fs jbd filesystem
601 * @param block block address to be replayed.*/
602 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
604 uint8_t *uuid __unused,
608 struct replay_arg *arg = __arg;
609 struct recover_info *info = arg->info;
610 uint32_t *this_block = arg->this_block;
611 struct revoke_entry *revoke_entry;
612 struct ext4_block journal_block, ext4_block;
613 struct ext4_fs *fs = jbd_fs->inode_ref.fs;
617 /* We replay this block only if the current transaction id
618 * is equal or greater than that in revoke entry.*/
619 revoke_entry = jbd_revoke_entry_lookup(info, block);
621 arg->this_trans_id < revoke_entry->trans_id)
625 "Replaying block in block_tag: %" PRIu64 "\n",
628 r = jbd_block_get(jbd_fs, &journal_block, *this_block);
632 /* We need special treatment for ext4 superblock. */
634 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
636 jbd_block_set(jbd_fs, &journal_block);
640 memcpy(ext4_block.data,
642 jbd_get32(&jbd_fs->sb, blocksize));
644 ext4_bcache_set_dirty(ext4_block.buf);
645 ext4_block_set(fs->bdev, &ext4_block);
647 uint16_t mount_count, state;
648 mount_count = ext4_get16(&fs->sb, mount_count);
649 state = ext4_get16(&fs->sb, state);
652 journal_block.data + EXT4_SUPERBLOCK_OFFSET,
653 EXT4_SUPERBLOCK_SIZE);
655 /* Mark system as mounted */
656 ext4_set16(&fs->sb, state, state);
657 r = ext4_sb_write(fs->bdev, &fs->sb);
661 /*Update mount count*/
662 ext4_set16(&fs->sb, mount_count, mount_count);
665 jbd_block_set(jbd_fs, &journal_block);
670 /**@brief Add block address to revoke tree, along with
671 * its transaction id.
672 * @param info journal replay info
673 * @param block block address to be replayed.*/
674 static void jbd_add_revoke_block_tags(struct recover_info *info,
677 struct revoke_entry *revoke_entry;
679 ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
680 /* If the revoke entry with respect to the block address
681 * exists already, update its transaction id.*/
682 revoke_entry = jbd_revoke_entry_lookup(info, block);
684 revoke_entry->trans_id = info->this_trans_id;
688 revoke_entry = jbd_alloc_revoke_entry();
689 ext4_assert(revoke_entry);
690 revoke_entry->block = block;
691 revoke_entry->trans_id = info->this_trans_id;
692 RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
697 static void jbd_destroy_revoke_tree(struct recover_info *info)
699 while (!RB_EMPTY(&info->revoke_root)) {
700 struct revoke_entry *revoke_entry =
701 RB_MIN(jbd_revoke, &info->revoke_root);
702 ext4_assert(revoke_entry);
703 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
704 jbd_free_revoke_entry(revoke_entry);
708 /* Make sure we wrap around the log correctly! */
709 #define wrap(sb, var) \
711 if (var >= jbd_get32((sb), maxlen)) \
712 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first)); \
715 #define ACTION_SCAN 0
716 #define ACTION_REVOKE 1
717 #define ACTION_RECOVER 2
719 /**@brief Add entries in a revoke block to revoke tree.
720 * @param jbd_fs jbd filesystem
721 * @param header revoke block header
722 * @param recover_info journal replay info*/
723 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
724 struct jbd_bhdr *header,
725 struct recover_info *info)
728 struct jbd_revoke_header *revoke_hdr =
729 (struct jbd_revoke_header *)header;
730 uint32_t i, nr_entries, record_len = 4;
732 /* If we are working on a 64bit jbd filesystem, */
733 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
734 JBD_FEATURE_INCOMPAT_64BIT))
737 nr_entries = (jbd_get32(revoke_hdr, count) -
738 sizeof(struct jbd_revoke_header)) /
741 blocks_entry = (char *)(revoke_hdr + 1);
743 for (i = 0;i < nr_entries;i++) {
744 if (record_len == 8) {
746 (uint64_t *)blocks_entry;
747 jbd_add_revoke_block_tags(info, to_be64(*blocks));
750 (uint32_t *)blocks_entry;
751 jbd_add_revoke_block_tags(info, to_be32(*blocks));
753 blocks_entry += record_len;
757 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
758 struct jbd_bhdr *header,
761 jbd_iterate_block_table(jbd_fs,
763 jbd_get32(&jbd_fs->sb, blocksize) -
764 sizeof(struct jbd_bhdr),
765 jbd_display_block_tags,
769 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
770 struct jbd_bhdr *header,
771 struct replay_arg *arg)
773 jbd_iterate_block_table(jbd_fs,
775 jbd_get32(&jbd_fs->sb, blocksize) -
776 sizeof(struct jbd_bhdr),
777 jbd_replay_block_tags,
781 /**@brief The core routine of journal replay.
782 * @param jbd_fs jbd filesystem
783 * @param recover_info journal replay info
784 * @param action action needed to be taken
785 * @return standard error code*/
786 static int jbd_iterate_log(struct jbd_fs *jbd_fs,
787 struct recover_info *info,
791 bool log_end = false;
792 struct jbd_sb *sb = &jbd_fs->sb;
793 uint32_t start_trans_id, this_trans_id;
794 uint32_t start_block, this_block;
796 /* We start iterating valid blocks in the whole journal.*/
797 start_trans_id = this_trans_id = jbd_get32(sb, sequence);
798 start_block = this_block = jbd_get32(sb, start);
800 ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
804 struct ext4_block block;
805 struct jbd_bhdr *header;
806 /* If we are not scanning for the last
807 * valid transaction in the journal,
808 * we will stop when we reach the end of
810 if (action != ACTION_SCAN)
811 if (this_trans_id > info->last_trans_id) {
816 r = jbd_block_get(jbd_fs, &block, this_block);
820 header = (struct jbd_bhdr *)block.data;
821 /* This block does not have a valid magic number,
822 * so we have reached the end of the journal.*/
823 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
824 jbd_block_set(jbd_fs, &block);
829 /* If the transaction id we found is not expected,
830 * we may have reached the end of the journal.
832 * If we are not scanning the journal, something
833 * bad might have taken place. :-( */
834 if (jbd_get32(header, sequence) != this_trans_id) {
835 if (action != ACTION_SCAN)
838 jbd_block_set(jbd_fs, &block);
843 switch (jbd_get32(header, blocktype)) {
844 case JBD_DESCRIPTOR_BLOCK:
845 ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
846 "trans_id: %" PRIu32"\n",
847 this_block, this_trans_id);
848 if (action == ACTION_RECOVER) {
849 struct replay_arg replay_arg;
850 replay_arg.info = info;
851 replay_arg.this_block = &this_block;
852 replay_arg.this_trans_id = this_trans_id;
854 jbd_replay_descriptor_block(jbd_fs,
855 header, &replay_arg);
857 jbd_debug_descriptor_block(jbd_fs,
858 header, &this_block);
861 case JBD_COMMIT_BLOCK:
862 ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
863 "trans_id: %" PRIu32"\n",
864 this_block, this_trans_id);
865 /* This is the end of a transaction,
866 * we may now proceed to the next transaction.
870 case JBD_REVOKE_BLOCK:
871 ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
872 "trans_id: %" PRIu32"\n",
873 this_block, this_trans_id);
874 if (action == ACTION_REVOKE) {
875 info->this_trans_id = this_trans_id;
876 jbd_build_revoke_tree(jbd_fs,
884 jbd_block_set(jbd_fs, &block);
886 wrap(sb, this_block);
887 if (this_block == start_block)
891 ext4_dbg(DEBUG_JBD, "End of journal.\n");
892 if (r == EOK && action == ACTION_SCAN) {
893 /* We have finished scanning the journal. */
894 info->start_trans_id = start_trans_id;
895 if (this_trans_id > start_trans_id)
896 info->last_trans_id = this_trans_id - 1;
898 info->last_trans_id = this_trans_id;
904 /**@brief Replay journal.
905 * @param jbd_fs jbd filesystem
906 * @return standard error code*/
907 int jbd_recover(struct jbd_fs *jbd_fs)
910 struct recover_info info;
911 struct jbd_sb *sb = &jbd_fs->sb;
915 RB_INIT(&info.revoke_root);
917 r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
921 r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
925 r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
927 /* If we successfully replay the journal,
928 * clear EXT4_FINCOM_RECOVER flag on the
929 * ext4 superblock, and set the start of
931 uint32_t features_incompatible =
932 ext4_get32(&jbd_fs->inode_ref.fs->sb,
933 features_incompatible);
934 jbd_set32(&jbd_fs->sb, start, 0);
935 features_incompatible &= ~EXT4_FINCOM_RECOVER;
936 ext4_set32(&jbd_fs->inode_ref.fs->sb,
937 features_incompatible,
938 features_incompatible);
939 jbd_fs->dirty = true;
940 r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
941 &jbd_fs->inode_ref.fs->sb);
943 jbd_destroy_revoke_tree(&info);
947 static void jbd_journal_write_sb(struct jbd_journal *journal)
949 struct jbd_fs *jbd_fs = journal->jbd_fs;
950 jbd_set32(&jbd_fs->sb, start, journal->start);
951 jbd_set32(&jbd_fs->sb, sequence, journal->trans_id);
952 jbd_fs->dirty = true;
955 /**@brief Start accessing the journal.
956 * @param jbd_fs jbd filesystem
957 * @param journal current journal session
958 * @return standard error code*/
959 int jbd_journal_start(struct jbd_fs *jbd_fs,
960 struct jbd_journal *journal)
963 uint32_t features_incompatible =
964 ext4_get32(&jbd_fs->inode_ref.fs->sb,
965 features_incompatible);
966 features_incompatible |= EXT4_FINCOM_RECOVER;
967 ext4_set32(&jbd_fs->inode_ref.fs->sb,
968 features_incompatible,
969 features_incompatible);
970 r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
971 &jbd_fs->inode_ref.fs->sb);
975 journal->first = jbd_get32(&jbd_fs->sb, first);
976 journal->start = journal->first;
977 journal->last = journal->first;
978 journal->trans_id = 1;
979 journal->alloc_trans_id = 1;
981 journal->block_size = jbd_get32(&jbd_fs->sb, blocksize);
983 TAILQ_INIT(&journal->trans_queue);
984 TAILQ_INIT(&journal->cp_queue);
985 RB_INIT(&journal->block_rec_root);
986 journal->jbd_fs = jbd_fs;
987 jbd_journal_write_sb(journal);
988 return jbd_write_sb(jbd_fs);
991 static void jbd_journal_flush_trans(struct jbd_trans *trans)
993 struct jbd_buf *jbd_buf, *tmp;
994 struct jbd_journal *journal = trans->journal;
995 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
996 LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
998 struct ext4_block block = jbd_buf->block;
999 ext4_block_flush_buf(fs->bdev, block.buf);
1004 jbd_journal_skip_pure_revoke(struct jbd_journal *journal,
1005 struct jbd_trans *trans)
1007 journal->start = trans->start_iblock +
1008 trans->alloc_blocks;
1009 wrap(&journal->jbd_fs->sb, journal->start);
1010 journal->trans_id = trans->trans_id + 1;
1011 jbd_journal_free_trans(journal,
1013 jbd_journal_write_sb(journal);
1016 static void jbd_journal_flush_all_trans(struct jbd_journal *journal)
1018 struct jbd_trans *trans;
1019 while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
1020 if (!trans->data_cnt) {
1021 TAILQ_REMOVE(&journal->cp_queue,
1024 jbd_journal_skip_pure_revoke(journal, trans);
1026 jbd_journal_flush_trans(trans);
1031 /**@brief Stop accessing the journal.
1032 * @param journal current journal session
1033 * @return standard error code*/
1034 int jbd_journal_stop(struct jbd_journal *journal)
1037 struct jbd_fs *jbd_fs = journal->jbd_fs;
1038 uint32_t features_incompatible;
1040 /* Commit all the transactions to the journal.*/
1041 jbd_journal_commit_all(journal);
1043 /* Make sure that journalled content have reached
1045 jbd_journal_flush_all_trans(journal);
1047 /* There should be no block record in this journal
1049 if (!RB_EMPTY(&journal->block_rec_root))
1051 DBG_WARN "There are still block records "
1052 "in this journal session!\n");
1054 features_incompatible =
1055 ext4_get32(&jbd_fs->inode_ref.fs->sb,
1056 features_incompatible);
1057 features_incompatible &= ~EXT4_FINCOM_RECOVER;
1058 ext4_set32(&jbd_fs->inode_ref.fs->sb,
1059 features_incompatible,
1060 features_incompatible);
1061 r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
1062 &jbd_fs->inode_ref.fs->sb);
1067 journal->trans_id = 0;
1068 jbd_journal_write_sb(journal);
1069 return jbd_write_sb(journal->jbd_fs);
1072 /**@brief Allocate a block in the journal.
1073 * @param journal current journal session
1074 * @param trans transaction
1075 * @return allocated block address*/
1076 static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
1077 struct jbd_trans *trans)
1079 uint32_t start_block;
1081 start_block = journal->last++;
1082 trans->alloc_blocks++;
1083 wrap(&journal->jbd_fs->sb, journal->last);
1085 /* If there is no space left, flush all journalled
1086 * blocks to disk first.*/
1087 if (journal->last == journal->start)
1088 jbd_journal_flush_all_trans(journal);
1093 /**@brief Allocate a new transaction
1094 * @param journal current journal session
1095 * @return transaction allocated*/
1097 jbd_journal_new_trans(struct jbd_journal *journal)
1099 struct jbd_trans *trans = calloc(1, sizeof(struct jbd_trans));
1103 /* We will assign a trans_id to this transaction,
1104 * once it has been committed.*/
1105 trans->journal = journal;
1110 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
1111 struct ext4_buf *buf __unused,
1115 /**@brief gain access to it before making any modications.
1116 * @param journal current journal session
1117 * @param trans transaction
1118 * @param block descriptor
1119 * @return standard error code.*/
1120 int jbd_trans_get_access(struct jbd_journal *journal,
1121 struct jbd_trans *trans,
1122 struct ext4_block *block)
1125 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1127 /* If the buffer has already been modified, we should
1128 * flush dirty data in this buffer to disk.*/
1129 if (ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
1130 block->buf->end_write == jbd_trans_end_write &&
1131 block->buf->end_write_arg != trans) {
1132 r = ext4_block_flush_buf(fs->bdev, block->buf);
1137 static struct jbd_block_rec *
1138 jbd_trans_block_rec_lookup(struct jbd_journal *journal,
1141 struct jbd_block_rec tmp = {
1145 return RB_FIND(jbd_block,
1146 &journal->block_rec_root,
1150 static inline struct jbd_block_rec *
1151 jbd_trans_insert_block_rec(struct jbd_trans *trans,
1153 struct ext4_buf *buf)
1155 struct jbd_block_rec *block_rec;
1156 block_rec = jbd_trans_block_rec_lookup(trans->journal, lba);
1158 /* Data should be flushed to disk already. */
1159 ext4_assert(!block_rec->buf);
1160 /* Now this block record belongs to this transaction. */
1161 block_rec->trans = trans;
1164 block_rec = calloc(1, sizeof(struct jbd_block_rec));
1168 block_rec->lba = lba;
1169 block_rec->buf = buf;
1170 block_rec->trans = trans;
1171 RB_INSERT(jbd_block, &trans->journal->block_rec_root, block_rec);
1176 jbd_trans_remove_block_rec(struct jbd_journal *journal,
1177 struct jbd_buf *jbd_buf)
1179 struct jbd_block_rec *block_rec = jbd_buf->block_rec;
1180 /* If this block record doesn't belong to this transaction,
1182 if (block_rec->trans == jbd_buf->trans) {
1183 RB_REMOVE(jbd_block,
1184 &journal->block_rec_root,
1190 /**@brief Add block to a transaction and mark it dirty.
1191 * @param trans transaction
1192 * @param block block descriptor
1193 * @return standard error code*/
1194 int jbd_trans_set_block_dirty(struct jbd_trans *trans,
1195 struct ext4_block *block)
1197 struct jbd_buf *buf;
1199 if (!ext4_bcache_test_flag(block->buf, BC_DIRTY) &&
1200 block->buf->end_write != jbd_trans_end_write) {
1201 struct jbd_block_rec *block_rec;
1202 buf = calloc(1, sizeof(struct jbd_buf));
1206 if ((block_rec = jbd_trans_insert_block_rec(trans,
1208 block->buf)) == NULL) {
1213 buf->block_rec = block_rec;
1215 buf->block = *block;
1216 ext4_bcache_inc_ref(block->buf);
1218 /* If the content reach the disk, notify us
1219 * so that we may do a checkpoint. */
1220 block->buf->end_write = jbd_trans_end_write;
1221 block->buf->end_write_arg = buf;
1224 LIST_INSERT_HEAD(&trans->buf_list, buf, buf_node);
1226 ext4_bcache_set_dirty(block->buf);
1231 /**@brief Add block to be revoked to a transaction
1232 * @param trans transaction
1233 * @param lba logical block address
1234 * @return standard error code*/
1235 int jbd_trans_revoke_block(struct jbd_trans *trans,
1238 struct jbd_revoke_rec *rec =
1239 calloc(1, sizeof(struct jbd_revoke_rec));
1244 LIST_INSERT_HEAD(&trans->revoke_list, rec, revoke_node);
1248 /**@brief Try to add block to be revoked to a transaction.
1249 * If @lba still remains in an transaction on checkpoint
1250 * queue, add @lba as a revoked block to the transaction.
1251 * @param trans transaction
1252 * @param lba logical block address
1253 * @return standard error code*/
1254 int jbd_trans_try_revoke_block(struct jbd_trans *trans,
1258 struct jbd_journal *journal = trans->journal;
1259 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1260 struct jbd_block_rec *block_rec =
1261 jbd_trans_block_rec_lookup(journal, lba);
1263 /* Make sure we don't flush any buffers belong to this transaction. */
1264 if (block_rec && block_rec->trans != trans) {
1265 /* If the buffer has not been flushed yet, flush it now. */
1266 if (block_rec->buf) {
1267 r = ext4_block_flush_buf(fs->bdev, block_rec->buf);
1273 jbd_trans_revoke_block(trans, lba);
1279 /**@brief Free a transaction
1280 * @param journal current journal session
1281 * @param trans transaction
1282 * @param abort discard all the modifications on the block?
1283 * @return standard error code*/
1284 void jbd_journal_free_trans(struct jbd_journal *journal,
1285 struct jbd_trans *trans,
1288 struct jbd_buf *jbd_buf, *tmp;
1289 struct jbd_revoke_rec *rec, *tmp2;
1290 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1291 LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
1294 jbd_buf->block.buf->end_write = NULL;
1295 jbd_buf->block.buf->end_write_arg = NULL;
1296 ext4_bcache_clear_dirty(jbd_buf->block.buf);
1297 ext4_block_set(fs->bdev, &jbd_buf->block);
1300 jbd_trans_remove_block_rec(journal, jbd_buf);
1301 LIST_REMOVE(jbd_buf, buf_node);
1304 LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
1306 LIST_REMOVE(rec, revoke_node);
1313 /**@brief Write commit block for a transaction
1314 * @param trans transaction
1315 * @return standard error code*/
1316 static int jbd_trans_write_commit_block(struct jbd_trans *trans)
1319 struct jbd_commit_header *header;
1320 uint32_t commit_iblock = 0;
1321 struct ext4_block commit_block;
1322 struct jbd_journal *journal = trans->journal;
1324 commit_iblock = jbd_journal_alloc_block(journal, trans);
1325 rc = jbd_block_get_noread(journal->jbd_fs,
1326 &commit_block, commit_iblock);
1330 header = (struct jbd_commit_header *)commit_block.data;
1331 jbd_set32(&header->header, magic, JBD_MAGIC_NUMBER);
1332 jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK);
1333 jbd_set32(&header->header, sequence, trans->trans_id);
1335 ext4_bcache_set_dirty(commit_block.buf);
1336 rc = jbd_block_set(journal->jbd_fs, &commit_block);
1343 /**@brief Write descriptor block for a transaction
1344 * @param journal current journal session
1345 * @param trans transaction
1346 * @return standard error code*/
1347 static int jbd_journal_prepare(struct jbd_journal *journal,
1348 struct jbd_trans *trans)
1350 int rc = EOK, i = 0;
1351 int32_t tag_tbl_size;
1352 uint32_t desc_iblock = 0;
1353 uint32_t data_iblock = 0;
1354 char *tag_start = NULL, *tag_ptr = NULL;
1355 struct jbd_buf *jbd_buf, *tmp;
1356 struct ext4_block desc_block, data_block;
1357 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1359 LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node, tmp) {
1360 struct tag_info tag_info;
1361 bool uuid_exist = false;
1362 if (!ext4_bcache_test_flag(jbd_buf->block.buf,
1364 /* The buffer has not been modified, just release
1366 jbd_buf->block.buf->end_write = NULL;
1367 jbd_buf->block.buf->end_write_arg = NULL;
1368 ext4_block_set(fs->bdev, &jbd_buf->block);
1369 LIST_REMOVE(jbd_buf, buf_node);
1375 struct jbd_bhdr *bhdr;
1376 desc_iblock = jbd_journal_alloc_block(journal, trans);
1377 rc = jbd_block_get_noread(journal->jbd_fs,
1378 &desc_block, desc_iblock);
1382 ext4_bcache_set_dirty(desc_block.buf);
1384 bhdr = (struct jbd_bhdr *)desc_block.data;
1385 jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
1386 jbd_set32(bhdr, blocktype, JBD_DESCRIPTOR_BLOCK);
1387 jbd_set32(bhdr, sequence, trans->trans_id);
1389 tag_start = (char *)(bhdr + 1);
1390 tag_ptr = tag_start;
1392 tag_tbl_size = journal->block_size -
1393 sizeof(struct jbd_bhdr);
1395 if (!trans->start_iblock)
1396 trans->start_iblock = desc_iblock;
1399 tag_info.block = jbd_buf->block.lb_id;
1400 tag_info.uuid_exist = uuid_exist;
1401 if (i == trans->data_cnt - 1)
1402 tag_info.last_tag = true;
1405 memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
1408 rc = jbd_write_block_tag(journal->jbd_fs,
1413 jbd_block_set(journal->jbd_fs, &desc_block);
1418 data_iblock = jbd_journal_alloc_block(journal, trans);
1419 rc = jbd_block_get_noread(journal->jbd_fs,
1420 &data_block, data_iblock);
1424 ext4_bcache_set_dirty(data_block.buf);
1426 memcpy(data_block.data, jbd_buf->block.data,
1427 journal->block_size);
1429 rc = jbd_block_set(journal->jbd_fs, &data_block);
1433 tag_ptr += tag_info.tag_bytes;
1434 tag_tbl_size -= tag_info.tag_bytes;
1438 if (rc == EOK && desc_iblock)
1439 jbd_block_set(journal->jbd_fs, &desc_block);
1444 /**@brief Write revoke block for a transaction
1445 * @param journal current journal session
1446 * @param trans transaction
1447 * @return standard error code*/
1449 jbd_journal_prepare_revoke(struct jbd_journal *journal,
1450 struct jbd_trans *trans)
1452 int rc = EOK, i = 0;
1453 int32_t tag_tbl_size;
1454 uint32_t desc_iblock = 0;
1455 char *blocks_entry = NULL;
1456 struct jbd_revoke_rec *rec, *tmp;
1457 struct ext4_block desc_block;
1458 struct jbd_revoke_header *header = NULL;
1459 int32_t record_len = 4;
1461 if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
1462 JBD_FEATURE_INCOMPAT_64BIT))
1465 LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
1469 struct jbd_bhdr *bhdr;
1470 desc_iblock = jbd_journal_alloc_block(journal, trans);
1471 rc = jbd_block_get_noread(journal->jbd_fs,
1472 &desc_block, desc_iblock);
1477 ext4_bcache_set_dirty(desc_block.buf);
1479 bhdr = (struct jbd_bhdr *)desc_block.data;
1480 jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
1481 jbd_set32(bhdr, blocktype, JBD_REVOKE_BLOCK);
1482 jbd_set32(bhdr, sequence, trans->trans_id);
1484 header = (struct jbd_revoke_header *)bhdr;
1485 blocks_entry = (char *)(header + 1);
1486 tag_tbl_size = journal->block_size -
1487 sizeof(struct jbd_revoke_header);
1489 if (!trans->start_iblock)
1490 trans->start_iblock = desc_iblock;
1494 if (tag_tbl_size < record_len) {
1495 jbd_set32(header, count,
1496 journal->block_size - tag_tbl_size);
1497 jbd_block_set(journal->jbd_fs, &desc_block);
1502 if (record_len == 8) {
1504 (uint64_t *)blocks_entry;
1505 *blocks = to_be64(rec->lba);
1508 (uint32_t *)blocks_entry;
1509 *blocks = to_be32(rec->lba);
1511 blocks_entry += record_len;
1512 tag_tbl_size -= record_len;
1516 if (rc == EOK && desc_iblock) {
1518 jbd_set32(header, count,
1519 journal->block_size - tag_tbl_size);
1521 jbd_block_set(journal->jbd_fs, &desc_block);
1527 /**@brief Submit the transaction to transaction queue.
1528 * @param journal current journal session
1529 * @param trans transaction*/
1531 jbd_journal_submit_trans(struct jbd_journal *journal,
1532 struct jbd_trans *trans)
1534 TAILQ_INSERT_TAIL(&journal->trans_queue,
1539 /**@brief Put references of block descriptors in a transaction.
1540 * @param journal current journal session
1541 * @param trans transaction*/
1542 void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
1544 struct jbd_buf *jbd_buf, *tmp;
1545 struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1546 LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
1548 struct ext4_block block = jbd_buf->block;
1549 ext4_block_set(fs->bdev, &block);
1553 /**@brief Update the start block of the journal when
1554 * all the contents in a transaction reach the disk.*/
1555 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
1556 struct ext4_buf *buf,
1560 struct jbd_buf *jbd_buf = arg;
1561 struct jbd_trans *trans = jbd_buf->trans;
1562 struct jbd_journal *journal = trans->journal;
1563 bool first_in_queue =
1564 trans == TAILQ_FIRST(&journal->cp_queue);
1568 LIST_REMOVE(jbd_buf, buf_node);
1569 jbd_buf->block_rec->buf = NULL;
1570 jbd_trans_remove_block_rec(journal, jbd_buf);
1573 /* Clear the end_write and end_write_arg fields. */
1574 buf->end_write = NULL;
1575 buf->end_write_arg = NULL;
1577 trans->written_cnt++;
1578 if (trans->written_cnt == trans->data_cnt) {
1579 TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
1581 if (first_in_queue) {
1582 journal->start = trans->start_iblock +
1583 trans->alloc_blocks;
1584 wrap(&journal->jbd_fs->sb, journal->start);
1585 journal->trans_id = trans->trans_id + 1;
1587 jbd_journal_free_trans(journal, trans, false);
1589 if (first_in_queue) {
1590 while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
1591 if (!trans->data_cnt) {
1592 TAILQ_REMOVE(&journal->cp_queue,
1595 jbd_journal_skip_pure_revoke(journal,
1598 journal->start = trans->start_iblock;
1599 wrap(&journal->jbd_fs->sb, journal->start);
1600 journal->trans_id = trans->trans_id;
1604 jbd_journal_write_sb(journal);
1605 jbd_write_sb(journal->jbd_fs);
1610 /**@brief Commit a transaction to the journal immediately.
1611 * @param journal current journal session
1612 * @param trans transaction
1613 * @return standard error code*/
1614 int jbd_journal_commit_trans(struct jbd_journal *journal,
1615 struct jbd_trans *trans)
1618 uint32_t last = journal->last;
1620 trans->trans_id = journal->alloc_trans_id;
1621 rc = jbd_journal_prepare(journal, trans);
1625 rc = jbd_journal_prepare_revoke(journal, trans);
1629 if (LIST_EMPTY(&trans->buf_list) &&
1630 LIST_EMPTY(&trans->revoke_list)) {
1631 /* Since there are no entries in both buffer list
1632 * and revoke entry list, we do not consider trans as
1633 * complete transaction and just return EOK.*/
1634 jbd_journal_free_trans(journal, trans, false);
1638 rc = jbd_trans_write_commit_block(trans);
1642 journal->alloc_trans_id++;
1643 if (TAILQ_EMPTY(&journal->cp_queue)) {
1644 if (trans->data_cnt) {
1645 journal->start = trans->start_iblock;
1646 wrap(&journal->jbd_fs->sb, journal->start);
1647 journal->trans_id = trans->trans_id;
1648 jbd_journal_write_sb(journal);
1649 jbd_write_sb(journal->jbd_fs);
1650 TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
1652 jbd_journal_cp_trans(journal, trans);
1654 journal->start = trans->start_iblock +
1655 trans->alloc_blocks;
1656 wrap(&journal->jbd_fs->sb, journal->start);
1657 journal->trans_id = trans->trans_id + 1;
1658 jbd_journal_write_sb(journal);
1659 jbd_journal_free_trans(journal, trans, false);
1662 TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
1664 if (trans->data_cnt)
1665 jbd_journal_cp_trans(journal, trans);
1670 journal->last = last;
1671 jbd_journal_free_trans(journal, trans, true);
1676 /**@brief Commit one transaction on transaction queue
1678 * @param journal current journal session.*/
1679 void jbd_journal_commit_one(struct jbd_journal *journal)
1681 struct jbd_trans *trans;
1683 if ((trans = TAILQ_FIRST(&journal->trans_queue))) {
1684 TAILQ_REMOVE(&journal->trans_queue, trans, trans_node);
1685 jbd_journal_commit_trans(journal, trans);
1689 /**@brief Commit all the transactions on transaction queue
1691 * @param journal current journal session.*/
1692 void jbd_journal_commit_all(struct jbd_journal *journal)
1694 while (!TAILQ_EMPTY(&journal->trans_queue)) {
1695 jbd_journal_commit_one(journal);