Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f9c4180 authored by Per Forlin's avatar Per Forlin Committed by Chris Ball
Browse files

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: default avatarPer Forlin <per.forlin@linaro.org>
Acked-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Reviewed-by: default avatarVenkatraman S <svenkatr@ti.com>
Tested-by: default avatarSourav <Poddar&lt;sourav.poddar@ti.com>
Tested-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 54f3caf5
Loading
Loading
Loading
Loading
+310 −8
Original line number Diff line number Diff line
@@ -148,6 +148,26 @@ struct mmc_test_card {
	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                                       */
/*******************************************************************/
@@ -685,6 +705,17 @@ static int mmc_test_check_result(struct mmc_test_card *test,
	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
 */
@@ -719,6 +750,85 @@ static int mmc_test_check_broken_result(struct mmc_test_card *test,
	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
 */
@@ -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,
			    unsigned int dev_addr, int write, int max_scatter,
			    int timed)
static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
				unsigned int dev_addr, int write,
				int max_scatter, int timed, int count,
				bool nonblock)
{
	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
@@ -1367,8 +1480,15 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,

	if (timed)
		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;
		}

	if (ret)
		return ret;

@@ -1376,11 +1496,19 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
		getnstimeofday(&ts2);

	if (timed)
		mmc_test_print_rate(test, sz, &ts1, &ts2);
		mmc_test_print_avg_rate(test, sz, count, &ts1, &ts2);

	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.
 */
@@ -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);
}

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[] = {
	{
		.name = "Basic write (no data verification)",
@@ -2221,6 +2496,33 @@ static const struct mmc_test_case mmc_test_cases[] = {
		.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);