mmc: mmc_test: add test for non-blocking transfers
Add four tests for read and write performance per different transfer size, 4k to 4M. * Read using blocking mmc request * Read using non-blocking mmc request * Write using blocking mmc request * Write using non-blocking mmc request The host driver must support pre_req() and post_req() in order to run the non-blocking test cases. Signed-off-by: Per Forlin <per.forlin@linaro.org> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Venkatraman S <svenkatr@ti.com> Tested-by: Sourav Poddar<sourav.poddar@ti.com> Tested-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
parent
54f3caf5bc
commit
9f9c4180f8
@ -148,6 +148,26 @@ struct mmc_test_card {
|
|||||||
struct mmc_test_general_result *gr;
|
struct mmc_test_general_result *gr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum mmc_test_prep_media {
|
||||||
|
MMC_TEST_PREP_NONE = 0,
|
||||||
|
MMC_TEST_PREP_WRITE_FULL = 1 << 0,
|
||||||
|
MMC_TEST_PREP_ERASE = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mmc_test_multiple_rw {
|
||||||
|
unsigned int *bs;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned int size;
|
||||||
|
bool do_write;
|
||||||
|
bool do_nonblock_req;
|
||||||
|
enum mmc_test_prep_media prepare;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mmc_test_async_req {
|
||||||
|
struct mmc_async_req areq;
|
||||||
|
struct mmc_test_card *test;
|
||||||
|
};
|
||||||
|
|
||||||
/*******************************************************************/
|
/*******************************************************************/
|
||||||
/* General helper functions */
|
/* General helper functions */
|
||||||
/*******************************************************************/
|
/*******************************************************************/
|
||||||
@ -661,7 +681,7 @@ static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
|
|||||||
* Checks that a normal transfer didn't have any errors
|
* Checks that a normal transfer didn't have any errors
|
||||||
*/
|
*/
|
||||||
static int mmc_test_check_result(struct mmc_test_card *test,
|
static int mmc_test_check_result(struct mmc_test_card *test,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -685,6 +705,17 @@ static int mmc_test_check_result(struct mmc_test_card *test,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_test_check_result_async(struct mmc_card *card,
|
||||||
|
struct mmc_async_req *areq)
|
||||||
|
{
|
||||||
|
struct mmc_test_async_req *test_async =
|
||||||
|
container_of(areq, struct mmc_test_async_req, areq);
|
||||||
|
|
||||||
|
mmc_test_wait_busy(test_async->test);
|
||||||
|
|
||||||
|
return mmc_test_check_result(test_async->test, areq->mrq);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks that a "short transfer" behaved as expected
|
* Checks that a "short transfer" behaved as expected
|
||||||
*/
|
*/
|
||||||
@ -719,6 +750,85 @@ static int mmc_test_check_broken_result(struct mmc_test_card *test,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests nonblock transfer with certain parameters
|
||||||
|
*/
|
||||||
|
static void mmc_test_nonblock_reset(struct mmc_request *mrq,
|
||||||
|
struct mmc_command *cmd,
|
||||||
|
struct mmc_command *stop,
|
||||||
|
struct mmc_data *data)
|
||||||
|
{
|
||||||
|
memset(mrq, 0, sizeof(struct mmc_request));
|
||||||
|
memset(cmd, 0, sizeof(struct mmc_command));
|
||||||
|
memset(data, 0, sizeof(struct mmc_data));
|
||||||
|
memset(stop, 0, sizeof(struct mmc_command));
|
||||||
|
|
||||||
|
mrq->cmd = cmd;
|
||||||
|
mrq->data = data;
|
||||||
|
mrq->stop = stop;
|
||||||
|
}
|
||||||
|
static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
|
||||||
|
struct scatterlist *sg, unsigned sg_len,
|
||||||
|
unsigned dev_addr, unsigned blocks,
|
||||||
|
unsigned blksz, int write, int count)
|
||||||
|
{
|
||||||
|
struct mmc_request mrq1;
|
||||||
|
struct mmc_command cmd1;
|
||||||
|
struct mmc_command stop1;
|
||||||
|
struct mmc_data data1;
|
||||||
|
|
||||||
|
struct mmc_request mrq2;
|
||||||
|
struct mmc_command cmd2;
|
||||||
|
struct mmc_command stop2;
|
||||||
|
struct mmc_data data2;
|
||||||
|
|
||||||
|
struct mmc_test_async_req test_areq[2];
|
||||||
|
struct mmc_async_req *done_areq;
|
||||||
|
struct mmc_async_req *cur_areq = &test_areq[0].areq;
|
||||||
|
struct mmc_async_req *other_areq = &test_areq[1].areq;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
test_areq[0].test = test;
|
||||||
|
test_areq[1].test = test;
|
||||||
|
|
||||||
|
mmc_test_nonblock_reset(&mrq1, &cmd1, &stop1, &data1);
|
||||||
|
mmc_test_nonblock_reset(&mrq2, &cmd2, &stop2, &data2);
|
||||||
|
|
||||||
|
cur_areq->mrq = &mrq1;
|
||||||
|
cur_areq->err_check = mmc_test_check_result_async;
|
||||||
|
other_areq->mrq = &mrq2;
|
||||||
|
other_areq->err_check = mmc_test_check_result_async;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
mmc_test_prepare_mrq(test, cur_areq->mrq, sg, sg_len, dev_addr,
|
||||||
|
blocks, blksz, write);
|
||||||
|
done_areq = mmc_start_req(test->card->host, cur_areq, &ret);
|
||||||
|
|
||||||
|
if (ret || (!done_areq && i > 0))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (done_areq) {
|
||||||
|
if (done_areq->mrq == &mrq2)
|
||||||
|
mmc_test_nonblock_reset(&mrq2, &cmd2,
|
||||||
|
&stop2, &data2);
|
||||||
|
else
|
||||||
|
mmc_test_nonblock_reset(&mrq1, &cmd1,
|
||||||
|
&stop1, &data1);
|
||||||
|
}
|
||||||
|
done_areq = cur_areq;
|
||||||
|
cur_areq = other_areq;
|
||||||
|
other_areq = done_areq;
|
||||||
|
dev_addr += blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
done_areq = mmc_start_req(test->card->host, NULL, &ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tests a basic transfer with certain parameters
|
* Tests a basic transfer with certain parameters
|
||||||
*/
|
*/
|
||||||
@ -1336,14 +1446,17 @@ static int mmc_test_area_transfer(struct mmc_test_card *test,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Map and transfer bytes.
|
* Map and transfer bytes for multiple transfers.
|
||||||
*/
|
*/
|
||||||
static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
|
||||||
unsigned int dev_addr, int write, int max_scatter,
|
unsigned int dev_addr, int write,
|
||||||
int timed)
|
int max_scatter, int timed, int count,
|
||||||
|
bool nonblock)
|
||||||
{
|
{
|
||||||
struct timespec ts1, ts2;
|
struct timespec ts1, ts2;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
struct mmc_test_area *t = &test->area;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the case of a maximally scattered transfer, the maximum transfer
|
* In the case of a maximally scattered transfer, the maximum transfer
|
||||||
@ -1367,8 +1480,15 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
|||||||
|
|
||||||
if (timed)
|
if (timed)
|
||||||
getnstimeofday(&ts1);
|
getnstimeofday(&ts1);
|
||||||
|
if (nonblock)
|
||||||
|
ret = mmc_test_nonblock_transfer(test, t->sg, t->sg_len,
|
||||||
|
dev_addr, t->blocks, 512, write, count);
|
||||||
|
else
|
||||||
|
for (i = 0; i < count && ret == 0; i++) {
|
||||||
|
ret = mmc_test_area_transfer(test, dev_addr, write);
|
||||||
|
dev_addr += sz >> 9;
|
||||||
|
}
|
||||||
|
|
||||||
ret = mmc_test_area_transfer(test, dev_addr, write);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1376,11 +1496,19 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
|||||||
getnstimeofday(&ts2);
|
getnstimeofday(&ts2);
|
||||||
|
|
||||||
if (timed)
|
if (timed)
|
||||||
mmc_test_print_rate(test, sz, &ts1, &ts2);
|
mmc_test_print_avg_rate(test, sz, count, &ts1, &ts2);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
||||||
|
unsigned int dev_addr, int write, int max_scatter,
|
||||||
|
int timed)
|
||||||
|
{
|
||||||
|
return mmc_test_area_io_seq(test, sz, dev_addr, write, max_scatter,
|
||||||
|
timed, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the test area entirely.
|
* Write the test area entirely.
|
||||||
*/
|
*/
|
||||||
@ -1954,6 +2082,153 @@ static int mmc_test_large_seq_write_perf(struct mmc_test_card *test)
|
|||||||
return mmc_test_large_seq_perf(test, 1);
|
return mmc_test_large_seq_perf(test, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mmc_test_rw_multiple(struct mmc_test_card *test,
|
||||||
|
struct mmc_test_multiple_rw *tdata,
|
||||||
|
unsigned int reqsize, unsigned int size)
|
||||||
|
{
|
||||||
|
unsigned int dev_addr;
|
||||||
|
struct mmc_test_area *t = &test->area;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Set up test area */
|
||||||
|
if (size > mmc_test_capacity(test->card) / 2 * 512)
|
||||||
|
size = mmc_test_capacity(test->card) / 2 * 512;
|
||||||
|
if (reqsize > t->max_tfr)
|
||||||
|
reqsize = t->max_tfr;
|
||||||
|
dev_addr = mmc_test_capacity(test->card) / 4;
|
||||||
|
if ((dev_addr & 0xffff0000))
|
||||||
|
dev_addr &= 0xffff0000; /* Round to 64MiB boundary */
|
||||||
|
else
|
||||||
|
dev_addr &= 0xfffff800; /* Round to 1MiB boundary */
|
||||||
|
if (!dev_addr)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (reqsize > size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* prepare test area */
|
||||||
|
if (mmc_can_erase(test->card) &&
|
||||||
|
tdata->prepare & MMC_TEST_PREP_ERASE) {
|
||||||
|
ret = mmc_erase(test->card, dev_addr,
|
||||||
|
size / 512, MMC_SECURE_ERASE_ARG);
|
||||||
|
if (ret)
|
||||||
|
ret = mmc_erase(test->card, dev_addr,
|
||||||
|
size / 512, MMC_ERASE_ARG);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run test */
|
||||||
|
ret = mmc_test_area_io_seq(test, reqsize, dev_addr,
|
||||||
|
tdata->do_write, 0, 1, size / reqsize,
|
||||||
|
tdata->do_nonblock_req);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
err:
|
||||||
|
printk(KERN_INFO "[%s] error\n", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mmc_test_rw_multiple_size(struct mmc_test_card *test,
|
||||||
|
struct mmc_test_multiple_rw *rw)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
void *pre_req = test->card->host->ops->pre_req;
|
||||||
|
void *post_req = test->card->host->ops->post_req;
|
||||||
|
|
||||||
|
if (rw->do_nonblock_req &&
|
||||||
|
((!pre_req && post_req) || (pre_req && !post_req))) {
|
||||||
|
printk(KERN_INFO "error: only one of pre/post is defined\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < rw->len && ret == 0; i++) {
|
||||||
|
ret = mmc_test_rw_multiple(test, rw, rw->bs[i], rw->size);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiple blocking write 4k to 4 MB chunks
|
||||||
|
*/
|
||||||
|
static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
|
||||||
|
{
|
||||||
|
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||||
|
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||||
|
struct mmc_test_multiple_rw test_data = {
|
||||||
|
.bs = bs,
|
||||||
|
.size = TEST_AREA_MAX_SIZE,
|
||||||
|
.len = ARRAY_SIZE(bs),
|
||||||
|
.do_write = true,
|
||||||
|
.do_nonblock_req = false,
|
||||||
|
.prepare = MMC_TEST_PREP_ERASE,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mmc_test_rw_multiple_size(test, &test_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiple non-blocking write 4k to 4 MB chunks
|
||||||
|
*/
|
||||||
|
static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
|
||||||
|
{
|
||||||
|
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||||
|
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||||
|
struct mmc_test_multiple_rw test_data = {
|
||||||
|
.bs = bs,
|
||||||
|
.size = TEST_AREA_MAX_SIZE,
|
||||||
|
.len = ARRAY_SIZE(bs),
|
||||||
|
.do_write = true,
|
||||||
|
.do_nonblock_req = true,
|
||||||
|
.prepare = MMC_TEST_PREP_ERASE,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mmc_test_rw_multiple_size(test, &test_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiple blocking read 4k to 4 MB chunks
|
||||||
|
*/
|
||||||
|
static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
|
||||||
|
{
|
||||||
|
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||||
|
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||||
|
struct mmc_test_multiple_rw test_data = {
|
||||||
|
.bs = bs,
|
||||||
|
.size = TEST_AREA_MAX_SIZE,
|
||||||
|
.len = ARRAY_SIZE(bs),
|
||||||
|
.do_write = false,
|
||||||
|
.do_nonblock_req = false,
|
||||||
|
.prepare = MMC_TEST_PREP_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mmc_test_rw_multiple_size(test, &test_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiple non-blocking read 4k to 4 MB chunks
|
||||||
|
*/
|
||||||
|
static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
|
||||||
|
{
|
||||||
|
unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
|
||||||
|
1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
|
||||||
|
struct mmc_test_multiple_rw test_data = {
|
||||||
|
.bs = bs,
|
||||||
|
.size = TEST_AREA_MAX_SIZE,
|
||||||
|
.len = ARRAY_SIZE(bs),
|
||||||
|
.do_write = false,
|
||||||
|
.do_nonblock_req = true,
|
||||||
|
.prepare = MMC_TEST_PREP_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
return mmc_test_rw_multiple_size(test, &test_data);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_test_case mmc_test_cases[] = {
|
static const struct mmc_test_case mmc_test_cases[] = {
|
||||||
{
|
{
|
||||||
.name = "Basic write (no data verification)",
|
.name = "Basic write (no data verification)",
|
||||||
@ -2221,6 +2496,33 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
|||||||
.cleanup = mmc_test_area_cleanup,
|
.cleanup = mmc_test_area_cleanup,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "Write performance with blocking req 4k to 4MB",
|
||||||
|
.prepare = mmc_test_area_prepare,
|
||||||
|
.run = mmc_test_profile_mult_write_blocking_perf,
|
||||||
|
.cleanup = mmc_test_area_cleanup,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "Write performance with non-blocking req 4k to 4MB",
|
||||||
|
.prepare = mmc_test_area_prepare,
|
||||||
|
.run = mmc_test_profile_mult_write_nonblock_perf,
|
||||||
|
.cleanup = mmc_test_area_cleanup,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "Read performance with blocking req 4k to 4MB",
|
||||||
|
.prepare = mmc_test_area_prepare,
|
||||||
|
.run = mmc_test_profile_mult_read_blocking_perf,
|
||||||
|
.cleanup = mmc_test_area_cleanup,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "Read performance with non-blocking req 4k to 4MB",
|
||||||
|
.prepare = mmc_test_area_prepare,
|
||||||
|
.run = mmc_test_profile_mult_read_nonblock_perf,
|
||||||
|
.cleanup = mmc_test_area_cleanup,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_MUTEX(mmc_test_lock);
|
static DEFINE_MUTEX(mmc_test_lock);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user