Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1eede985 authored by Dolev Raviv's avatar Dolev Raviv
Browse files

scsi: ufs: rework unit test infrastructure



Upgrade the APIs used.
Encapsulate test ops (read/write/open functions) and dentry creation
in macros.
Fix naming collisions with mmc tests.

Change-Id: I5c48b369beaf902f8187ced9d884a2149a8644f0
Signed-off-by: default avatarDolev Raviv <draviv@codeaurora.org>
parent 01364e16
Loading
Loading
Loading
Loading
+174 −261
Original line number Diff line number Diff line
@@ -44,23 +44,44 @@
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)

#define TEST_OPS(test_name, upper_case_name)				\
static int ufs_test_ ## test_name ## _show(struct seq_file *file,	\
		void *data)						\
{ return ufs_test_show(file, UFS_TEST_ ## upper_case_name); }		\
static int ufs_test_ ## test_name ## _open(struct inode *inode,		\
		struct file *file)					\
{ return single_open(file, ufs_test_ ## test_name ## _show,		\
		inode->i_private); }					\
static ssize_t ufs_test_ ## test_name ## _write(struct file *file,	\
		const char __user *buf, size_t count, loff_t *ppos)	\
{ return ufs_test_write(file, buf, count, ppos,				\
			UFS_TEST_ ## upper_case_name); }		\
const struct file_operations ufs_test_ ## test_name ## _ops = {		\
	.open = ufs_test_ ## test_name ## _open,			\
	.read = seq_read,						\
	.write = ufs_test_ ## test_name ## _write,			\
};

#define add_test(utd, test_name, upper_case_name)			\
ufs_test_add_test(utd, UFS_TEST_ ## upper_case_name, "ufs_test_"#test_name,\
				&(ufs_test_ ## test_name ## _ops));	\





enum ufs_test_testcases {
	UFS_TEST_WRITE_READ_TEST,

	TEST_LONG_SEQUENTIAL_READ,
	TEST_LONG_SEQUENTIAL_WRITE,
};
	UFS_TEST_LONG_SEQUENTIAL_READ,
	UFS_TEST_LONG_SEQUENTIAL_WRITE,

struct ufs_test_debug {
	struct dentry *write_read_test; /* basic test */
	struct dentry *random_test_seed; /* parameters in utils */
	struct dentry *long_sequential_read_test;
	struct dentry *long_sequential_write_test;
	NUM_TESTS,
};

struct ufs_test_data {
	/* Data structure for debugfs dentrys */
	struct ufs_test_debug debug;
	struct dentry **test_list;
	/*
	 * Data structure containing individual test information, including
	 * self-defined specific data
@@ -70,8 +91,6 @@ struct ufs_test_data {
	struct blk_dev_test_type bdt;
	/* A wait queue for OPs to complete */
	wait_queue_head_t wait_q;
	/* a flag for read compleation */
	bool read_completed;
	/* a flag for write compleation */
	bool write_completed;
	/*
@@ -79,13 +98,40 @@ struct ufs_test_data {
	 * disabled and 2 BIOs are written.
	 */
	unsigned int random_test_seed;
	struct dentry *random_test_seed_dentry;

	/* A counter for the number of test requests completed */
	unsigned int completed_req_count;
};

static struct ufs_test_data *utd;

static bool message_repeat;
static int ufs_test_add_test(struct ufs_test_data *utd,
		enum ufs_test_testcases test_id, char *test_str,
		const struct file_operations *test_fops)
{
	int ret = 0;
	struct dentry *tests_root;

	if (test_id >= NUM_TESTS)
		return -EINVAL;

	tests_root = test_iosched_get_debugfs_tests_root();
	if (!tests_root) {
		test_pr_err("%s: Failed to create debugfs root.", __func__);
		return -EINVAL;
	}

	utd->test_list[test_id] = debugfs_create_file(test_str,
						S_IRUGO | S_IWUGO, tests_root,
						NULL, test_fops);
	if (!utd->test_list[test_id]) {
		test_pr_err("%s: Could not create the test %s", test_str,
				__func__);
		ret = -ENOMEM;
	}
	return ret;
}

static char *ufs_test_get_test_case_str(struct test_data *td)
{
@@ -98,10 +144,10 @@ static char *ufs_test_get_test_case_str(struct test_data *td)
	case UFS_TEST_WRITE_READ_TEST:
		return "UFS write read test";
		break;
	case TEST_LONG_SEQUENTIAL_READ:
	case UFS_TEST_LONG_SEQUENTIAL_READ:
		return "UFS long sequential read test";
		break;
	case TEST_LONG_SEQUENTIAL_WRITE:
	case UFS_TEST_LONG_SEQUENTIAL_WRITE:
		return "UFS long sequential write test";
		break;
	default:
@@ -138,16 +184,53 @@ static void ufs_test_write_read_test_end_io_fn(struct request *rq, int err)
	struct test_request *test_rq = (struct test_request *)rq->elv.priv[0];
	BUG_ON(!test_rq);

	test_rq->req_completed = 1;
	test_rq->req_result = err;

	test_pr_info("%s: request %d completed, err=%d",
			__func__, test_rq->req_id, err);
	test_rq->req_completed = 1;
	test_rq->req_result = err;

	utd->write_completed = true;
	wake_up(&utd->wait_q);
}

static int ufs_test_show(struct seq_file *file, int test_case)
{
	char *test_description;

	switch (test_case) {
	case UFS_TEST_WRITE_READ_TEST:
		test_description = "\nufs_write_read_test\n"
		 "=========\n"
		 "Description:\n"
		 "This test write once a random block and than reads it to "
		 "verify its content. Used to debug first time transactions.\n";
		break;
	case UFS_TEST_LONG_SEQUENTIAL_READ:
		test_description = "\nufs_long_sequential_read_test\n"
		 "=========\n"
		 "Description:\n"
		 "This test runs the following scenarios\n"
		 "- Long Sequential Read Test: this test measures read "
		 "throughput at the driver level by sequentially reading many "
		 "large requests.\n";
		break;
	case UFS_TEST_LONG_SEQUENTIAL_WRITE:
		test_description =  "\nufs_long_sequential_write_test\n"
		 "=========\n"
		 "Description:\n"
		 "This test runs the following scenarios\n"
		 "- Long Sequential Write Test: this test measures write "
		 "throughput at the driver level by sequentially writing many "
		 "large requests\n";
		break;
	default:
		test_description = "Unknown test";
	}

	seq_puts(file, test_description);
	return 0;
}

static struct gendisk *ufs_test_get_rq_disk(void)
{
	struct request_queue *req_q = test_iosched_get_req_queue();
@@ -198,8 +281,8 @@ static int ufs_test_run_write_read_test(struct test_data *td)
			, __func__, num_bios, td->wr_rd_next_req_id);

	utd->write_completed = false;
	ret = test_iosched_add_wr_rd_test_req(0, WRITE, start_sec,
					num_bios, TEST_PATTERN_5A,
	ret = test_iosched_add_wr_rd_test_req(0, WRITE, start_sec, num_bios,
					TEST_PATTERN_5A,
					ufs_test_write_read_test_end_io_fn);

	if (ret) {
@@ -226,81 +309,14 @@ static int ufs_test_run_write_read_test(struct test_data *td)
	return ret;
}

static
int ufs_test_write_read_test_open_cb(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	message_repeat = 1;
	test_pr_info("%s:UFS test initialized", __func__);
	return 0;
}

static ssize_t ufs_test_write_read_test_write_cb(struct file *file,
					const char __user *buf,
					size_t count, loff_t *ppos)
{
	int ret = 0;
	int i;
	int number;

	sscanf(buf, "%d", &number);

	if (number <= 0)
		number = 1;

	test_pr_info("%s:the test will run for %d iterations.",
			__func__, number);
	memset(&utd->test_info, 0, sizeof(struct test_info));

	/* Initializing test */
	utd->test_info.data = utd;
	utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
	utd->test_info.testcase = UFS_TEST_WRITE_READ_TEST;
	utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
	utd->test_info.run_test_fn = ufs_test_run_write_read_test;

	/* Running the test multiple times */
	for (i = 0; i < number; ++i) {
		ret = test_iosched_start_test(&utd->test_info);
		if (ret) {
			test_pr_err("%s: Test failed.", __func__);
			return ret;
		}
	}

	test_pr_info("%s: Completed all the ufs test iterations.", __func__);

	return count;
}

static ssize_t ufs_test_write_read_test_read_cb(struct file *file,
		char __user *buffer, size_t count, loff_t *offset)
{
	memset((void *) buffer, 0, count);

	snprintf(buffer, count, "\nThis is a UFS write-read test for debug.\n");

	if (message_repeat == 1) {
		message_repeat = 0;
		return strnlen(buffer, count);
	} else
		return 0;
}

const struct file_operations write_read_test_ops = {
		.open = ufs_test_write_read_test_open_cb,
		.write = ufs_test_write_read_test_write_cb,
		.read = ufs_test_write_read_test_read_cb,
};

static void long_seq_test_free_end_io_fn(struct request *rq, int err)
{
	struct test_request *test_rq;
	struct test_data *ptd = test_get_test_data();

	if (rq)
	if (rq) {
		test_rq = (struct test_request *)rq->elv.priv[0];
	else {
	} else {
		test_pr_err("%s: error: NULL request", __func__);
		return;
	}
@@ -317,8 +333,9 @@ static void long_seq_test_free_end_io_fn(struct request *rq, int err)
	kfree(test_rq);
	utd->completed_req_count++;

	test_pr_err("%s: request %d completed, err=%d",
	       __func__, test_rq->req_id, err);
	if (err)
		test_pr_err("%s: request %d completed, err=%d", __func__,
			test_rq->req_id, err);

	check_test_completion();

@@ -335,7 +352,7 @@ static int run_long_seq_test(struct test_data *td)
	utd->completed_req_count = 0;
	inserted_requests = 0;

	if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_READ)
	if (td->test_info.testcase == UFS_TEST_LONG_SEQUENTIAL_READ)
		direction = READ;
	else
		direction = WRITE;
@@ -379,11 +396,13 @@ static int run_long_seq_test(struct test_data *td)
	return ret;
}


void long_seq_test_calc_throughput(unsigned long mtime,
				   unsigned long byte_count)
static int  long_seq_test_calc_throughput(struct test_data *td)
{
	unsigned long fraction, integer;
	unsigned long mtime, byte_count;

	mtime = ktime_to_ms(utd->test_info.test_duration);
	byte_count = utd->test_info.test_byte_count;

	test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
			__func__, mtime, LONG_TEST_SIZE_INTEGER(byte_count),
@@ -401,168 +420,80 @@ void long_seq_test_calc_throughput(unsigned long mtime,

	test_pr_info("%s: Throughput: %lu.%lu MiB/sec\n",
		__func__, integer, fraction);
}

static ssize_t long_sequential_read_test_write(struct file *file,
				const char __user *buf,
				size_t count,
				loff_t *ppos)
{
	int ret = 0;
	int i = 0;
	int number = -1;
	unsigned long mtime, byte_count;

	test_pr_info("%s: -- UFS Long Sequential Read TEST --", __func__);

	sscanf(buf, "%d", &number);

	if (number <= 0)
		number = 1;

	memset(&utd->test_info, 0, sizeof(struct test_info));

	utd->test_info.data = utd;
	utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
	utd->test_info.run_test_fn = run_long_seq_test;
	utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
	utd->test_info.testcase = TEST_LONG_SEQUENTIAL_READ;

	for (i = 0 ; i < number ; ++i) {
		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
		test_pr_info("%s: ====================", __func__);

		ret = test_iosched_start_test(&utd->test_info);
		if (ret)
			break;

		mtime = ktime_to_ms(utd->test_info.test_duration);
		byte_count = utd->test_info.test_byte_count;

		long_seq_test_calc_throughput(mtime, byte_count);

		/* Allow FS requests to be dispatched */
		msleep(1000);
	}

	return count;
}

static ssize_t long_sequential_read_test_read(struct file *file,
			       char __user *buffer,
			       size_t count,
			       loff_t *offset)
{
	memset((void *)buffer, 0, count);

	snprintf(buffer, count,
		 "\nufs_long_sequential_read_test\n"
		 "=========\n"
		 "Description:\n"
		 "This test runs the following scenarios\n"
		 "- Long Sequential Read Test: this test measures read "
		 "throughput at the driver level by sequentially reading many "
		 "large requests.\n");

	if (message_repeat == 1) {
		message_repeat = 0;
		return strnlen(buffer, count);
	} else
	return 0;
}

static bool message_repeat;
static int test_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	message_repeat = 1;
	return 0;
}

const struct file_operations long_sequential_read_test_ops = {
	.open = test_open,
	.write = long_sequential_read_test_write,
	.read = long_sequential_read_test_read,
};

static ssize_t long_sequential_write_test_write(struct file *file,
				const char __user *buf,
				size_t count,
				loff_t *ppos)
static ssize_t ufs_test_write(struct file *file, const char __user *buf,
			size_t count, loff_t *ppos, int test_case)
{
	int ret = 0;
	int i = 0;
	int number = -1;
	unsigned long mtime, byte_count;

	test_pr_info("%s: -- UFS Long Sequential Write TEST --", __func__);
	int i;
	int number;

	sscanf(buf, "%d", &number);
	ret = kstrtoint_from_user(buf, count, 0, &number);
	if (ret < 0) {
		test_pr_err("%s: Error while reading test parameter value %d",
				__func__, ret);
		return ret;
	}

	if (number <= 0)
		number = 1;

	test_pr_info("%s:the test will run for %d iterations.",
			__func__, number);
	memset(&utd->test_info, 0, sizeof(struct test_info));

	/* Initializing test */
	utd->test_info.data = utd;
	utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;
	utd->test_info.get_test_case_str_fn = ufs_test_get_test_case_str;
	utd->test_info.testcase = test_case;
	utd->test_info.get_rq_disk_fn = ufs_test_get_rq_disk;

	switch (test_case) {
	case UFS_TEST_WRITE_READ_TEST:
		utd->test_info.run_test_fn = ufs_test_run_write_read_test;
		break;
	case UFS_TEST_LONG_SEQUENTIAL_READ:
	case UFS_TEST_LONG_SEQUENTIAL_WRITE:
		utd->test_info.run_test_fn = run_long_seq_test;
	utd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
		utd->test_info.post_test_fn = long_seq_test_calc_throughput;
		break;
	default:
		test_pr_err("%s: Unknown test-case: %d", __func__, test_case);
		WARN_ON(true);
	}

	/* Running the test multiple times */
	for (i = 0; i < number; ++i) {
		test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
		test_pr_info("%s: ====================", __func__);

		utd->test_info.test_byte_count = 0;
		ret = test_iosched_start_test(&utd->test_info);
		if (ret)
			break;

		mtime = ktime_to_ms(utd->test_info.test_duration);
		byte_count = utd->test_info.test_byte_count;

		long_seq_test_calc_throughput(mtime, byte_count);
		if (ret) {
			test_pr_err("%s: Test failed, err=%d.", __func__, ret);
			return ret;
		}

		/* Allow FS requests to be dispatched */
		msleep(1000);
	}

	return count;
}

static ssize_t long_sequential_write_test_read(struct file *file,
			       char __user *buffer,
			       size_t count,
			       loff_t *offset)
{
	memset((void *)buffer, 0, count);

	snprintf(buffer, count,
		 "\nufs_long_sequential_write_test\n"
		 "=========\n"
		 "Description:\n"
		 "This test runs the following scenarios\n"
		 "- Long Sequential Write Test: this test measures write "
		 "throughput at the driver level by sequentially writing many "
		 "large requests\n");
	test_pr_info("%s: Completed all the ufs test iterations.", __func__);

	if (message_repeat == 1) {
		message_repeat = 0;
		return strnlen(buffer, count);
	} else
		return 0;
	return count;
}

const struct file_operations long_sequential_write_test_ops = {
	.open = test_open,
	.write = long_sequential_write_test_write,
	.read = long_sequential_write_test_read,
};
TEST_OPS(write_read_test, WRITE_READ_TEST);
TEST_OPS(long_sequential_read, LONG_SEQUENTIAL_READ);
TEST_OPS(long_sequential_write, LONG_SEQUENTIAL_WRITE);

static void ufs_test_debugfs_cleanup(void)
{
	debugfs_remove_recursive(test_iosched_get_debugfs_tests_root());
	kfree(utd->test_list);
}

static int ufs_test_debugfs_init(void)
@@ -573,59 +504,43 @@ static int ufs_test_debugfs_init(void)
	utils_root = test_iosched_get_debugfs_utils_root();
	tests_root = test_iosched_get_debugfs_tests_root();

	utd->test_list = kmalloc(sizeof(struct dentry *) * NUM_TESTS,
			GFP_KERNEL);
	if (!utd->test_list) {
		test_pr_err("%s: failed to allocate tests dentrys", __func__);
		return -ENODEV;
	}

	if (!utils_root || !tests_root) {
		test_pr_err("%s: Failed to create debugfs root.", __func__);
		ret = -EINVAL;
		goto exit;
		goto exit_err;
	}

	utd->debug.random_test_seed = debugfs_create_u32("random_test_seed",
	utd->random_test_seed_dentry = debugfs_create_u32("random_test_seed",
			S_IRUGO | S_IWUGO, utils_root, &utd->random_test_seed);

	if (!utd->debug.random_test_seed) {
	if (!utd->random_test_seed_dentry) {
		test_pr_err("%s: Could not create debugfs random_test_seed.",
				__func__);
		ret = -ENOMEM;
		goto exit;
	}

	utd->debug.write_read_test = debugfs_create_file("ufs_write_read_test",
					S_IRUGO | S_IWUGO, tests_root,
					NULL, &write_read_test_ops);

	if (!utd->debug.write_read_test) {
		ret = -ENOMEM;
		goto exit_err;
	}

	utd->debug.long_sequential_read_test = debugfs_create_file(
					"ufs_long_sequential_read_test",
					S_IRUGO | S_IWUGO,
					tests_root,
					NULL,
					&long_sequential_read_test_ops);

	if (!utd->debug.long_sequential_read_test) {
		ret = -ENOMEM;
	ret = add_test(utd, write_read_test, WRITE_READ_TEST);
	if (ret)
		goto exit_err;
	}

	utd->debug.long_sequential_write_test = debugfs_create_file(
					"ufs_long_sequential_write_test",
					S_IRUGO | S_IWUGO,
					tests_root,
					NULL,
					&long_sequential_write_test_ops);

	if (!utd->debug.long_sequential_write_test) {
		ret = -ENOMEM;
	ret = add_test(utd, long_sequential_read, LONG_SEQUENTIAL_READ);
	if (ret)
		goto exit_err;
	ret = add_test(utd, long_sequential_write, LONG_SEQUENTIAL_WRITE);
	if (ret)
		goto exit_err;
	}

	goto exit;

exit_err:
	debugfs_remove_recursive(tests_root);
	ufs_test_debugfs_cleanup();
exit:
	return ret;
}
@@ -663,10 +578,8 @@ static void __exit ufs_test_exit(void)
	test_iosched_unregister(&utd->bdt);
	kfree(utd);
}
module_init(ufs_test_init)
;
module_exit(ufs_test_exit)
;
module_init(ufs_test_init);
module_exit(ufs_test_exit);

MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("UFC test");