[PATCH] mtd-utils: integck: add support for volume specific power-cut test

Mats Kärrman Mats.Karrman at tritech.se
Sun Sep 1 11:25:37 EDT 2013


Problem:
The current power-cut test requires power-cut emulation to be enabled globally
(i.e. for all UBIFS volumes) since the power-cut mode is otherwise lost when
the file-system is re-mounted but this may lead to some practical problems when
the root fs of the tested machine is also of the same type.

Proposed solution:
Add a new option -P that extends the existing option -p with the ability to
automatically re-enable power-cut emulation after every re-mount by writing
"1" to the associated control file. The file to use is deduced from the mounted
device and the assumption that debugfs is mounted on /sys/kernel/debug.
The old option -p is kept for backwards compatibility and because the result
may be different if the power-cut mode is active also during re-mount.

Note:
The following patch includes a perhaps unrelated change; a number of
'usleep(MOUNT_DELAY)' has been inserted to avoid error 16 (Device or resource
busy) when remounting. I have not seen any problems with this on my target
system with real flash but when running tests on my PC with mtdram the tests
keep failing within a minute or so without these delays.

Signed-off-by: Mats Karrman <mats.karrman at tritech.se>


diff --git a/tests/fs-tests/integrity/integck.c b/tests/fs-tests/integrity/integck.c
index 8badd1f..7a65b03 100644
--- a/tests/fs-tests/integrity/integck.c
+++ b/tests/fs-tests/integrity/integck.c
@@ -61,6 +61,12 @@
 /* Maximum buffer size for a single read/write operation */
 #define IO_BUFFER_SIZE 32768
 
+/* To avoid trouble with error 16 (Device or resource busy) */
+#define MOUNT_DELAY 10000
+
+/* Kernel debug fs mount point */
+#define DEBUGFS_MOUNT_POINT "/sys/kernel/debug"
+
 /*
  * Check if a condition is true and die if not.
  */
