mmc: new request notification unit-test
The new request notification test checks the following scenario:
A new request arrives after a NULL request was sent to the mmc_queue,
which is waiting for completion of a former request.
Change-Id: I05db0959ded400e292eb5e84e1ecfc579b78ee62
Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
Signed-off-by: Lee Susman <lsusman@codeaurora.org>
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 92603f6..08c75a0 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -65,9 +65,10 @@
#define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
#define SANITIZE_TEST_TIMEOUT 240000
+#define NEW_REQ_TEST_SLEEP_TIME 1
+#define NEW_REQ_TEST_NUM_BIOS 64
#define TEST_REQUEST_NUM_OF_BIOS 3
-
#define CHECK_BKOPS_STATS(stats, exp_bkops, exp_hpi, exp_suspend) \
((stats.bkops != exp_bkops) || \
(stats.hpi != exp_hpi) || \
@@ -153,6 +154,8 @@
TEST_LONG_SEQUENTIAL_READ,
TEST_LONG_SEQUENTIAL_WRITE,
+
+ TEST_NEW_REQ_NOTIFICATION,
};
enum mmc_block_test_group {
@@ -163,6 +166,7 @@
TEST_SEND_INVALID_GROUP,
TEST_PACKING_CONTROL_GROUP,
TEST_BKOPS_GROUP,
+ TEST_NEW_NOTIFICATION_GROUP,
};
enum bkops_test_stages {
@@ -182,6 +186,7 @@
struct dentry *bkops_test;
struct dentry *long_sequential_read_test;
struct dentry *long_sequential_write_test;
+ struct dentry *new_req_notification_test;
};
struct mmc_block_test_data {
@@ -217,6 +222,8 @@
enum bkops_test_stages bkops_stage;
/* A wait queue for BKOPs tests */
wait_queue_head_t bkops_wait_q;
+
+ unsigned int completed_req_count;
};
static struct mmc_block_test_data *mbtd;
@@ -695,6 +702,8 @@
return "\"long sequential read\"";
case TEST_LONG_SEQUENTIAL_WRITE:
return "\"long sequential write\"";
+ case TEST_NEW_REQ_NOTIFICATION:
+ return "\"new request notification test\"";
default:
return " Unknown testcase";
}
@@ -2069,6 +2078,170 @@
return ret;
}
+/*
+ * new_req_post_test() - Do post test operations for
+ * new_req_notification test: disable the statistics and clear
+ * the feature flags.
+ * @td The test_data for the new_req test that has
+ * ended.
+ */
+static int new_req_post_test(struct test_data *td)
+{
+ struct mmc_queue *mq;
+
+ if (!td || !td->req_q)
+ goto exit;
+
+ mq = (struct mmc_queue *)td->req_q->queuedata;
+
+ if (!mq || !mq->card)
+ goto exit;
+
+ /* disable async_event test stats */
+ mq->card->async_event_stats.enabled = false;
+ mmc_print_async_event_stats(mq->card);
+ test_pr_info("Completed %d requests",
+ mbtd->completed_req_count);
+
+exit:
+ return 0;
+}
+
+/*
+ * check_new_req_result() - Print out the number of completed
+ * requests. Assigned to the check_test_result_fn pointer,
+ * therefore the name.
+ * @td The test_data for the new_req test that has
+ * ended.
+ */
+static int check_new_req_result(struct test_data *td)
+{
+ test_pr_info("%s: Test results: Completed %d requests",
+ __func__, mbtd->completed_req_count);
+ return 0;
+}
+
+/*
+ * new_req_free_end_io_fn() - Remove request from queuelist and
+ * free request's allocated memory. Used as a call-back
+ * assigned to end_io member in request struct.
+ * @rq The request to be freed
+ * @err Unused
+ */
+static void new_req_free_end_io_fn(struct request *rq, int err)
+{
+ struct test_request *test_rq =
+ (struct test_request *)rq->elv.priv[0];
+ struct test_data *ptd = test_get_test_data();
+
+ BUG_ON(!test_rq);
+
+ spin_lock_irq(&ptd->lock);
+ list_del_init(&test_rq->queuelist);
+ ptd->dispatched_count--;
+ spin_unlock_irq(&ptd->lock);
+
+ __blk_put_request(ptd->req_q, test_rq->rq);
+ kfree(test_rq->bios_buffer);
+ kfree(test_rq);
+ mbtd->completed_req_count++;
+}
+
+static int prepare_new_req(struct test_data *td)
+{
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+
+ mmc_blk_init_packed_statistics(mq->card);
+ mmc_blk_init_async_event_statistics(mq->card);
+
+ mbtd->completed_req_count = 0;
+
+ return 0;
+}
+
+static int test_new_req_notification(struct test_data *ptd)
+{
+ int ret = 0;
+ int i;
+ unsigned int requests_count = 2;
+ unsigned int bio_num;
+ struct test_request *test_rq = NULL;
+
+ while (1) {
+ for (i = 0; i < requests_count; i++) {
+ bio_num = TEST_MAX_BIOS_PER_REQ;
+ test_rq = test_iosched_create_test_req(0, READ,
+ ptd->start_sector,
+ bio_num, TEST_PATTERN_5A,
+ new_req_free_end_io_fn);
+ if (test_rq) {
+ spin_lock_irq(ptd->req_q->queue_lock);
+ list_add_tail(&test_rq->queuelist,
+ &ptd->test_queue);
+ ptd->test_count++;
+ spin_unlock_irq(ptd->req_q->queue_lock);
+ } else {
+ test_pr_err("%s: failed to create read request",
+ __func__);
+ ret = -ENODEV;
+ break;
+ }
+ }
+
+ __blk_run_queue(ptd->req_q);
+ /* wait while a mmc layer will send all requests in test_queue*/
+ while (!list_empty(&ptd->test_queue))
+ msleep(NEW_REQ_TEST_SLEEP_TIME);
+
+ /* test finish criteria */
+ if (mbtd->completed_req_count > 1000) {
+ if (ptd->dispatched_count)
+ continue;
+ else
+ break;
+ }
+
+ for (i = 0; i < requests_count; i++) {
+ bio_num = NEW_REQ_TEST_NUM_BIOS;
+ test_rq = test_iosched_create_test_req(0, READ,
+ ptd->start_sector,
+ bio_num, TEST_PATTERN_5A,
+ new_req_free_end_io_fn);
+ if (test_rq) {
+ spin_lock_irq(ptd->req_q->queue_lock);
+ list_add_tail(&test_rq->queuelist,
+ &ptd->test_queue);
+ ptd->test_count++;
+ spin_unlock_irq(ptd->req_q->queue_lock);
+ } else {
+ test_pr_err("%s: failed to create read request",
+ __func__);
+ ret = -ENODEV;
+ break;
+ }
+ }
+ __blk_run_queue(ptd->req_q);
+ }
+
+ test_iosched_mark_test_completion();
+ test_pr_info("%s: EXIT: %d code", __func__, ret);
+
+ return ret;
+}
+
+static int run_new_req(struct test_data *td)
+{
+ int ret = 0;
+ struct request_queue *q = td->req_q;
+ struct mmc_queue *mq = (struct mmc_queue *)q->queuedata;
+
+ mmc_blk_init_async_event_statistics(mq->card);
+ ret = test_new_req_notification(td);
+
+ return ret;
+}
+
static bool message_repeat;
static int test_open(struct inode *inode, struct file *file)
{
@@ -2789,6 +2962,73 @@
.read = long_sequential_write_test_read,
};
+static ssize_t new_req_notification_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+
+ test_pr_info("%s: -- new_req_notification TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ mbtd->test_group = TEST_NEW_NOTIFICATION_GROUP;
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_new_req;
+ mbtd->test_info.check_test_result_fn = check_new_req_result;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+ mbtd->test_info.run_test_fn = run_new_req;
+ mbtd->test_info.timeout_msec = 10 * 60 * 1000; /* 1 min */
+ mbtd->test_info.post_test_fn = new_req_post_test;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ===================", __func__);
+ test_pr_info("%s: start test case TEST_NEW_REQ_NOTIFICATION",
+ __func__);
+ mbtd->test_info.testcase = TEST_NEW_REQ_NOTIFICATION;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret) {
+ test_pr_info("%s: break from new_req tests loop",
+ __func__);
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t new_req_notification_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nnew_req_notification_test\n========================\n"
+ "Description:\n"
+ "This test checks following scenarious\n"
+ "- new request arrives after a NULL request was sent to the "
+ "mmc_queue,\n"
+ "which is waiting for completion of a former request\n");
+
+ return strnlen(buffer, count);
+}
+
+const struct file_operations new_req_notification_test_ops = {
+ .open = test_open,
+ .write = new_req_notification_test_write,
+ .read = new_req_notification_test_read,
+};
static void mmc_block_test_debugfs_cleanup(void)
{
@@ -2801,6 +3041,7 @@
debugfs_remove(mbtd->debug.bkops_test);
debugfs_remove(mbtd->debug.long_sequential_read_test);
debugfs_remove(mbtd->debug.long_sequential_write_test);
+ debugfs_remove(mbtd->debug.new_req_notification_test);
}
static int mmc_block_test_debugfs_init(void)
@@ -2880,6 +3121,16 @@
NULL,
&bkops_test_ops);
+ mbtd->debug.new_req_notification_test =
+ debugfs_create_file("new_req_notification_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &new_req_notification_test_ops);
+
+ if (!mbtd->debug.new_req_notification_test)
+ goto err_nomem;
+
if (!mbtd->debug.bkops_test)
goto err_nomem;