diff mbox

[ndctl,2/2] ndctl: add an option to check-namespace to rewrite the log

Message ID 20180113000315.29082-2-vishal.l.verma@intel.com (mailing list archive)
State Accepted
Commit 28da1f5ef4a8
Headers show

Commit Message

Verma, Vishal L Jan. 13, 2018, 12:03 a.m. UTC
Add a --rewrite-log option to ndctl check-namespace which reads the
active log entries, and rewrites them as though initializing a new BTT.
This allows us to convert an old (pre 4.15) format of log/padding layout
to a new one that is compatible with other BTT implementations.

In the btt-pad-compat unit test, add testing for the format conversion
operation.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 Documentation/ndctl/ndctl-check-namespace.txt | 11 +++++
 ndctl/check.c                                 | 70 ++++++++++++++++++++++++++-
 ndctl/namespace.c                             |  6 ++-
 test/btt-pad-compat.sh                        |  9 ++++
 4 files changed, 93 insertions(+), 3 deletions(-)
diff mbox

Patch

diff --git a/Documentation/ndctl/ndctl-check-namespace.txt b/Documentation/ndctl/ndctl-check-namespace.txt
index 49353b1..ea4183a 100644
--- a/Documentation/ndctl/ndctl-check-namespace.txt
+++ b/Documentation/ndctl/ndctl-check-namespace.txt
@@ -42,6 +42,17 @@  OPTIONS
 	Perform metadata repairs if possible. Without this option,
 	the raw namespace contents will not be touched.
 
+-L::
+--rewrite-log::
+	Regenerate the BTT log and write it to media. This can be used to
+	convert from the old (pre 4.15) padding format that was incompatible
+	with other BTT implementations to the updated format. This requires
+	the --repair option to be provided.
+
+	WARNING: Do not interrupt this operation as it can potentially cause
+	unrecoverable metadata corruption. It is highly recommended to create
+	a backup of the raw namespace before attempting this.
+
 -f::
 --force::
 	Unless this option is specified, a check-namespace operation
diff --git a/ndctl/check.c b/ndctl/check.c
index d3aa1aa..09dd125 100644
--- a/ndctl/check.c
+++ b/ndctl/check.c
@@ -46,6 +46,7 @@  struct check_opts {
 	bool verbose;
 	bool force;
 	bool repair;
+	bool logfix;
 };
 
 struct btt_chk {
@@ -246,6 +247,12 @@  static void btt_log_group_read(struct arena_info *a, u32 lane,
 	memcpy(log, &a->map.log[lane], LOG_GRP_SIZE);
 }
 
+static void btt_log_group_write(struct arena_info *a, u32 lane,
+			struct log_group *log)
+{
+	memcpy(&a->map.log[lane], log, LOG_GRP_SIZE);
+}
+
 static u32 log_seq(struct log_group *log, int log_idx)
 {
 	return le32_to_cpu(log->ent[log_idx].seq);
@@ -358,6 +365,7 @@  enum btt_errcodes {
 	BTT_LOG_MAP_ERR,
 	BTT_MAP_OOB,
 	BTT_BITMAP_ERROR,
+	BTT_LOGFIX_ERR,
 };
 
 static void btt_xlat_status(struct arena_info *a, int errcode)
@@ -405,6 +413,11 @@  static void btt_xlat_status(struct arena_info *a, int errcode)
 			"arena %d: bitmap error: internal blocks are incorrectly referenced\n",
 			a->num);
 		break;
+	case BTT_LOGFIX_ERR:
+		err(a->bttc,
+			"arena %d: rewrite-log error: log may be in an unknown/unrecoverable state\n",
+			a->num);
+		break;
 	default:
 		err(a->bttc, "arena %d: unknown error: %d\n",
 			a->num, errcode);
@@ -563,6 +576,44 @@  static int btt_check_bitmap(struct arena_info *a)
 	return rc;
 }
 