@@ -94,7 +100,7 @@
 /* The variables below are set by command line arguments */
 static struct {
 	long repeat_cnt;
-	int power_cut_mode;
+	int power_cut_mode;	/* 0=off, 1=on, 2=auto enable */
 	int verify_ops;
 	int reattach;
 	int mtdn;
@@ -123,6 +129,8 @@ static struct {
  *              options as stored as a set of flags)
  * mount_point: tested file-system mount point path
  * test_dir: the directory on the tested file-system where we test
+ * pc_reenable_path: path to the debugfs control knob file used to re-enable
+ *                   power-cut mode for the tested file-system
  */
 static struct {
 	int max_name_len;
@@ -137,6 +145,7 @@ static struct {
 	unsigned long mount_flags;
 	char *mount_point;
 	char *test_dir;
+	char *pc_reenable_path;
 } fsinfo = {
 	.nospc_size_ok = 1,
 	.can_mmap = 1,
@@ -2580,6 +2589,26 @@ static int rm_minus_rf_dir(const char *dir_name)
 }
 
 /**
+ * Re-enable the power cut emulation mode on the tested file system
+ */
+static int reenable_power_cut(void)
+{
+	int fd;
+
+	fd = open(fsinfo.pc_reenable_path, O_WRONLY);
+	if (fd == -1) {
+		errmsg("failed to re-enable power-cut: %d", errno);
+		return -1;
+	}
+
+	v("re-enabling power-cut for %s", fsinfo.fsdev);
+	CHECK(write(fd, "1\n", 2) == 2);
+	CHECK(close(fd) == 0);
+
+	return 0;
+}
+
+/**
  * Re-mount the test file-system. This function randomly select how to
  * re-mount.
  */
@@ -2604,6 +2633,7 @@ static int remount_tested_fs(void)
 
 	if (rorw1) {
 		flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT;
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -2614,6 +2644,7 @@ static int remount_tested_fs(void)
 
 		flags = fsinfo.mount_flags | MS_REMOUNT;
 		flags &= ~((unsigned long)MS_RDONLY);
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -2626,6 +2657,7 @@ static int remount_tested_fs(void)
 	if (um) {
 		if (um_ro) {
 			flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT;
+			usleep(MOUNT_DELAY);
 			ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 				    fsinfo.fstype, flags, fsinfo.mount_opts);
 			if (ret) {
@@ -2635,6 +2667,7 @@ static int remount_tested_fs(void)
 			}
 		}
 
+		usleep(MOUNT_DELAY);
 		ret = umount(fsinfo.mount_point);
 		if (ret) {
 			pcv("cannot unmount %s", fsinfo.mount_point);
@@ -2642,6 +2675,7 @@ static int remount_tested_fs(void)
 		}
 
 		if (!um_rorw) {
+			usleep(MOUNT_DELAY);
 			ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 				    fsinfo.fstype, fsinfo.mount_flags,
 				    fsinfo.mount_opts);
@@ -2651,6 +2685,7 @@ static int remount_tested_fs(void)
 				return -1;
 			}
 		} else {
+			usleep(MOUNT_DELAY);
 			ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 				    fsinfo.fstype, fsinfo.mount_flags | MS_RDONLY,
 				    fsinfo.mount_opts);
@@ -2662,6 +2697,7 @@ static int remount_tested_fs(void)
 
 			flags = fsinfo.mount_flags | MS_REMOUNT;
 			flags &= ~((unsigned long)MS_RDONLY);
+			usleep(MOUNT_DELAY);
 			ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 				    fsinfo.fstype, flags, fsinfo.mount_opts);
 			if (ret) {
@@ -2674,6 +2710,7 @@ static int remount_tested_fs(void)
 
 	if (rorw2) {
 		flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT;
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -2683,6 +2720,7 @@ static int remount_tested_fs(void)
 
 		flags = fsinfo.mount_flags | MS_REMOUNT;
 		flags &= ~((unsigned long)MS_RDONLY);
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -2693,6 +2731,13 @@ static int remount_tested_fs(void)
 	}
 
 	CHECK(chdir(fsinfo.mount_point) == 0);
+
+	if (args.power_cut_mode == 2) {
+		ret = reenable_power_cut();
+		if (ret)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -2850,6 +2895,115 @@ static int integck(void)
 }
 
 /*
+ * This is a helper function for 'make_ubifs_power_cut_reenable_path()' that
+ * uses libubi to find out the ubifs volume number from a volume name.
+ */
+static int get_ubi_vol_num_from_name(int devnum, const char * name)
+{
+	libubi_t libubi;
+	struct ubi_vol_info vol_info;
+	int err, vol_num;
+
+	libubi = libubi_open();
+	if (!libubi) {
+		if (errno == 0)
+			errmsg("UBI is not present in the system");
+		else
+			sys_errmsg("cannot open libubi");
+		return -1;
+	}
+
+	err = ubi_get_vol_info1_nm(libubi, devnum, name, &vol_info);
+	if (err) {
+		sys_errmsg("cannot find UBI volume \"%s\"", name);
+		vol_num = -1;
+	} else {
+		vol_num = vol_info.vol_id;
+	}
+
+	libubi_close(libubi);
+	return vol_num;
+}
+
+/*
+ * Make a path for an ubifs power-cut enable knob file according to the
+ * device and volume number of the tested file-system.
+ */
+static char * make_ubifs_power_cut_reenable_path(void)
+{
+	int ubidev, ubivol;
+	char *p;
+	char path_tail[32];
+
+	/*
+	 * Try to find the ubifs device and volume number using the
+	 * fsinfo device string.
+	 * Valid formats are (from comments in the 'open_ubi()' function):
+	 * o ubiX_Y    - mount UBI device number X, volume Y;
+	 * o ubiY      - mount UBI device number 0, volume Y;
+	 * o ubiX:NAME - mount UBI device X, volume with name NAME;
+	 * o ubi:NAME  - mount UBI device 0, volume with name NAME.
+	 * The first format may also be preceeded by the path to the dev node
+	 * directory, e.g. "/dev/ubiX_Y".
+	 */
+	ubivol = -1;
+	p = strstr(fsinfo.fsdev, "ubi");
+	if (p) {
+		p += 3;
+		if (isdigit(*p)) {
+			if (p[1] == '_' && isdigit(p[2]) && p[3] == '\0') {
+				ubidev = *p - '0';
+				ubivol = p[2] - '0';
+			} else if (p[1] == '\0') {
+				ubidev = 0;
+				ubivol = *p - '0';
+			} else if (p[1] == ':') {
+				ubidev = *p - '0';
+				ubivol = get_ubi_vol_num_from_name(ubidev, p+2);
+			}
+		} else if (*p == ':') {
+			ubidev = 0;
+			ubivol = get_ubi_vol_num_from_name(ubidev, p+1);
+		}
+	}
+
+	if (ubivol == -1) {
+		errmsg("failed to parse ubifs device string '%s'",
+		       fsinfo.fsdev);
+		return NULL;
+	}
+	
+	sprintf(path_tail, "/ubifs/ubi%d_%d/tst_recovery", ubidev, ubivol);
+	return cat_strings(DEBUGFS_MOUNT_POINT, path_tail);
+}
+
+/*
+ * Set path to the power-cut enable knob file according to the type of
+ * the tested file system.
+ */
+static void set_power_cut_reenable_path(void)
+{
+	char * path;
+	struct stat st;
+
+	if (!strcmp("ubifs", fsinfo.fstype)) {
+		path = make_ubifs_power_cut_reenable_path();
+	} else {
+		errmsg("automatic power-cut re-enable not supported for %s",
+		       fsinfo.fstype);
+		CHECK(0);
+	}
+
+	if (stat(path, &st) == -1) {
+		sys_errmsg("failed to stat power-cut re-enable knob '%s': %d",
+			   path, errno);
+		CHECK(0);
+	}
+
+	fsinfo.pc_reenable_path = path;
+}
+
+/*
  * This is a helper function for 'get_tested_fs_info()'. It parses file-system
  * mount options string, extracts standard mount options from there, and saves
  * them in the 'fsinfo.mount_flags' variable, and non-standard mount options
@@ -3015,12 +3169,17 @@ static const char doc[] = PROGRAM_NAME " version " PROGRAM_VERSION
 "file-system error) for all operations which modify it. In this case this test\n"
 "program re-mounts the file-system and checks that all files and directories\n"
 "which have been successfully synchronized before the power cut. And the test\n"
-"continues forever.\n";
+"continues forever.\n"
+"Whereas -p requires power cut emulation to be enabled globally, the -P option\n"
+"automatically enables the use of power cut emulation on the tested fs instance\n"
+"only (currently implemented for UBIFS only).\n";
 
 static const char optionsstr[] =
 "-n, --repeat=<count> repeat count, default is 1; zero value - repeat forever\n"
 "-p, --power-cut      power cut testing mode (-n parameter is ignored and the\n"
 "                     test continues forever)\n"
+"-P, --pc-reenable    Like -p but power-cut mode is re-enabled by writing\n"
+"                     '1' to the associated sysfs file after every re-mount.\n"
 "-e, --verify-ops     verify all operations, e.g., every time a file is written\n"
 "                     to, read the data back and verify it, every time a\n"
 "                     directory is created, check that it exists, etc\n"
@@ -3031,13 +3190,14 @@ static const char optionsstr[] =
 "-V, --version        print program version\n";
 
 static const struct option long_options[] = {
-	{ .name = "repeat",     .has_arg = 1, .flag = NULL, .val = 'n' },
-	{ .name = "power-cut",  .has_arg = 0, .flag = NULL, .val = 'p' },
-	{ .name = "verify-ops", .has_arg = 0, .flag = NULL, .val = 'e' },
-	{ .name = "reattach",   .has_arg = 1, .flag = NULL, .val = 'm' },
-	{ .name = "verbose",    .has_arg = 0, .flag = NULL, .val = 'v' },
-	{ .name = "help",       .has_arg = 0, .flag = NULL, .val = 'h' },
-	{ .name = "version",    .has_arg = 0, .flag = NULL, .val = 'V' },
+	{ .name = "repeat",      .has_arg = 1, .flag = NULL, .val = 'n' },
+	{ .name = "power-cut",   .has_arg = 0, .flag = NULL, .val = 'p' },
+	{ .name = "pc-reenable", .has_arg = 0, .flag = NULL, .val = 'P' },
+	{ .name = "verify-ops",  .has_arg = 0, .flag = NULL, .val = 'e' },
+	{ .name = "reattach",    .has_arg = 1, .flag = NULL, .val = 'm' },
+	{ .name = "verbose",     .has_arg = 0, .flag = NULL, .val = 'v' },
+	{ .name = "help",        .has_arg = 0, .flag = NULL, .val = 'h' },
+	{ .name = "version",     .has_arg = 0, .flag = NULL, .val = 'V' },
 	{ NULL, 0, NULL, 0},
 };
 
@@ -3052,7 +3212,7 @@ static int parse_opts(int argc, char * const argv[])
 	while (1) {
 		int key, error = 0;
 
-		key = getopt_long(argc, argv, "n:pm:evVh?", long_options, NULL);
+		key = getopt_long(argc, argv, "n:pPm:evVh?", long_options, NULL);
 		if (key == -1)
 			break;
 
@@ -3063,7 +3223,11 @@ static int parse_opts(int argc, char * const argv[])
 				return errmsg("bad repeat count: \"%s\"", optarg);
 			break;
 		case 'p':
-			args.power_cut_mode = 1;
+			if (!args.power_cut_mode)
+				args.power_cut_mode = 1;
+			break;
+		case 'P':
+			args.power_cut_mode = 2;
 			break;
 		case 'm':
 			args.mtdn = simple_strtoul(optarg, &error);
@@ -3205,13 +3369,16 @@ static int recover_tested_fs(void)
 	 * while mounting in 'remount_tested_fs()'.
 	 */
 	mntent = get_tested_fs_mntent();
-	if (mntent)
+	if (mntent) {
+		usleep(MOUNT_DELAY);
 		CHECK(umount(fsinfo.mount_point) != -1);
+	}
 
 	if (args.reattach)
 		CHECK(reattach() == 0);
 
 	if (!um_rorw) {
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 			    fsinfo.fstype, fsinfo.mount_flags,
 			    fsinfo.mount_opts);
@@ -3221,6 +3388,7 @@ static int recover_tested_fs(void)
 			return -1;
 		}
 	} else {
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 			    fsinfo.fstype, fsinfo.mount_flags | MS_RDONLY,
 			    fsinfo.mount_opts);
