forked from rubenslte/android_kernel_samsung_msm8226
Merge "scsi: ufs: long sequential read/write tests"
This commit is contained in:
committed by
Gerrit - the friendly Code Review server
commit
180a5584c5
@@ -784,10 +784,10 @@ int test_iosched_start_test(struct test_info *t_info)
|
||||
test_pr_info("%s: Waiting for the test completion", __func__);
|
||||
|
||||
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
|
||||
t_info->test_duration = ptd->test_info.test_duration;
|
||||
t_info->test_byte_count = ptd->test_info.test_byte_count;
|
||||
del_timer_sync(&ptd->timeout_timer);
|
||||
|
||||
memcpy(t_info, &ptd->test_info, sizeof(struct test_info));
|
||||
|
||||
ret = check_test_result(ptd);
|
||||
if (ret) {
|
||||
test_pr_err("%s: check_test_result failed\n",
|
||||
|
||||
+338
-13
@@ -18,13 +18,27 @@
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <../sd.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MODULE_NAME "ufs_test"
|
||||
|
||||
#define TEST_MAX_BIOS_PER_REQ 120
|
||||
#define TEST_MAX_BIOS_PER_REQ 16
|
||||
#define LARGE_PRIME_1 1103515367
|
||||
#define LARGE_PRIME_2 35757
|
||||
#define DEFAULT_NUM_OF_BIOS 2
|
||||
#define DEFAULT_NUM_OF_BIOS 2
|
||||
|
||||
/* the amount of requests that will be inserted */
|
||||
#define LONG_SEQ_TEST_NUM_REQS 256
|
||||
/* request queue limitation is 128 requests, and we leave 10 spare requests */
|
||||
#define QUEUE_MAX_REQUESTS 118
|
||||
#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
|
||||
/* actual number of MiB in test multiplied by 10, for single digit precision*/
|
||||
#define BYTE_TO_MB_x_10(x) ((x * 10) / (1024 * 1024))
|
||||
/* extract integer value */
|
||||
#define LONG_TEST_SIZE_INTEGER(x) (BYTE_TO_MB_x_10(x) / 10)
|
||||
/* 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 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)
|
||||
@@ -32,11 +46,16 @@
|
||||
|
||||
enum ufs_test_testcases {
|
||||
UFS_TEST_WRITE_READ_TEST,
|
||||
|
||||
TEST_LONG_SEQUENTIAL_READ,
|
||||
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;
|
||||
};
|
||||
|
||||
struct ufs_test_data {
|
||||
@@ -60,6 +79,8 @@ struct ufs_test_data {
|
||||
* disabled and 2 BIOs are written.
|
||||
*/
|
||||
unsigned int random_test_seed;
|
||||
/* A counter for the number of test requests completed */
|
||||
unsigned int completed_req_count;
|
||||
};
|
||||
|
||||
static struct ufs_test_data *utd;
|
||||
@@ -77,6 +98,12 @@ 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:
|
||||
return "UFS long sequential read test";
|
||||
break;
|
||||
case TEST_LONG_SEQUENTIAL_WRITE:
|
||||
return "UFS long sequential write test";
|
||||
break;
|
||||
default:
|
||||
return "Unknown test";
|
||||
}
|
||||
@@ -166,8 +193,9 @@ static int ufs_test_run_write_read_test(struct test_data *td)
|
||||
num_bios = DEFAULT_NUM_OF_BIOS;
|
||||
|
||||
/* Adding a write request */
|
||||
test_pr_info("%s: Adding a write requests to Q, first req_id=%d",
|
||||
__func__, td->wr_rd_next_req_id);
|
||||
test_pr_info(
|
||||
"%s: Adding a write request with %d bios to Q, req_id=%d"
|
||||
, __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,
|
||||
@@ -265,21 +293,290 @@ const struct file_operations write_read_test_ops = {
|
||||
.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)
|
||||
test_rq = (struct test_request *)rq->elv.priv[0];
|
||||
else {
|
||||
test_pr_err("%s: error: NULL request", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
BUG_ON(!test_rq);
|
||||
|
||||
spin_lock_irq(&ptd->lock);
|
||||
ptd->dispatched_count--;
|
||||
list_del_init(&test_rq->queuelist);
|
||||
__blk_put_request(ptd->req_q, test_rq->rq);
|
||||
spin_unlock_irq(&ptd->lock);
|
||||
|
||||
kfree(test_rq->bios_buffer);
|
||||
kfree(test_rq);
|
||||
utd->completed_req_count++;
|
||||
|
||||
test_pr_err("%s: request %d completed, err=%d",
|
||||
__func__, test_rq->req_id, err);
|
||||
|
||||
check_test_completion();
|
||||
|
||||
}
|
||||
|
||||
static int run_long_seq_test(struct test_data *td)
|
||||
{
|
||||
int ret = 0;
|
||||
int direction;
|
||||
static unsigned int inserted_requests;
|
||||
|
||||
BUG_ON(!td);
|
||||
td->test_count = 0;
|
||||
utd->completed_req_count = 0;
|
||||
inserted_requests = 0;
|
||||
|
||||
if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_READ)
|
||||
direction = READ;
|
||||
else
|
||||
direction = WRITE;
|
||||
|
||||
test_pr_info("%s: Adding %d requests, first req_id=%d",
|
||||
__func__, LONG_SEQ_TEST_NUM_REQS,
|
||||
td->wr_rd_next_req_id);
|
||||
|
||||
do {
|
||||
/*
|
||||
* since our requests come from a pool containing 128
|
||||
* requests, we don't want to exhaust this quantity,
|
||||
* therefore we add up to QUEUE_MAX_REQUESTS (which
|
||||
* includes a safety margin) and then call the mmc layer
|
||||
* to fetch them
|
||||
*/
|
||||
if (td->test_count >= QUEUE_MAX_REQUESTS) {
|
||||
blk_run_queue(td->req_q);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = test_iosched_add_wr_rd_test_req(0, direction,
|
||||
td->start_sector, TEST_MAX_BIOS_PER_REQ,
|
||||
TEST_PATTERN_5A,
|
||||
long_seq_test_free_end_io_fn);
|
||||
if (ret) {
|
||||
test_pr_err("%s: failed to create request" , __func__);
|
||||
break;
|
||||
}
|
||||
inserted_requests++;
|
||||
td->test_info.test_byte_count +=
|
||||
(TEST_MAX_BIOS_PER_REQ * sizeof(unsigned int) *
|
||||
BIO_U32_SIZE);
|
||||
|
||||
} while (inserted_requests < LONG_SEQ_TEST_NUM_REQS);
|
||||
|
||||
/* in this case the queue will not run in the above loop */
|
||||
if (LONG_SEQ_TEST_NUM_REQS < QUEUE_MAX_REQUESTS)
|
||||
blk_run_queue(td->req_q);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void long_seq_test_calc_throughput(unsigned long mtime,
|
||||
unsigned long byte_count)
|
||||
{
|
||||
unsigned long fraction, integer;
|
||||
|
||||
test_pr_info("%s: time is %lu msec, size is %lu.%lu MiB",
|
||||
__func__, mtime, LONG_TEST_SIZE_INTEGER(byte_count),
|
||||
LONG_TEST_SIZE_FRACTION(byte_count));
|
||||
|
||||
/* we first multiply in order not to lose precision */
|
||||
mtime *= MB_MSEC_RATIO_APPROXIMATION;
|
||||
/* divide values to get a MiB/sec integer value with one
|
||||
digit of precision
|
||||
*/
|
||||
fraction = integer = (byte_count * 10) / mtime;
|
||||
integer /= 10;
|
||||
/* and calculate the MiB value fraction */
|
||||
fraction -= integer * 10;
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
int number = -1;
|
||||
unsigned long mtime, byte_count;
|
||||
|
||||
test_pr_info("%s: -- UFS Long Sequential Write 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.get_test_case_str_fn = ufs_test_get_test_case_str;
|
||||
utd->test_info.run_test_fn = run_long_seq_test;
|
||||
utd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
|
||||
|
||||
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);
|
||||
|
||||
/* 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");
|
||||
|
||||
if (message_repeat == 1) {
|
||||
message_repeat = 0;
|
||||
return strnlen(buffer, count);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations long_sequential_write_test_ops = {
|
||||
.open = test_open,
|
||||
.write = long_sequential_write_test_write,
|
||||
.read = long_sequential_write_test_read,
|
||||
};
|
||||
|
||||
static void ufs_test_debugfs_cleanup(void)
|
||||
{
|
||||
debugfs_remove(utd->debug.write_read_test);
|
||||
debugfs_remove_recursive(test_iosched_get_debugfs_tests_root());
|
||||
}
|
||||
|
||||
static int ufs_test_debugfs_init(void)
|
||||
{
|
||||
struct dentry *utils_root, *tests_root;
|
||||
int ret = 0;
|
||||
|
||||
utils_root = test_iosched_get_debugfs_utils_root();
|
||||
tests_root = test_iosched_get_debugfs_tests_root();
|
||||
|
||||
if (!utils_root || !tests_root) {
|
||||
test_pr_err("%s: Failed to create debugfs root.", __func__);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
utd->debug.random_test_seed = debugfs_create_u32("random_test_seed",
|
||||
@@ -288,21 +585,49 @@ static int ufs_test_debugfs_init(void)
|
||||
if (!utd->debug.random_test_seed) {
|
||||
test_pr_err("%s: Could not create debugfs random_test_seed.",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
utd->debug.write_read_test = debugfs_create_file("write_read_test",
|
||||
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) {
|
||||
debugfs_remove(utd->debug.random_test_seed);
|
||||
test_pr_err("%s: Could not create debugfs write_read_test.",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
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;
|
||||
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;
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
exit_err:
|
||||
debugfs_remove_recursive(tests_root);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ufs_test_probe(void)
|
||||
|
||||
@@ -130,7 +130,7 @@ struct test_request {
|
||||
* @check_test_result_fn: Test specific test result checking
|
||||
* callback
|
||||
* @get_test_case_str_fn: Test specific function to get the test name
|
||||
* @test_duration: A jiffies value saved for timing
|
||||
* @test_duration: A ktime value saved for timing
|
||||
* calculations
|
||||
* @data: Test specific private data
|
||||
* @test_byte_count: Total number of bytes dispatched in
|
||||
@@ -263,4 +263,6 @@ extern struct test_data *test_get_test_data(void);
|
||||
void test_iosched_add_urgent_req(struct test_request *test_rq);
|
||||
|
||||
int test_is_req_urgent(struct request *rq);
|
||||
|
||||
void check_test_completion(void);
|
||||
#endif /* _LINUX_TEST_IOSCHED_H */
|
||||
|
||||
Reference in New Issue
Block a user