From 79d8df445334d30b79b2135a563d0b3af7850d86 Mon Sep 17 00:00:00 2001 From: Dolev Raviv Date: Mon, 19 Jan 2015 20:59:54 +0200 Subject: [PATCH] scsi: ufs: multiple queries test Sends parallel queries with a pseudo random command. The test sends 33 queries, to make sure at least the queue depth is reached. It tests stability in case of multiple queries. Change-Id: I00f5992b495d17696d2ae50b510253a96ce5c979 Signed-off-by: Dolev Raviv [merez@codeaurora.org: fix conflicts due to exiting changes in 3.14] Signed-off-by: Maya Erez --- drivers/scsi/ufs/ufs_test.c | 146 +++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c index 245b8ed0fab9..687cb60b76f6 100644 --- a/drivers/scsi/ufs/ufs_test.c +++ b/drivers/scsi/ufs/ufs_test.c @@ -11,9 +11,12 @@ * */ -#include +#include +#include #include #include +#include +#include #include #include #include @@ -29,6 +32,7 @@ #define LARGE_PRIME_2 35757 #define DEFAULT_NUM_OF_BIOS 2 #define LONG_SEQUENTIAL_MIXED_TIMOUT_MS 100000 +#define THREADS_COMPLETION_TIMOUT 10000 /* 10 sec */ /* the amount of requests that will be inserted */ #define LONG_SEQ_TEST_NUM_REQS 256 @@ -42,6 +46,7 @@ /* and calculate the MiB value fraction */ #define LONG_TEST_SIZE_FRACTION(x) (BYTE_TO_MB_x_10(x) - \ (LONG_TEST_SIZE_INTEGER(x) * 10)) +#define MAX_PARALLEL_QUERIES 33 #define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args) #define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args) @@ -75,6 +80,7 @@ ufs_test_add_test(utd, UFS_TEST_ ## upper_case_name, "ufs_test_"#test_name,\ enum ufs_test_testcases { UFS_TEST_WRITE_READ_TEST, + UFS_TEST_MULTI_QUERY, UFS_TEST_LONG_SEQUENTIAL_READ, UFS_TEST_LONG_SEQUENTIAL_WRITE, @@ -116,6 +122,11 @@ struct ufs_test_data { unsigned int completed_req_count; /* Test stage */ enum ufs_test_stage test_stage; + + /* Parameters for maintaining multiple threads */ + int fail_threads; + atomic_t outstanding_threads; + struct completion outstanding_complete; }; static struct ufs_test_data *utd; @@ -158,6 +169,9 @@ 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 UFS_TEST_MULTI_QUERY: + return "Test multiple queries at the same time"; + break; case UFS_TEST_LONG_SEQUENTIAL_READ: return "UFS long sequential read test"; break; @@ -208,6 +222,9 @@ static int ufs_test_show(struct seq_file *file, int test_case) "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_MULTI_QUERY: + test_description = "Test multiple queries at the same time.\n"; + break; case UFS_TEST_LONG_SEQUENTIAL_READ: test_description = "\nufs_long_sequential_read_test\n" "=========\n" @@ -283,6 +300,13 @@ static int ufs_test_check_result(struct test_data *td) test_pr_err("%s: An error occurred during the test.", __func__); return TEST_FAILED; } + + if (utd->fail_threads != 0) { + test_pr_err("%s: About %d threads failed during execution.", + __func__, utd->fail_threads); + return utd->fail_threads; + } + return 0; } @@ -344,6 +368,118 @@ static int ufs_test_run_write_read_test(struct test_data *td) return ret; } +static void ufs_test_thread_complete(int result) +{ + if (result) + utd->fail_threads++; + atomic_dec(&utd->outstanding_threads); + if (!atomic_read(&utd->outstanding_threads)) + complete(&utd->outstanding_complete); +} + +static void ufs_test_random_async_query(void *data, async_cookie_t cookie) +{ + int op; + struct ufs_hba *hba = (struct ufs_hba *)data; + int buff_len = UNIT_DESC_MAX_SIZE; + u8 desc_buf[UNIT_DESC_MAX_SIZE]; + bool flag; + u32 att; + int ret = 0; + + op = ufs_test_pseudo_random_seed(&utd->random_test_seed, 1, 8); + /* + * When write data (descriptor/attribute/flag) queries are issued, + * regular work and functionality must be kept. The data is read + * first to make sure the original state is restored. + */ + switch (op) { + case UPIU_QUERY_OPCODE_READ_DESC: + case UPIU_QUERY_OPCODE_WRITE_DESC: + ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC, + QUERY_DESC_IDN_UNIT, 0, 0, desc_buf, &buff_len); + if (ret || op == UPIU_QUERY_OPCODE_READ_DESC) + break; + + ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_WRITE_DESC, + QUERY_DESC_IDN_UNIT, 0, 0, desc_buf, &buff_len); + break; + case UPIU_QUERY_OPCODE_WRITE_ATTR: + case UPIU_QUERY_OPCODE_READ_ATTR: + ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, + QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &att); + if (ret || op == UPIU_QUERY_OPCODE_READ_ATTR) + break; + + ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, + QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &att); + break; + case UPIU_QUERY_OPCODE_READ_FLAG: + case UPIU_QUERY_OPCODE_SET_FLAG: + case UPIU_QUERY_OPCODE_CLEAR_FLAG: + case UPIU_QUERY_OPCODE_TOGGLE_FLAG: + /* We read the QUERY_FLAG_IDN_BKOPS_EN and restore it later */ + ret = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, + QUERY_FLAG_IDN_BKOPS_EN, &flag); + if (ret || op == UPIU_QUERY_OPCODE_READ_FLAG) + break; + + /* After changing the flag we have to change it back */ + ret = ufshcd_query_flag(hba, op, QUERY_FLAG_IDN_BKOPS_EN, NULL); + if ((op == UPIU_QUERY_OPCODE_SET_FLAG && flag) || + (op == UPIU_QUERY_OPCODE_CLEAR_FLAG && !flag)) + /* No need to change it back */ + break; + + if (flag) + ret |= ufshcd_query_flag(hba, + UPIU_QUERY_OPCODE_SET_FLAG, + QUERY_FLAG_IDN_BKOPS_EN, NULL); + else + ret |= ufshcd_query_flag(hba, + UPIU_QUERY_OPCODE_CLEAR_FLAG, + QUERY_FLAG_IDN_BKOPS_EN, NULL); + break; + default: + test_pr_err("%s: Random error unknown op %d", __func__, op); + } + + if (ret) + test_pr_err("%s: Query thread with op %d, failed with err %d.", + __func__, op, ret); + + ufs_test_thread_complete(ret); +} + +static int ufs_test_run_multi_query_test(struct test_data *td) +{ + int i; + struct scsi_device *sdev; + struct ufs_hba *hba; + + BUG_ON(!td || !td->req_q || !td->req_q->queuedata); + sdev = (struct scsi_device *)td->req_q->queuedata; + BUG_ON(!sdev->host); + hba = shost_priv(sdev->host); + BUG_ON(!hba); + + atomic_set(&utd->outstanding_threads, 0); + utd->fail_threads = 0; + init_completion(&utd->outstanding_complete); + for (i = 0; i < MAX_PARALLEL_QUERIES; ++i) { + atomic_inc(&utd->outstanding_threads); + async_schedule(ufs_test_random_async_query, hba); + } + + if (!wait_for_completion_timeout(&utd->outstanding_complete, + THREADS_COMPLETION_TIMOUT)) { + test_pr_err("%s: Multi-query test timed-out %d threads left", + __func__, atomic_read(&utd->outstanding_threads)); + } + test_iosched_mark_test_completion(); + return 0; +} + static void long_seq_test_free_end_io_fn(struct request *rq, int err) { struct test_request *test_rq; @@ -550,6 +686,10 @@ static ssize_t ufs_test_write(struct file *file, const char __user *buf, utd->test_info.check_test_completion_fn = ufs_write_read_completion; break; + case UFS_TEST_MULTI_QUERY: + utd->test_info.run_test_fn = ufs_test_run_multi_query_test; + utd->test_info.check_test_result_fn = ufs_test_check_result; + break; case UFS_TEST_LONG_SEQUENTIAL_READ: case UFS_TEST_LONG_SEQUENTIAL_WRITE: utd->test_info.run_test_fn = run_long_seq_test; @@ -589,6 +729,7 @@ static ssize_t ufs_test_write(struct file *file, const char __user *buf, } TEST_OPS(write_read_test, WRITE_READ_TEST); +TEST_OPS(multi_query, MULTI_QUERY); TEST_OPS(long_sequential_read, LONG_SEQUENTIAL_READ); TEST_OPS(long_sequential_write, LONG_SEQUENTIAL_WRITE); TEST_OPS(long_sequential_mixed, LONG_SEQUENTIAL_MIXED); @@ -640,6 +781,9 @@ static int ufs_test_debugfs_init(void) if (ret) goto exit_err; ret = add_test(utd, long_sequential_mixed, LONG_SEQUENTIAL_MIXED); + if (ret) + goto exit_err; + add_test(utd, multi_query, MULTI_QUERY); if (ret) goto exit_err;