aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarshad Shirwadkar <harshadshirwadkar@gmail.com>2021-01-20 13:26:39 -0800
committerTheodore Ts'o <tytso@mit.edu>2021-01-21 11:46:29 -0500
commit8968289b5178bacd5454fcbe4b1214afc28c7b26 (patch)
tree46a685c72bcd3f0fd020ac7bb881c0f98d445471
parentef44eef00ef1f31447c4390b5dd9ff21fc868593 (diff)
downloade2fsprogs-8968289b5178bacd5454fcbe4b1214afc28c7b26.tar.gz
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 <harshadshirwadkar@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r--debugfs/logdump.c122
-rw-r--r--lib/ext2fs/fast_commit.h203
2 files changed, 320 insertions, 5 deletions
diff --git a/debugfs/logdump.c b/debugfs/logdump.c
index 168899542..151be3e21 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 <uuid/uuid.h>
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 000000000..b83e18103
--- /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__ */