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:
parent
2ca1be32b2
commit
79d8df4453
1 changed files with 145 additions and 1 deletions
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue