From 8968289b5178bacd5454fcbe4b1214afc28c7b26 Mon Sep 17 00:00:00 2001 From: Harshad Shirwadkar Date: Wed, 20 Jan 2021 13:26:39 -0800 Subject: [PATCH] debugfs: add fast commit support to logdump Add fast commit support for debugfs logdump. This commit also adds fast_commit.h that contains the necessary helpers needed for fast commit replay. Note that this file is also byte by byte identical with kernel's fast_commit.h. Signed-off-by: Harshad Shirwadkar Signed-off-by: Theodore Ts'o --- debugfs/logdump.c | 122 ++++++++++++++++++++++++++-- lib/ext2fs/fast_commit.h | 203 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 lib/ext2fs/fast_commit.h diff --git a/debugfs/logdump.c b/debugfs/logdump.c index 1688995..151be3e 100644 --- a/debugfs/logdump.c +++ b/debugfs/logdump.c @@ -33,6 +33,7 @@ extern char *optarg; #include "debugfs.h" #include "blkid/blkid.h" #include "jfs_user.h" +#include "ext2fs/fast_commit.h" #include enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; @@ -65,6 +66,9 @@ static void dump_metadata_block(FILE *, struct journal_source *, unsigned int, unsigned int, unsigned int, int, tid_t); +static void dump_fc_block(FILE *out_file, char *buf, int blocksize, + int transaction, int *fc_done, int dump_old); + static void do_hexdump (FILE *, char *, int); #define WRAP(jsb, blocknr) \ @@ -353,6 +357,7 @@ static void dump_journal(char *cmdname, FILE *out_file, journal_header_t *header; tid_t transaction; unsigned int blocknr = 0; + int fc_done; /* First, check to see if there's an ext2 superblock header */ retval = read_journal_block(cmdname, source, 0, buf, 2048); @@ -410,7 +415,7 @@ static void dump_journal(char *cmdname, FILE *out_file, if (!blocknr) { /* Empty journal, nothing to do. */ if (!dump_old) - return; + goto fc; else blocknr = 1; } @@ -420,7 +425,7 @@ static void dump_journal(char *cmdname, FILE *out_file, ((ext2_loff_t) blocknr) * blocksize, buf, blocksize); if (retval) - return; + break; header = (journal_header_t *) buf; @@ -431,7 +436,7 @@ static void dump_journal(char *cmdname, FILE *out_file, if (magic != JBD2_MAGIC_NUMBER) { fprintf (out_file, "No magic number at block %u: " "end of journal.\n", blocknr); - return; + break; } if (sequence != transaction) { @@ -439,7 +444,7 @@ static void dump_journal(char *cmdname, FILE *out_file, "block %u: end of journal.\n", sequence, transaction, blocknr); if (!dump_old) - return; + break; } if (dump_descriptors) { @@ -473,9 +478,25 @@ static void dump_journal(char *cmdname, FILE *out_file, default: fprintf (out_file, "Unexpected block type %u at " "block %u.\n", blocktype, blocknr); - return; + break; } } + +fc: + blocknr = be32_to_cpu(jsb->s_maxlen) - jbd2_journal_get_num_fc_blks(jsb) + 1; + while (blocknr <= be32_to_cpu(jsb->s_maxlen)) { + retval = read_journal_block(cmdname, source, + ((ext2_loff_t) blocknr) * blocksize, + buf, blocksize); + if (retval) + return; + + dump_fc_block(out_file, buf, blocksize, transaction, &fc_done, + dump_old); + if (!dump_old && fc_done) + break; + blocknr++; + } } static inline size_t journal_super_tag_bytes(journal_superblock_t *jsb) @@ -496,6 +517,97 @@ static inline size_t journal_super_tag_bytes(journal_superblock_t *jsb) return sz - sizeof(__u32); } +static void dump_fc_block(FILE *out_file, char *buf, int blocksize, + int transaction, int *fc_done, int dump_old) +{ + struct ext4_fc_tl *tl; + struct ext4_fc_head *head; + struct ext4_fc_add_range *add_range; + struct ext4_fc_del_range *del_range; + struct ext4_fc_dentry_info *dentry_info; + struct ext4_fc_tail *tail; + struct ext3_extent *ex; + + *fc_done = 0; + fc_for_each_tl(buf, buf + blocksize, tl) { + switch (le16_to_cpu(tl->fc_tag)) { + case EXT4_FC_TAG_ADD_RANGE: + add_range = + (struct ext4_fc_add_range *)ext4_fc_tag_val(tl); + ex = (struct ext3_extent *)add_range->fc_ex; + fprintf(out_file, + "tag %s, inode %d, lblk %d, pblk %ld, len %d\n", + tag2str(tl->fc_tag), + le32_to_cpu(add_range->fc_ino), + le32_to_cpu(ex->ee_block), + le32_to_cpu(ex->ee_start) + + (((__u64) le16_to_cpu(ex->ee_start_hi)) << 32), + le16_to_cpu(ex->ee_len) > EXT_INIT_MAX_LEN ? + le16_to_cpu(ex->ee_len) - EXT_INIT_MAX_LEN : + le16_to_cpu(ex->ee_len)); + break; + case EXT4_FC_TAG_DEL_RANGE: + del_range = + (struct ext4_fc_del_range *)ext4_fc_tag_val(tl); + fprintf(out_file, "tag %s, inode %d, lblk %d, len %d\n", + tag2str(tl->fc_tag), + le32_to_cpu(del_range->fc_ino), + le32_to_cpu(del_range->fc_lblk), + le32_to_cpu(del_range->fc_len)); + break; + case EXT4_FC_TAG_LINK: + case EXT4_FC_TAG_UNLINK: + case EXT4_FC_TAG_CREAT: + dentry_info = + (struct ext4_fc_dentry_info *) + ext4_fc_tag_val(tl); + fprintf(out_file, + "tag %s, parent %d, ino %d, name \"%s\"\n", + tag2str(tl->fc_tag), + le32_to_cpu(dentry_info->fc_parent_ino), + le32_to_cpu(dentry_info->fc_ino), + dentry_info->fc_dname); + break; + case EXT4_FC_TAG_INODE: + fprintf(out_file, "tag %s, inode %d\n", + tag2str(tl->fc_tag), + le32_to_cpu(((struct ext4_fc_inode *) + ext4_fc_tag_val(tl))->fc_ino)); + break; + case EXT4_FC_TAG_PAD: + fprintf(out_file, "tag %s\n", tag2str(tl->fc_tag)); + break; + case EXT4_FC_TAG_TAIL: + tail = (struct ext4_fc_tail *)ext4_fc_tag_val(tl); + fprintf(out_file, "tag %s, tid %d\n", + tag2str(tl->fc_tag), + le32_to_cpu(tail->fc_tid)); + if (!dump_old && + le32_to_cpu(tail->fc_tid) < transaction) { + *fc_done = 1; + return; + } + break; + case EXT4_FC_TAG_HEAD: + fprintf(out_file, "\n*** Fast Commit Area ***\n"); + head = (struct ext4_fc_head *)ext4_fc_tag_val(tl); + fprintf(out_file, "tag %s, features 0x%x, tid %d\n", + tag2str(tl->fc_tag), + le32_to_cpu(head->fc_features), + le32_to_cpu(head->fc_tid)); + if (!dump_old && + le32_to_cpu(head->fc_tid) < transaction) { + *fc_done = 1; + return; + } + break; + default: + *fc_done = 1; + break; + } + } +} + static void dump_descriptor_block(FILE *out_file, struct journal_source *source, char *buf, diff --git a/lib/ext2fs/fast_commit.h b/lib/ext2fs/fast_commit.h new file mode 100644 index 0000000..b83e181 --- /dev/null +++ b/lib/ext2fs/fast_commit.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __FAST_COMMIT_H__ +#define __FAST_COMMIT_H__ + +#include "jfs_compat.h" + +/* + * Note this file is present in e2fsprogs/lib/ext2fs/fast_commit.h and + * linux/fs/ext4/fast_commit.h. These file should always be byte identical. + */ + +/* Fast commit tags */ +#define EXT4_FC_TAG_ADD_RANGE 0x0001 +#define EXT4_FC_TAG_DEL_RANGE 0x0002 +#define EXT4_FC_TAG_CREAT 0x0003 +#define EXT4_FC_TAG_LINK 0x0004 +#define EXT4_FC_TAG_UNLINK 0x0005 +#define EXT4_FC_TAG_INODE 0x0006 +#define EXT4_FC_TAG_PAD 0x0007 +#define EXT4_FC_TAG_TAIL 0x0008 +#define EXT4_FC_TAG_HEAD 0x0009 + +#define EXT4_FC_SUPPORTED_FEATURES 0x0 + +/* On disk fast commit tlv value structures */ + +/* Fast commit on disk tag length structure */ +struct ext4_fc_tl { + __le16 fc_tag; + __le16 fc_len; +}; + +/* Value structure for tag EXT4_FC_TAG_HEAD. */ +struct ext4_fc_head { + __le32 fc_features; + __le32 fc_tid; +}; + +/* Value structure for EXT4_FC_TAG_ADD_RANGE. */ +struct ext4_fc_add_range { + __le32 fc_ino; + __u8 fc_ex[12]; +}; + +/* Value structure for tag EXT4_FC_TAG_DEL_RANGE. */ +struct ext4_fc_del_range { + __le32 fc_ino; + __le32 fc_lblk; + __le32 fc_len; +}; + +/* + * This is the value structure for tags EXT4_FC_TAG_CREAT, EXT4_FC_TAG_LINK + * and EXT4_FC_TAG_UNLINK. + */ +struct ext4_fc_dentry_info { + __le32 fc_parent_ino; + __le32 fc_ino; + __u8 fc_dname[0]; +}; + +/* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */ +struct ext4_fc_inode { + __le32 fc_ino; + __u8 fc_raw_inode[0]; +}; + +/* Value structure for tag EXT4_FC_TAG_TAIL. */ +struct ext4_fc_tail { + __le32 fc_tid; + __le32 fc_crc; +}; + +/* + * Fast commit reason codes + */ +enum { + /* + * Commit status codes: + */ + EXT4_FC_REASON_OK = 0, + EXT4_FC_REASON_INELIGIBLE, + EXT4_FC_REASON_ALREADY_COMMITTED, + EXT4_FC_REASON_FC_START_FAILED, + EXT4_FC_REASON_FC_FAILED, + + /* + * Fast commit ineligiblity reasons: + */ + EXT4_FC_REASON_XATTR = 0, + EXT4_FC_REASON_CROSS_RENAME, + EXT4_FC_REASON_JOURNAL_FLAG_CHANGE, + EXT4_FC_REASON_NOMEM, + EXT4_FC_REASON_SWAP_BOOT, + EXT4_FC_REASON_RESIZE, + EXT4_FC_REASON_RENAME_DIR, + EXT4_FC_REASON_FALLOC_RANGE, + EXT4_FC_REASON_INODE_JOURNAL_DATA, + EXT4_FC_COMMIT_FAILED, + EXT4_FC_REASON_MAX +}; + +#ifdef __KERNEL__ +/* + * In memory list of dentry updates that are performed on the file + * system used by fast commit code. + */ +struct ext4_fc_dentry_update { + int fcd_op; /* Type of update create / unlink / link */ + int fcd_parent; /* Parent inode number */ + int fcd_ino; /* Inode number */ + struct qstr fcd_name; /* Dirent name */ + unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */ + struct list_head fcd_list; +}; + +struct ext4_fc_stats { + unsigned int fc_ineligible_reason_count[EXT4_FC_REASON_MAX]; + unsigned long fc_num_commits; + unsigned long fc_ineligible_commits; + unsigned long fc_numblks; +}; + +#define EXT4_FC_REPLAY_REALLOC_INCREMENT 4 + +/* + * Physical block regions added to different inodes due to fast commit + * recovery. These are set during the SCAN phase. During the replay phase, + * our allocator excludes these from its allocation. This ensures that + * we don't accidentally allocating a block that is going to be used by + * another inode. + */ +struct ext4_fc_alloc_region { + ext4_lblk_t lblk; + ext4_fsblk_t pblk; + int ino, len; +}; + +/* + * Fast commit replay state. + */ +struct ext4_fc_replay_state { + int fc_replay_num_tags; + int fc_replay_expected_off; + int fc_current_pass; + int fc_cur_tag; + int fc_crc; + struct ext4_fc_alloc_region *fc_regions; + int fc_regions_size, fc_regions_used, fc_regions_valid; + int *fc_modified_inodes; + int fc_modified_inodes_used, fc_modified_inodes_size; +}; + +#define region_last(__region) (((__region)->lblk) + ((__region)->len) - 1) +#endif + +#define fc_for_each_tl(__start, __end, __tl) \ + for (tl = (struct ext4_fc_tl *)(__start); \ + (__u8 *)tl < (__u8 *)(__end); \ + tl = (struct ext4_fc_tl *)((__u8 *)tl + \ + sizeof(struct ext4_fc_tl) + \ + + le16_to_cpu(tl->fc_len))) + +static inline const char *tag2str(__u16 tag) +{ + switch (tag) { + case EXT4_FC_TAG_LINK: + return "ADD_ENTRY"; + case EXT4_FC_TAG_UNLINK: + return "DEL_ENTRY"; + case EXT4_FC_TAG_ADD_RANGE: + return "ADD_RANGE"; + case EXT4_FC_TAG_CREAT: + return "CREAT_DENTRY"; + case EXT4_FC_TAG_DEL_RANGE: + return "DEL_RANGE"; + case EXT4_FC_TAG_INODE: + return "INODE"; + case EXT4_FC_TAG_PAD: + return "PAD"; + case EXT4_FC_TAG_TAIL: + return "TAIL"; + case EXT4_FC_TAG_HEAD: + return "HEAD"; + default: + return "ERROR"; + } +} + +/* Get length of a particular tlv */ +static inline int ext4_fc_tag_len(struct ext4_fc_tl *tl) +{ + return le16_to_cpu(tl->fc_len); +} + +/* Get a pointer to "value" of a tlv */ +static inline __u8 *ext4_fc_tag_val(struct ext4_fc_tl *tl) +{ + return (__u8 *)tl + sizeof(*tl); +} + +#endif /* __FAST_COMMIT_H__ */ -- 1.8.3.1