diff mbox series

[f2fs-dev,RCF,v3,04/10] inject.f2fs: add cp injection

Message ID 20240704025740.527171-5-shengyong@oppo.com (mailing list archive)
State New
Headers show
Series f2fs-tools: introduce inject.f2fs | expand

Commit Message

Sheng Yong July 4, 2024, 2:57 a.m. UTC
This patch enables injecting checkpoint. To archive this, a helper
write_raw_cp_blocks() is added to write the first and last blocks
of a specific cp. And print_ckpt_info() is exported to show new
checkpoint info.

The meanings of options are:
 * cp: means cp is injected, its argument chooses which cp pack to be
       injected, where 0 means the current valid cp is choosen
       automatically.

The members could be injected in cp contains:
 * checkpoint_ver: checkpoint version
 * ckpt_flags: checkpoint flags
 * cur_node_segno: cur_node_segno array
 * cur_node_blkoff: cur_node_blkoff array
 * cur_data_segno: cur_data_segno array
 * cur_data_blkoff: cur_data_blkoff array

Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Sheng Yong <shengyong@oppo.com>
---
 fsck/fsck.h   |   3 ++
 fsck/inject.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fsck/inject.h |   1 +
 fsck/main.c   |   1 +
 fsck/mount.c  |  26 +++++++++++
 5 files changed, 158 insertions(+)
diff mbox series

Patch

diff --git a/fsck/fsck.h b/fsck/fsck.h
index 4ca75b3..02986ef 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -236,12 +236,15 @@  extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int, bool);
 extern void duplicate_checkpoint(struct f2fs_sb_info *);
 extern void write_checkpoint(struct f2fs_sb_info *);
 extern void write_checkpoints(struct f2fs_sb_info *);
+extern void write_raw_cp_blocks(struct f2fs_sb_info *sbi,
+			struct f2fs_checkpoint *cp, int which);
 extern void update_superblock(struct f2fs_super_block *, int);
 extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t,
 			struct f2fs_node *);
 extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
 
 extern void print_raw_sb_info(struct f2fs_super_block *);
+extern void print_ckpt_info(struct f2fs_sb_info *);
 extern bool is_checkpoint_stop(struct f2fs_super_block *, bool);
 extern bool is_inconsistent_error(struct f2fs_super_block *);
 extern pgoff_t current_nat_addr(struct f2fs_sb_info *, nid_t, int *);
diff --git a/fsck/inject.c b/fsck/inject.c
index f29e746..da4356d 100644
--- a/fsck/inject.c
+++ b/fsck/inject.c
@@ -23,6 +23,7 @@  void inject_usage(void)
 	MSG(0, "  --str <new string> new string to set\n");
 	MSG(0, "  --idx <slot index> which slot is injected in an array\n");
 	MSG(0, "  --sb <0|1|2> --mb <name> [--idx <index>] --val/str <value/string> inject superblock\n");
+	MSG(0, "  --cp <0|1|2> --mb <name> [--idx <index>] --val <value> inject checkpoint\n");
 	MSG(0, "  --dry-run do not really inject\n");
 
 	exit(1);
@@ -42,6 +43,22 @@  static void inject_sb_usage(void)
 	MSG(0, "  devs.path: inject path in devs array selected by --idx <index> specified by --str <string>\n");
 }
 
+static void inject_cp_usage(void)
+{
+	MSG(0, "inject.f2fs --cp <0|1|2> --mb <name> [--idx <index>] --val <value> inject checkpoint\n");
+	MSG(0, "[cp]:\n");
+	MSG(0, "  0: auto select the current cp pack\n");
+	MSG(0, "  1: select the first cp pack\n");
+	MSG(0, "  2: select the second cp pack\n");
+	MSG(0, "[mb]:\n");
+	MSG(0, "  checkpoint_ver: inject checkpoint_ver\n");
+	MSG(0, "  ckpt_flags: inject ckpt_flags\n");
+	MSG(0, "  cur_node_segno: inject cur_node_segno array selected by --idx <index>\n");
+	MSG(0, "  cur_node_blkoff: inject cur_node_blkoff array selected by --idx <index>\n");
+	MSG(0, "  cur_data_segno: inject cur_data_segno array selected by --idx <index>\n");
+	MSG(0, "  cur_data_blkoff: inject cur_data_blkoff array selected by --idx <index>\n");
+}
+
 int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 {
 	int o = 0;
@@ -55,6 +72,7 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 		{"val", required_argument, 0, 4},
 		{"str", required_argument, 0, 5},
 		{"sb", required_argument, 0, 6},
+		{"cp", required_argument, 0, 7},
 		{0, 0, 0, 0}
 	};
 
