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 <draviv@codeaurora.org>
[merez@codeaurora.org: fix conflicts due to exiting changes in 3.14]
Signed-off-by: Maya Erez <merez@codeaurora.org>
This commit is contained in:
Dolev Raviv 2015-01-19 20:59:54 +02:00 committed by David Keitel
parent 2ca1be32b2
commit 79d8df4453

View file

@ -11,9 +11,12 @@
*
*/
#include <linux/module.h>
#include <linux/async.h>
#include <linux/atomic.h>
#include <linux/blkdev.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/test-iosched.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
@ -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;