+static int btt_rewrite_log(struct arena_info *a)
+{
+	struct log_group log;
+	int rc;
+	u32 i;
+
+	info(a->bttc, "arena %d: rewriting log\n", a->num);
+	/*
+	 * To rewrite the log, we implicitly use the 'new' padding scheme of
+	 * (0, 1) but resetting the log to a completely initial state (i.e.
+	 * slot-0 contains a made-up entry containing the 'free' block from
+	 * the existing current log entry, and a sequence number of '1'. All
+	 * other slots are zeroed.
+	 *
+	 * This way of rewriting the log is the most flexible as it can be
+	 * (ab)used to convert a new padding format back to the old one.
+	 * Since it only recreates slot-0, which is common between both
+	 * existing formats, an older kernel will simply initialize the free
+	 * list using those slot-0 entries, and run with it as though slot-2
+	 * is the other valid slot.
+	 */
+	memset(&log, 0, LOG_GRP_SIZE);
+	for (i = 0; i < a->nfree; i++) {
+		struct log_entry ent;
+
+		rc = btt_log_read(a, i, &ent);
+		if (rc)
+			return BTT_LOGFIX_ERR;
+
+		log.ent[0].lba = ent.lba;
+		log.ent[0].old_map = ent.old_map;
+		log.ent[0].new_map = ent.new_map;
+		log.ent[0].seq = 1;
+		btt_log_group_write(a, i, &log);
+	}
+	return 0;
+}
+
 static int btt_check_arenas(struct btt_chk *bttc)
 {
 	struct arena_info *a = NULL;
@@ -591,6 +642,12 @@  static int btt_check_arenas(struct btt_chk *bttc)
 		rc = btt_check_bitmap(a);
 		if (rc)
 			break;
+
+		if (bttc->opts->logfix) {
+			rc = btt_rewrite_log(a);
+			if (rc)
+				break;
+		}
 	}
 
 	if (a && rc != BTT_OK) {
@@ -1094,13 +1151,14 @@  static int btt_recover_first_sb(struct btt_chk *bttc)
 }
 
 int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
-		bool repair)
+		bool repair, bool logfix)
 {
 	const char *devname = ndctl_namespace_get_devname(ndns);
 	struct check_opts __opts = {
 		.verbose = verbose,
 		.force = force,
 		.repair = repair,
+		.logfix = logfix,
 	}, *opts = &__opts;
 	int raw_mode, rc, disabled_flag = 0, open_flags;
 	struct btt_sb *btt_sb;
@@ -1127,6 +1185,16 @@  int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
 		goto out_bttc;
 	}
 
+	if (opts->logfix) {
+		if (!opts->repair) {
+			err(bttc, "--rewrite-log also requires --repair\n");
+			rc = -EINVAL;
+			goto out_bttc;
+		}
+		info(bttc,
+			"WARNING: interruption may cause unrecoverable metadata corruption\n");
+	}
+
 	bttc->opts = opts;
 	bttc->sys_page_size = sysconf(_SC_PAGESIZE);
 	bttc->rawsize = ndctl_namespace_get_size(ndns);
diff --git a/ndctl/namespace.c b/ndctl/namespace.c
index d31244b..6b19f28 100644
--- a/ndctl/namespace.c
+++ b/ndctl/namespace.c
@@ -39,6 +39,7 @@ 
 static bool verbose;
 static bool force;
 static bool repair;
+static bool logfix;
 static struct parameters {
 	bool do_scan;
 	bool mode_default;
@@ -121,6 +122,7 @@  OPT_BOOLEAN('L', "autolabel", &param.autolabel, "automatically initialize labels
 
 #define CHECK_OPTIONS() \
 OPT_BOOLEAN('R', "repair", &repair, "perform metadata repairs"), \
+OPT_BOOLEAN('L', "rewrite-log", &logfix, "regenerate the log"), \
 OPT_BOOLEAN('f', "force", &force, "check namespace even if currently active")
 
 static const struct option base_options[] = {
@@ -985,7 +987,7 @@  static int namespace_reconfig(struct ndctl_region *region,
 }
 
 int namespace_check(struct ndctl_namespace *ndns, bool verbose, bool force,
-		bool repair);
+		bool repair, bool logfix);
 
 static int do_xaction_namespace(const char *namespace,
 		enum device_action action, struct ndctl_ctx *ctx)
@@ -1049,7 +1051,7 @@  static int do_xaction_namespace(const char *namespace,
 					break;
 				case ACTION_CHECK:
 					rc = namespace_check(ndns, verbose,
-							force, repair);
+							force, repair, logfix);
 					if (rc < 0)
 						return rc;
 					break;
diff --git a/test/btt-pad-compat.sh b/test/btt-pad-compat.sh
index cbfe990..d10efe3 100755
--- a/test/btt-pad-compat.sh
+++ b/test/btt-pad-compat.sh
@@ -193,6 +193,15 @@  do_tests()
 	cycle_ns "$dev"
 	verify_idx 0 2
 
+	# rewrite log using ndctl, verify conversion to new format
+	ndctl check-namespace --rewrite-log --repair --force --verbose $dev
+	do_random_io "/dev/$blockdev"
+	cycle_ns "$dev"
+	verify_idx 0 1
+
+	# check-namespace again to make sure everything is ok
+	ndctl check-namespace --force --verbose $dev
+
 	# the old format btt metadata was created with a null parent uuid,
 	# making it 'stickier' than a normally created btt. Be sure to clean
 	# it up by wiping the info block