@@ -97,6 +115,14 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 				return -ERANGE;
 			MSG(0, "Info: inject sb %s\n", pack[opt->sb]);
 			break;
+		case 7:
+			if (!is_digits(optarg))
+				return EWRONG_OPT;
+			opt->cp = atoi(optarg);
+			if (opt->cp < 0 || opt->cp > 2)
+				return -ERANGE;
+			MSG(0, "Info: inject cp pack %s\n", pack[opt->cp]);
+			break;
 		case 'd':
 			if (optarg[0] == '-' || !is_digits(optarg))
 				return EWRONG_OPT;
@@ -111,6 +137,9 @@  int inject_parse_options(int argc, char *argv[], struct inject_option *opt)
 			if (opt->sb >= 0) {
 				inject_sb_usage();
 				exit(0);
+			} else if (opt->cp >= 0) {
+				inject_cp_usage();
+				exit(0);
 			}
 			return EUNKNOWN_OPT;
 		}
@@ -190,6 +219,102 @@  out:
 	return ret;
 }
 
+static int inject_cp(struct f2fs_sb_info *sbi, struct inject_option *opt)
+{
+	struct f2fs_checkpoint *cp, *cur_cp = F2FS_CKPT(sbi);
+	char *buf = NULL;
+	int ret = 0;
+
+	if (opt->cp == 0)
+		opt->cp = sbi->cur_cp;
+
+	if (opt->cp != sbi->cur_cp) {
+		struct f2fs_super_block *sb = sbi->raw_super;
+		block_t cp_addr;
+
+		buf = calloc(1, F2FS_BLKSIZE);
+		ASSERT(buf != NULL);
+
+		cp_addr = get_sb(cp_blkaddr);
+		if (opt->cp == 2)
+			cp_addr += 1 << get_sb(log_blocks_per_seg);
+		ret = dev_read_block(buf, cp_addr);
+		ASSERT(ret >= 0);
+
+		cp = (struct f2fs_checkpoint *)buf;
+		sbi->ckpt = cp;
+		sbi->cur_cp = opt->cp;
+	} else {
+		cp = cur_cp;
+	}
+
+	if (!strcmp(opt->mb, "checkpoint_ver")) {
+		MSG(0, "Info: inject checkpoint_ver of cp %d: 0x%llx -> 0x%lx\n",
+		    opt->cp, get_cp(checkpoint_ver), (u64)opt->val);
+		set_cp(checkpoint_ver, (u64)opt->val);
+	} else if (!strcmp(opt->mb, "ckpt_flags")) {
+		MSG(0, "Info: inject ckpt_flags of cp %d: 0x%x -> 0x%x\n",
+		    opt->cp, get_cp(ckpt_flags), (u32)opt->val);
+		set_cp(ckpt_flags, (u32)opt->val);
+	} else if (!strcmp(opt->mb, "cur_node_segno")) {
+		if (opt->idx >= MAX_ACTIVE_NODE_LOGS) {
+			ERR_MSG("invalid index %u of cp->cur_node_segno[]\n",
+				opt->idx);
+			ret = -EINVAL;
+			goto out;
+		}
+		MSG(0, "Info: inject cur_node_segno[%d] of cp %d: 0x%x -> 0x%x\n",
+		    opt->idx, opt->cp, get_cp(cur_node_segno[opt->idx]),
+		    (u32)opt->val);
+		set_cp(cur_node_segno[opt->idx], (u32)opt->val);
+	} else if (!strcmp(opt->mb, "cur_node_blkoff")) {
+		if (opt->idx >= MAX_ACTIVE_NODE_LOGS) {
+			ERR_MSG("invalid index %u of cp->cur_node_blkoff[]\n",
+				opt->idx);
+			ret = -EINVAL;
+			goto out;
+		}
+		MSG(0, "Info: inject cur_node_blkoff[%d] of cp %d: 0x%x -> 0x%x\n",
+		    opt->idx, opt->cp, get_cp(cur_node_blkoff[opt->idx]),
+		    (u16)opt->val);
+		set_cp(cur_node_blkoff[opt->idx], (u16)opt->val);
+	} else if (!strcmp(opt->mb, "cur_data_segno")) {
+		if (opt->idx >= MAX_ACTIVE_DATA_LOGS) {
+			ERR_MSG("invalid index %u of cp->cur_data_segno[]\n",
+				opt->idx);
+			ret = -EINVAL;
+			goto out;
+		}
+		MSG(0, "Info: inject cur_data_segno[%d] of cp %d: 0x%x -> 0x%x\n",
+		    opt->idx, opt->cp, get_cp(cur_data_segno[opt->idx]),
+		    (u32)opt->val);
+		set_cp(cur_data_segno[opt->idx], (u32)opt->val);
+	} else if (!strcmp(opt->mb, "cur_data_blkoff")) {
+		if (opt->idx >= MAX_ACTIVE_DATA_LOGS) {
+			ERR_MSG("invalid index %u of cp->cur_data_blkoff[]\n",
+				opt->idx);
+			ret = -EINVAL;
+			goto out;
+		}
+		MSG(0, "Info: inject cur_data_blkoff[%d] of cp %d: 0x%x -> 0x%x\n",
+		    opt->idx, opt->cp, get_cp(cur_data_blkoff[opt->idx]),
+		    (u16)opt->val);
+		set_cp(cur_data_blkoff[opt->idx], (u16)opt->val);
+	} else {
+		ERR_MSG("unknown or unsupported member \"%s\"\n", opt->mb);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	print_ckpt_info(sbi);
+	write_raw_cp_blocks(sbi, cp, opt->cp);
+
+out:
+	free(buf);
+	sbi->ckpt = cur_cp;
+	return ret;
+}
+
 int do_inject(struct f2fs_sb_info *sbi)
 {
 	struct inject_option *opt = (struct inject_option *)c.private;
@@ -197,6 +322,8 @@  int do_inject(struct f2fs_sb_info *sbi)
 
 	if (opt->sb >= 0)
 		ret = inject_sb(sbi, opt);
+	else if (opt->cp >= 0)
+		ret = inject_cp(sbi, opt);
 
 	return ret;
 }
diff --git a/fsck/inject.h b/fsck/inject.h
index c7d0e33..907309f 100644
--- a/fsck/inject.h
+++ b/fsck/inject.h
@@ -25,6 +25,7 @@  struct inject_option {
 	long long val;		/* new value */
 	char *str;		/* new string */
 	int sb;			/* which sb */
+	int cp;			/* which cp */
 };
 
 void inject_usage(void);
diff --git a/fsck/main.c b/fsck/main.c
index 40edf35..da58f0c 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -824,6 +824,7 @@  void f2fs_parse_options(int argc, char *argv[])
 #ifdef WITH_INJECT
 		static struct inject_option inject_opt = {
 			.sb = -1,
+			.cp = -1,
 			.idx = -1,
 		};
 
diff --git a/fsck/mount.c b/fsck/mount.c
index 77e3919..77b6144 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -3426,6 +3426,32 @@  void write_checkpoints(struct f2fs_sb_info *sbi)
 	write_checkpoint(sbi);
 }
 
+void write_raw_cp_blocks(struct f2fs_sb_info *sbi,
+			 struct f2fs_checkpoint *cp, int which)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	uint32_t crc;
+	block_t cp_blkaddr;
+	int ret;
+
+	crc = f2fs_checkpoint_chksum(cp);
+	*((__le32 *)((unsigned char *)cp + get_cp(checksum_offset))) =
+							cpu_to_le32(crc);
+
+	cp_blkaddr = get_sb(cp_blkaddr);
+	if (which == 2)
+		cp_blkaddr += 1 << get_sb(log_blocks_per_seg);
+
+	/* write the first cp block in this CP pack */
+	ret = dev_write_block(cp, cp_blkaddr);
+	ASSERT(ret >= 0);
+
+	/* write the second cp block in this CP pack */
+	cp_blkaddr += get_cp(cp_pack_total_block_count) - 1;
+	ret = dev_write_block(cp, cp_blkaddr);
+	ASSERT(ret >= 0);
+}
+
 void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
 {
 	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);