@@ -3232,6 +3400,7 @@ static int recover_tested_fs(void)
 
 		flags = fsinfo.mount_flags | MS_REMOUNT;
 		flags &= ~((unsigned long)MS_RDONLY);
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point,
 			    fsinfo.fstype, flags, fsinfo.mount_opts);
 		if (ret) {
@@ -3243,6 +3412,7 @@ static int recover_tested_fs(void)
 
 	if (rorw2) {
 		flags = fsinfo.mount_flags | MS_RDONLY | MS_REMOUNT;
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -3252,6 +3422,7 @@ static int recover_tested_fs(void)
 
 		flags = fsinfo.mount_flags | MS_REMOUNT;
 		flags &= ~((unsigned long)MS_RDONLY);
+		usleep(MOUNT_DELAY);
 		ret = mount(fsinfo.fsdev, fsinfo.mount_point, fsinfo.fstype,
 			    flags, fsinfo.mount_opts);
 		if (ret) {
@@ -3261,6 +3432,12 @@ static int recover_tested_fs(void)
 		}
 	}
 
+	if (args.power_cut_mode == 2) {
+		ret = reenable_power_cut();
+		if (ret)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -3285,6 +3462,8 @@ int main(int argc, char *argv[])
 		return EXIT_FAILURE;
 
 	get_tested_fs_info();
+	if (args.power_cut_mode == 2)
+		set_power_cut_reenable_path();
 
 	/* Seed the random generator with out PID */
 	random_seed = getpid();


More information about the linux-mtd mailing list