summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilad Broner <gbroner@codeaurora.org>2015-01-20 15:49:05 +0200
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:01:57 -0700
commit60f3461f17b575ac2252d8171507791ce848b37a (patch)
treeb1a1a397ee3fa5151636c67c180f4ba87ae48748
parentddb20cd6f07fd03283b2a28eba323bdb96a06d34 (diff)
block: test-iosched: enable running of simultaneous tests
Current test-iosched design enables running only a single test for a single block device. This change modifies the test-iosched framework to allow running several tests on several block devices. Change-Id: I051d842733873488b64e89053d9c4e30e1249870 Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [merez@codeaurora.org: fix conflicts due to removal of BKOPs UT] Signed-off-by: Maya Erez <merez@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> [venkatg@codeaurora.org: Drop changes to ufs_test.c and mmc_block_test.c] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
-rw-r--r--block/test-iosched.c604
-rw-r--r--include/linux/test-iosched.h84
2 files changed, 358 insertions, 330 deletions
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 9ac5d1441e0b..f63c6a21686e 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -36,40 +36,27 @@
#define TIMEOUT_TIMER_MS 40000
#define TEST_MAX_TESTCASE_ROUNDS 15
-static DEFINE_SPINLOCK(blk_dev_test_list_lock);
-static LIST_HEAD(blk_dev_test_list);
-static struct test_data *ptd;
-
-/**
- * test_iosched_get_req_queue() - returns the request queue
- * served by the scheduler
- */
-struct request_queue *test_iosched_get_req_queue(void)
-{
- if (!ptd)
- return NULL;
+static DEFINE_MUTEX(blk_dev_test_list_lock);
+static LIST_HEAD(blk_dev_test_list);
- return ptd->req_q;
-}
-EXPORT_SYMBOL(test_iosched_get_req_queue);
/**
* test_iosched_mark_test_completion() - Wakeup the debugfs
* thread, waiting on the test completion
*/
-void test_iosched_mark_test_completion(void)
+void test_iosched_mark_test_completion(struct test_iosched *tios)
{
- if (!ptd)
+ if (!tios)
return;
pr_info("%s: mark test is completed, test_count=%d, ", __func__,
- ptd->test_count);
+ tios->test_count);
pr_info("%s: urgent_count=%d, reinsert_count=%d,", __func__,
- ptd->urgent_count, ptd->reinsert_count);
+ tios->urgent_count, tios->reinsert_count);
- ptd->test_state = TEST_COMPLETED;
- wake_up(&ptd->wait_q);
+ tios->test_state = TEST_COMPLETED;
+ wake_up(&tios->wait_q);
}
EXPORT_SYMBOL(test_iosched_mark_test_completion);
@@ -77,36 +64,37 @@ EXPORT_SYMBOL(test_iosched_mark_test_completion);
* check_test_completion() - Check if all the queued test
* requests were completed
*/
-void check_test_completion(void)
+void check_test_completion(struct test_iosched *tios)
{
struct test_request *test_rq;
- if (!ptd)
+ if (!tios)
goto exit;
- if (ptd->test_info.check_test_completion_fn &&
- !ptd->test_info.check_test_completion_fn())
+ if (tios->test_info.check_test_completion_fn &&
+ !tios->test_info.check_test_completion_fn(tios))
goto exit;
- list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist)
+ list_for_each_entry(test_rq, &tios->dispatched_queue, queuelist)
if (!test_rq->req_completed)
goto exit;
- if (!list_empty(&ptd->test_queue)
- || !list_empty(&ptd->reinsert_queue)
- || !list_empty(&ptd->urgent_queue)) {
+ if (!list_empty(&tios->test_queue)
+ || !list_empty(&tios->reinsert_queue)
+ || !list_empty(&tios->urgent_queue)) {
pr_info("%s: Test still not completed,", __func__);
- pr_info("%s: test_count=%d, reinsert_count=%d",
- __func__, ptd->test_count, ptd->reinsert_count);
- pr_info("%s: dispatched_count=%d, urgent_count=%d",
- __func__, ptd->dispatched_count, ptd->urgent_count);
+ pr_info("%s: test_count=%d, reinsert_count=%d", __func__,
+ tios->test_count, tios->reinsert_count);
+ pr_info("%s: dispatched_count=%d, urgent_count=%d", __func__,
+ tios->dispatched_count,
+ tios->urgent_count);
goto exit;
}
- ptd->test_info.test_duration = ktime_sub(ktime_get(),
- ptd->test_info.test_duration);
+ tios->test_info.test_duration = ktime_sub(ktime_get(),
+ tios->test_info.test_duration);
- test_iosched_mark_test_completion();
+ test_iosched_mark_test_completion(tios);
exit:
return;
@@ -132,7 +120,7 @@ static void end_test_bio(struct bio *bio, int err)
static void end_test_req(struct request *rq, int err)
{
struct test_request *test_rq;
-
+ struct test_iosched *tios = rq->q->elevator->elevator_data;
test_rq = (struct test_request *)rq->elv.priv[0];
BUG_ON(!test_rq);
@@ -142,7 +130,7 @@ static void end_test_req(struct request *rq, int err)
test_rq->req_completed = true;
test_rq->req_result = err;
- check_test_completion();
+ check_test_completion(tios);
}
/**
@@ -156,16 +144,16 @@ static void end_test_req(struct request *rq, int err)
* @end_req_io: specific completion callback. When not
* set, the defaulcallback will be used
*/
-int test_iosched_add_unique_test_req(int is_err_expcted,
- enum req_unique_type req_unique,
- int start_sec, int nr_sects, rq_end_io_fn *end_req_io)
+int test_iosched_add_unique_test_req(struct test_iosched *tios,
+ int is_err_expcted, enum req_unique_type req_unique,
+ int start_sec, int nr_sects, rq_end_io_fn *end_req_io)
{
struct bio *bio;
struct request *rq;
int rw_flags;
struct test_request *test_rq;
- if (!ptd)
+ if (!tios)
return -ENODEV;
bio = bio_alloc(GFP_KERNEL, 0);
@@ -196,7 +184,7 @@ int test_iosched_add_unique_test_req(int is_err_expcted,
if (bio->bi_rw & REQ_SYNC)
rw_flags |= REQ_SYNC;
- rq = blk_get_request(ptd->req_q, rw_flags, GFP_KERNEL);
+ rq = blk_get_request(tios->req_q, rw_flags, GFP_KERNEL);
if (!rq) {
pr_err("%s: Failed to allocate a request", __func__);
bio_put(bio);
@@ -221,16 +209,16 @@ int test_iosched_add_unique_test_req(int is_err_expcted,
test_rq->rq = rq;
test_rq->is_err_expected = is_err_expcted;
rq->elv.priv[0] = (void *)test_rq;
- test_rq->req_id = ptd->unique_next_req_id++;
+ test_rq->req_id = tios->unique_next_req_id++;
pr_debug(
"%s: added request %d to the test requests list, type = %d",
__func__, test_rq->req_id, req_unique);
- spin_lock_irq(ptd->req_q->queue_lock);
- list_add_tail(&test_rq->queuelist, &ptd->test_queue);
- ptd->test_count++;
- spin_unlock_irq(ptd->req_q->queue_lock);
+ spin_lock_irq(tios->req_q->queue_lock);
+ list_add_tail(&test_rq->queuelist, &tios->test_queue);
+ tios->test_count++;
+ spin_unlock_irq(tios->req_q->queue_lock);
return 0;
}
@@ -286,9 +274,10 @@ static void fill_buf_with_pattern(int *buf, int num_bytes, int pattern)
* request memory is freed at the end of the test and the
* allocated BIO memory is freed by end_test_bio.
*/
-struct test_request *test_iosched_create_test_req(int is_err_expcted,
- int direction, int start_sec,
- int num_bios, int pattern, rq_end_io_fn *end_req_io)
+struct test_request *test_iosched_create_test_req(
+ struct test_iosched *tios, int is_err_expcted,
+ int direction, int start_sec, int num_bios, int pattern,
+ rq_end_io_fn *end_req_io)
{
struct request *rq;
struct test_request *test_rq;
@@ -297,12 +286,12 @@ struct test_request *test_iosched_create_test_req(int is_err_expcted,
unsigned int *bio_ptr = NULL;
struct bio *bio = NULL;
- if (!ptd)
+ if (!tios)
return NULL;
rw_flags = direction;
- rq = blk_get_request(ptd->req_q, rw_flags, GFP_KERNEL);
+ rq = blk_get_request(tios->req_q, rw_flags, GFP_KERNEL);
if (!rq) {
pr_err("%s: Failed to allocate a request", __func__);
return NULL;
@@ -330,7 +319,7 @@ struct test_request *test_iosched_create_test_req(int is_err_expcted,
bio_ptr = test_rq->bios_buffer;
for (i = 0; i < num_bios; ++i) {
- ret = blk_rq_map_kern(ptd->req_q, rq,
+ ret = blk_rq_map_kern(tios->req_q, rq,
(void *)bio_ptr,
sizeof(unsigned int)*BIO_U32_SIZE,
GFP_KERNEL);
@@ -359,14 +348,15 @@ struct test_request *test_iosched_create_test_req(int is_err_expcted,
bio->bi_end_io = end_test_bio;
}
- ptd->num_of_write_bios += num_bios;
- test_rq->req_id = ptd->wr_rd_next_req_id++;
+ tios->num_of_write_bios += num_bios;
+ test_rq->req_id = tios->wr_rd_next_req_id++;
test_rq->req_completed = false;
test_rq->req_result = -EINVAL;
test_rq->rq = rq;
- if (ptd->test_info.get_rq_disk_fn)
- test_rq->rq->rq_disk = ptd->test_info.get_rq_disk_fn();
+ if (tios->test_info.get_rq_disk_fn)
+ test_rq->rq->rq_disk =
+ tios->test_info.get_rq_disk_fn(tios);
test_rq->is_err_expected = is_err_expcted;
rq->elv.priv[0] = (void *)test_rq;
@@ -409,20 +399,19 @@ EXPORT_SYMBOL(test_iosched_create_test_req);
* memory is freed at the end of the test and the allocated BIO
* memory is freed by end_test_bio.
*/
-int test_iosched_add_wr_rd_test_req(int is_err_expcted,
- int direction, int start_sec,
- int num_bios, int pattern, rq_end_io_fn *end_req_io)
+int test_iosched_add_wr_rd_test_req(struct test_iosched *tios,
+ int is_err_expcted, int direction, int start_sec, int num_bios,
+ int pattern, rq_end_io_fn *end_req_io)
{
struct test_request *test_rq = NULL;
- test_rq = test_iosched_create_test_req(is_err_expcted,
- direction, start_sec,
- num_bios, pattern, end_req_io);
+ test_rq = test_iosched_create_test_req(tios, is_err_expcted, direction,
+ start_sec, num_bios, pattern, end_req_io);
if (test_rq) {
- spin_lock_irq(ptd->req_q->queue_lock);
- list_add_tail(&test_rq->queuelist, &ptd->test_queue);
- ptd->test_count++;
- spin_unlock_irq(ptd->req_q->queue_lock);
+ spin_lock_irq(tios->req_q->queue_lock);
+ list_add_tail(&test_rq->queuelist, &tios->test_queue);
+ tios->test_count++;
+ spin_unlock_irq(tios->req_q->queue_lock);
return 0;
}
return -ENODEV;
@@ -430,10 +419,11 @@ int test_iosched_add_wr_rd_test_req(int is_err_expcted,
EXPORT_SYMBOL(test_iosched_add_wr_rd_test_req);
/* Converts the testcase number into a string */
-static char *get_test_case_str(struct test_data *td)
+static char *get_test_case_str(struct test_iosched *tios)
{
- if (td->test_info.get_test_case_str_fn)
- return td->test_info.get_test_case_str_fn(td);
+ if (tios->test_info.get_test_case_str_fn)
+ return tios->test_info.get_test_case_str_fn(
+ tios->test_info.testcase);
return "Unknown testcase";
}
@@ -485,43 +475,40 @@ EXPORT_SYMBOL(compare_buffer_to_pattern);
* check_testcase_result for result checking that are specific
* to a test case.
*/
-static int check_test_result(struct test_data *td)
+static int check_test_result(struct test_iosched *tios)
{
- struct test_request *test_rq;
+ struct test_request *trq;
int res = 0;
static int run;
- if (!ptd)
- goto err;
-
- list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist) {
- if (!test_rq->rq) {
+ list_for_each_entry(trq, &tios->dispatched_queue, queuelist) {
+ if (!trq->rq) {
pr_info("%s: req_id %d is contains empty req",
- __func__, test_rq->req_id);
+ __func__, trq->req_id);
continue;
}
- if (!test_rq->req_completed) {
+ if (!trq->req_completed) {
pr_err("%s: rq %d not completed", __func__,
- test_rq->req_id);
+ trq->req_id);
res = -EINVAL;
goto err;
}
- if ((test_rq->req_result < 0) && !test_rq->is_err_expected) {
+ if ((trq->req_result < 0) && !trq->is_err_expected) {
pr_err(
"%s: rq %d completed with err, not as expected",
- __func__, test_rq->req_id);
+ __func__, trq->req_id);
res = -EINVAL;
goto err;
}
- if ((test_rq->req_result == 0) && test_rq->is_err_expected) {
+ if ((trq->req_result == 0) && trq->is_err_expected) {
pr_err("%s: rq %d succeeded, not as expected",
- __func__, test_rq->req_id);
+ __func__, trq->req_id);
res = -EINVAL;
goto err;
}
- if (rq_data_dir(test_rq->rq) == READ) {
- res = compare_buffer_to_pattern(test_rq);
+ if (rq_data_dir(trq->rq) == READ) {
+ res = compare_buffer_to_pattern(trq);
if (res) {
pr_err("%s: read pattern not as expected",
__func__);
@@ -531,31 +518,32 @@ static int check_test_result(struct test_data *td)
}
}
- if (td->test_info.check_test_result_fn) {
- res = td->test_info.check_test_result_fn(td);
+ if (tios->test_info.check_test_result_fn) {
+ res = tios->test_info.check_test_result_fn(
+ tios);
if (res)
goto err;
}
pr_info("%s: %s, run# %03d, PASSED",
- __func__, get_test_case_str(td), ++run);
- td->test_result = TEST_PASSED;
+ __func__, get_test_case_str(tios), ++run);
+ tios->test_result = TEST_PASSED;
return 0;
err:
pr_err("%s: %s, run# %03d, FAILED",
- __func__, get_test_case_str(td), ++run);
- td->test_result = TEST_FAILED;
+ __func__, get_test_case_str(tios), ++run);
+ tios->test_result = TEST_FAILED;
return res;
}
/* Create and queue the required requests according to the test case */
-static int prepare_test(struct test_data *td)
+static int prepare_test(struct test_iosched *tios)
{
int ret = 0;
- if (td->test_info.prepare_test_fn) {
- ret = td->test_info.prepare_test_fn(td);
+ if (tios->test_info.prepare_test_fn) {
+ ret = tios->test_info.prepare_test_fn(tios);
return ret;
}
@@ -563,16 +551,16 @@ static int prepare_test(struct test_data *td)
}
/* Run the test */
-static int run_test(struct test_data *td)
+static int run_test(struct test_iosched *tios)
{
int ret = 0;
- if (td->test_info.run_test_fn) {
- ret = td->test_info.run_test_fn(td);
+ if (tios->test_info.run_test_fn) {
+ ret = tios->test_info.run_test_fn(tios);
return ret;
}
- blk_run_queue(td->req_q);
+ blk_run_queue(tios->req_q);
return 0;
}
@@ -618,26 +606,26 @@ static void free_test_queue(struct list_head *test_queue)
* @td The test_data struct whos test requests will be
* freed.
*/
-static void free_test_requests(struct test_data *td)
+static void free_test_requests(struct test_iosched *tios)
{
- if (!td)
+ if (!tios)
return;
- if (td->urgent_count) {
- free_test_queue(&td->urgent_queue);
- td->urgent_count = 0;
+ if (tios->urgent_count) {
+ free_test_queue(&tios->urgent_queue);
+ tios->urgent_count = 0;
}
- if (td->test_count) {
- free_test_queue(&td->test_queue);
- td->test_count = 0;
+ if (tios->test_count) {
+ free_test_queue(&tios->test_queue);
+ tios->test_count = 0;
}
- if (td->dispatched_count) {
- free_test_queue(&td->dispatched_queue);
- td->dispatched_count = 0;
+ if (tios->dispatched_count) {
+ free_test_queue(&tios->dispatched_queue);
+ tios->dispatched_count = 0;
}
- if (td->reinsert_count) {
- free_test_queue(&td->reinsert_queue);
- td->reinsert_count = 0;
+ if (tios->reinsert_count) {
+ free_test_queue(&tios->reinsert_queue);
+ tios->reinsert_count = 0;
}
}
@@ -647,27 +635,26 @@ static void free_test_requests(struct test_data *td)
* @td The test_data struct for the test that has
* ended.
*/
-static int post_test(struct test_data *td)
+static int post_test(struct test_iosched *tios)
{
int ret = 0;
- if (td->test_info.post_test_fn)
- ret = td->test_info.post_test_fn(td);
+ if (tios->test_info.post_test_fn)
+ ret = tios->test_info.post_test_fn(tios);
- ptd->test_info.testcase = 0;
- ptd->test_state = TEST_IDLE;
+ tios->test_info.testcase = 0;
+ tios->test_state = TEST_IDLE;
- free_test_requests(td);
+ free_test_requests(tios);
return ret;
}
-static unsigned int get_timeout_msec(struct test_data *td)
+static unsigned int get_timeout_msec(struct test_iosched *tios)
{
- if (td->test_info.timeout_msec)
- return td->test_info.timeout_msec;
- else
- return TIMEOUT_TIMER_MS;
+ if (tios->test_info.timeout_msec)
+ return tios->test_info.timeout_msec;
+ return TIMEOUT_TIMER_MS;
}
/**
@@ -679,83 +666,86 @@ static unsigned int get_timeout_msec(struct test_data *td)
*
* The function also checks the test result upon test completion
*/
-int test_iosched_start_test(struct test_info *t_info)
+int test_iosched_start_test(struct test_iosched *tios,
+ struct test_info *t_info)
{
int ret = 0;
- unsigned timeout_msec;
+ unsigned long timeout;
int counter = 0;
char *test_name = NULL;
- if (!ptd)
+ if (!tios)
return -ENODEV;
if (!t_info) {
- ptd->test_result = TEST_FAILED;
+ tios->test_result = TEST_FAILED;
return -EINVAL;
}
+ timeout = msecs_to_jiffies(get_timeout_msec(tios));
+
do {
- if (ptd->ignore_round)
+ if (tios->ignore_round)
/*
* We ignored the last run due to FS write requests.
* Sleep to allow those requests to be issued
*/
msleep(2000);
- spin_lock(&ptd->lock);
+ spin_lock(&tios->lock);
- if (ptd->test_state != TEST_IDLE) {
+ if (tios->test_state != TEST_IDLE) {
pr_info(
"%s: Another test is running, try again later",
__func__);
- spin_unlock(&ptd->lock);
+ spin_unlock(&tios->lock);
return -EBUSY;
}
- if (ptd->start_sector == 0) {
+ if (tios->start_sector == 0) {
pr_err("%s: Invalid start sector", __func__);
- ptd->test_result = TEST_FAILED;
- spin_unlock(&ptd->lock);
+ tios->test_result = TEST_FAILED;
+ spin_unlock(&tios->lock);
return -EINVAL;
}
- memcpy(&ptd->test_info, t_info, sizeof(struct test_info));
+ memcpy(&tios->test_info, t_info, sizeof(*t_info));
- ptd->test_result = TEST_NO_RESULT;
- ptd->num_of_write_bios = 0;
+ tios->test_result = TEST_NO_RESULT;
+ tios->num_of_write_bios = 0;
- ptd->unique_next_req_id = UNIQUE_START_REQ_ID;
- ptd->wr_rd_next_req_id = WR_RD_START_REQ_ID;
+ tios->unique_next_req_id = UNIQUE_START_REQ_ID;
+ tios->wr_rd_next_req_id = WR_RD_START_REQ_ID;
- ptd->ignore_round = false;
- ptd->fs_wr_reqs_during_test = false;
+ tios->ignore_round = false;
+ tios->fs_wr_reqs_during_test = false;
- ptd->test_state = TEST_RUNNING;
+ tios->test_state = TEST_RUNNING;
- spin_unlock(&ptd->lock);
+ spin_unlock(&tios->lock);
/*
* Give an already dispatch request from
* FS a chanse to complete
*/
msleep(2000);
- timeout_msec = get_timeout_msec(ptd);
-
- if (ptd->test_info.get_test_case_str_fn)
- test_name = ptd->test_info.get_test_case_str_fn(ptd);
+ if (tios->test_info.get_test_case_str_fn)
+ test_name =
+ tios->test_info.get_test_case_str_fn(
+ tios->test_info.testcase);
else
test_name = "Unknown testcase";
pr_info("%s: Starting test %s", __func__, test_name);
- ret = prepare_test(ptd);
+ ret = prepare_test(tios);
if (ret) {
pr_err("%s: failed to prepare the test",
__func__);
goto error;
}
- ptd->test_info.test_duration = ktime_get();
- ret = run_test(ptd);
+ tios->test_info.test_duration = ktime_get();
+ ret = run_test(tios);
if (ret) {
pr_err("%s: failed to run the test", __func__);
goto error;
@@ -763,11 +753,10 @@ int test_iosched_start_test(struct test_info *t_info)
pr_info("%s: Waiting for the test completion", __func__);
- ret = wait_event_interruptible_timeout(ptd->wait_q,
- (ptd->test_state == TEST_COMPLETED),
- msecs_to_jiffies(timeout_msec));
+ ret = wait_event_interruptible_timeout(tios->wait_q,
+ (tios->test_state == TEST_COMPLETED), timeout);
if (ret <= 0) {
- ptd->test_state = TEST_COMPLETED;
+ tios->test_state = TEST_COMPLETED;
if (!ret)
pr_info("%s: Test timeout\n", __func__);
else
@@ -775,16 +764,15 @@ int test_iosched_start_test(struct test_info *t_info)
goto error;
}
- memcpy(t_info, &ptd->test_info, sizeof(struct test_info));
+ memcpy(t_info, &tios->test_info, sizeof(*t_info));
- ret = check_test_result(ptd);
+ ret = check_test_result(tios);
if (ret) {
- pr_err("%s: check_test_result failed",
- __func__);
+ pr_err("%s: check_test_result failed", __func__);
goto error;
}
- ret = post_test(ptd);
+ ret = post_test(tios);
if (ret) {
pr_err("%s: post_test failed", __func__);
goto error;
@@ -794,9 +782,9 @@ int test_iosched_start_test(struct test_info *t_info)
* Wakeup the queue thread to fetch FS requests that might got
* postponded due to the test
*/
- blk_run_queue(ptd->req_q);
+ blk_run_queue(tios->req_q);
- if (ptd->ignore_round)
+ if (tios->ignore_round)
pr_info(
"%s: Round canceled (Got wr reqs in the middle)",
__func__);
@@ -804,19 +792,20 @@ int test_iosched_start_test(struct test_info *t_info)
if (++counter == TEST_MAX_TESTCASE_ROUNDS) {
pr_info("%s: Too many rounds, did not succeed...",
__func__);
- ptd->test_result = TEST_FAILED;
+ tios->test_result = TEST_FAILED;
}
- } while ((ptd->ignore_round) && (counter < TEST_MAX_TESTCASE_ROUNDS));
+ } while ((tios->ignore_round) &&
+ (counter < TEST_MAX_TESTCASE_ROUNDS));
- if (ptd->test_result == TEST_PASSED)
+ if (tios->test_result == TEST_PASSED)
return 0;
else
return -EINVAL;
error:
- post_test(ptd);
- ptd->test_result = TEST_FAILED;
+ post_test(tios);
+ tios->test_result = TEST_FAILED;
return ret;
}
EXPORT_SYMBOL(test_iosched_start_test);
@@ -828,11 +817,15 @@ EXPORT_SYMBOL(test_iosched_start_test);
*/
void test_iosched_register(struct blk_dev_test_type *bdt)
{
- spin_lock(&blk_dev_test_list_lock);
+ if (!bdt)
+ return;
+
+ mutex_lock(&blk_dev_test_list_lock);
list_add_tail(&bdt->list, &blk_dev_test_list);
- spin_unlock(&blk_dev_test_list_lock);
+ mutex_unlock(&blk_dev_test_list_lock);
+
}
-EXPORT_SYMBOL_GPL(test_iosched_register);
+EXPORT_SYMBOL(test_iosched_register);
/**
* test_iosched_unregister() - unregister a block device test
@@ -841,23 +834,27 @@ EXPORT_SYMBOL_GPL(test_iosched_register);
*/
void test_iosched_unregister(struct blk_dev_test_type *bdt)
{
- spin_lock(&blk_dev_test_list_lock);
+ if (!bdt)
+ return;
+
+ mutex_lock(&blk_dev_test_list_lock);
list_del_init(&bdt->list);
- spin_unlock(&blk_dev_test_list_lock);
+ mutex_unlock(&blk_dev_test_list_lock);
}
-EXPORT_SYMBOL_GPL(test_iosched_unregister);
+EXPORT_SYMBOL(test_iosched_unregister);
/**
* test_iosched_set_test_result() - Set the test
* result(PASS/FAIL)
* @test_result: the test result
*/
-void test_iosched_set_test_result(int test_result)
+void test_iosched_set_test_result(struct test_iosched *tios,
+ int test_result)
{
- if (!ptd)
+ if (!tios)
return;
- ptd->test_result = test_result;
+ tios->test_result = test_result;
}
EXPORT_SYMBOL(test_iosched_set_test_result);
@@ -867,83 +864,65 @@ EXPORT_SYMBOL(test_iosched_set_test_result);
* @ignore_round: A flag to indicate if this test round
* should be ignored and re-run
*/
-void test_iosched_set_ignore_round(bool ignore_round)
+void test_iosched_set_ignore_round(struct test_iosched *tios,
+ bool ignore_round)
{
- if (!ptd)
+ if (!tios)
return;
- ptd->ignore_round = ignore_round;
+ tios->ignore_round = ignore_round;
}
EXPORT_SYMBOL(test_iosched_set_ignore_round);
-/**
- * test_iosched_get_debugfs_tests_root() - returns the root
- * debugfs directory for the test_iosched tests
- */
-struct dentry *test_iosched_get_debugfs_tests_root(void)
+static int test_debugfs_init(struct test_iosched *tios)
{
- if (!ptd)
- return NULL;
+ char name[2*BDEVNAME_SIZE];
- return ptd->debug.debug_tests_root;
-}
-EXPORT_SYMBOL(test_iosched_get_debugfs_tests_root);
-
-/**
- * test_iosched_get_debugfs_utils_root() - returns the root
- * debugfs directory for the test_iosched utils
- */
-struct dentry *test_iosched_get_debugfs_utils_root(void)
-{
- if (!ptd)
- return NULL;
- return ptd->debug.debug_utils_root;
-}
-EXPORT_SYMBOL(test_iosched_get_debugfs_utils_root);
+ snprintf(name, 2*BDEVNAME_SIZE - 1, "%s-%s", "test-iosched",
+ tios->req_q->kobj.parent->name);
+ pr_debug("%s: creating test-iosched instance %s\n", __func__, name);
-static int test_debugfs_init(struct test_data *td)
-{
- td->debug.debug_root = debugfs_create_dir("test-iosched", NULL);
- if (!td->debug.debug_root)
+ tios->debug.debug_root = debugfs_create_dir(name, NULL);
+ if (!tios->debug.debug_root)
return -ENOENT;
- td->debug.debug_tests_root = debugfs_create_dir("tests",
- td->debug.debug_root);
- if (!td->debug.debug_tests_root)
+ tios->debug.debug_tests_root = debugfs_create_dir("tests",
+ tios->debug.debug_root);
+ if (!tios->debug.debug_tests_root)
goto err;
- td->debug.debug_utils_root = debugfs_create_dir("utils",
- td->debug.debug_root);
- if (!td->debug.debug_utils_root)
+ tios->debug.debug_utils_root = debugfs_create_dir("utils",
+ tios->debug.debug_root);
+ if (!tios->debug.debug_utils_root)
goto err;
- td->debug.debug_test_result = debugfs_create_u32(
+ tios->debug.debug_test_result = debugfs_create_u32(
"test_result",
S_IRUGO | S_IWUGO,
- td->debug.debug_utils_root,
- &td->test_result);
- if (!td->debug.debug_test_result)
+ tios->debug.debug_utils_root,
+ &tios->test_result);
+ if (!tios->debug.debug_test_result)
goto err;
- td->debug.start_sector = debugfs_create_u32(
+ tios->debug.start_sector = debugfs_create_u32(
"start_sector",
S_IRUGO | S_IWUGO,
- td->debug.debug_utils_root,
- &td->start_sector);
- if (!td->debug.start_sector)
+ tios->debug.debug_utils_root,
+ &tios->start_sector);
+ if (!tios->debug.start_sector)
goto err;
return 0;
err:
- debugfs_remove_recursive(td->debug.debug_root);
+ debugfs_remove_recursive(tios->debug.debug_root);
return -ENOENT;
}
-static void test_debugfs_cleanup(struct test_data *td)
+static void test_debugfs_cleanup(struct test_iosched *tios)
{
- debugfs_remove_recursive(td->debug.debug_root);
+ debugfs_remove_recursive(tios->debug.debug_root);
}
static void print_req(struct request *req)
@@ -980,7 +959,7 @@ static void test_merged_requests(struct request_queue *q,
}
/*
* test_dispatch_from(): Dispatch request from @queue to the @dispatched_queue.
- * Also update th dispatched_count counter.
+ * Also update the dispatched_count counter.
*/
static int test_dispatch_from(struct request_queue *q,
struct list_head *queue, unsigned int *count)
@@ -988,33 +967,35 @@ static int test_dispatch_from(struct request_queue *q,
struct test_request *test_rq;
struct request *rq;
int ret = 0;
+ struct test_iosched *tios = q->elevator->elevator_data;
unsigned long flags;
- if (!ptd)
+ if (!tios)
goto err;
- spin_lock_irqsave(&ptd->lock, flags);
+ spin_lock_irqsave(&tios->lock, flags);
if (!list_empty(queue)) {
test_rq = list_entry(queue->next, struct test_request,
queuelist);
rq = test_rq->rq;
if (!rq) {
pr_err("%s: null request,return", __func__);
- spin_unlock_irqrestore(&ptd->lock, flags);
+ spin_unlock_irqrestore(&tios->lock, flags);
goto err;
}
- list_move_tail(&test_rq->queuelist, &ptd->dispatched_queue);
- ptd->dispatched_count++;
+ list_move_tail(&test_rq->queuelist,
+ &tios->dispatched_queue);
+ tios->dispatched_count++;
(*count)--;
- spin_unlock_irqrestore(&ptd->lock, flags);
+ spin_unlock_irqrestore(&tios->lock, flags);
print_req(rq);
elv_dispatch_sort(q, rq);
- ptd->test_info.test_byte_count += test_rq->buf_size;
+ tios->test_info.test_byte_count += test_rq->buf_size;
ret = 1;
goto err;
}
- spin_unlock_irqrestore(&ptd->lock, flags);
+ spin_unlock_irqrestore(&tios->lock, flags);
err:
return ret;
@@ -1026,15 +1007,15 @@ err:
*/
static int test_dispatch_requests(struct request_queue *q, int force)
{
- struct test_data *td = q->elevator->elevator_data;
+ struct test_iosched *tios = q->elevator->elevator_data;
struct request *rq = NULL;
int ret = 0;
- switch (td->test_state) {
+ switch (tios->test_state) {
case TEST_IDLE:
- if (!list_empty(&td->queue)) {
- rq = list_entry(td->queue.next, struct request,
- queuelist);
+ if (!list_empty(&tios->queue)) {
+ rq = list_entry(tios->queue.next,
+ struct request, queuelist);
list_del_init(&rq->queuelist);
elv_dispatch_sort(q, rq);
ret = 1;
@@ -1042,23 +1023,24 @@ static int test_dispatch_requests(struct request_queue *q, int force)
}
break;
case TEST_RUNNING:
- if (test_dispatch_from(q, &td->urgent_queue,
- &td->urgent_count)) {
+ if (test_dispatch_from(q, &tios->urgent_queue,
+ &tios->urgent_count)) {
pr_debug("%s: Dispatched from urgent_count=%d",
- __func__, ptd->urgent_count);
+ __func__, tios->urgent_count);
ret = 1;
goto exit;
}
- if (test_dispatch_from(q, &td->reinsert_queue,
- &td->reinsert_count)) {
+ if (test_dispatch_from(q, &tios->reinsert_queue,
+ &tios->reinsert_count)) {
pr_debug("%s: Dispatched from reinsert_count=%d",
- __func__, ptd->reinsert_count);
+ __func__, tios->reinsert_count);
ret = 1;
goto exit;
}
- if (test_dispatch_from(q, &td->test_queue, &td->test_count)) {
+ if (test_dispatch_from(q, &tios->test_queue,
+ &tios->test_count)) {
pr_debug("%s: Dispatched from test_count=%d",
- __func__, ptd->test_count);
+ __func__, tios->test_count);
ret = 1;
goto exit;
}
@@ -1074,27 +1056,28 @@ exit:
static void test_add_request(struct request_queue *q, struct request *rq)
{
- struct test_data *td = q->elevator->elevator_data;
+ struct test_iosched *tios = q->elevator->elevator_data;
- list_add_tail(&rq->queuelist, &td->queue);
+ list_add_tail(&rq->queuelist, &tios->queue);
/*
* The write requests can be followed by a FLUSH request that might
* cause unexpected results of the test.
*/
- if ((rq_data_dir(rq) == WRITE) && (td->test_state == TEST_RUNNING)) {
+ if (rq_data_dir(rq) == WRITE &&
+ tios->test_state == TEST_RUNNING) {
pr_debug("%s: got WRITE req in the middle of the test",
__func__);
- td->fs_wr_reqs_during_test = true;
+ tios->fs_wr_reqs_during_test = true;
}
}
static struct request *
test_former_request(struct request_queue *q, struct request *rq)
{
- struct test_data *td = q->elevator->elevator_data;
+ struct test_iosched *tios = q->elevator->elevator_data;
- if (rq->queuelist.prev == &td->queue)
+ if (rq->queuelist.prev == &tios->queue)
return NULL;
return list_entry(rq->queuelist.prev, struct request, queuelist);
}
@@ -1102,9 +1085,9 @@ test_former_request(struct request_queue *q, struct request *rq)
static struct request *
test_latter_request(struct request_queue *q, struct request *rq)
{
- struct test_data *td = q->elevator->elevator_data;
+ struct test_iosched *tios = q->elevator->elevator_data;
- if (rq->queuelist.next == &td->queue)
+ if (rq->queuelist.next == &tios->queue)
return NULL;
return list_entry(rq->queuelist.next, struct request, queuelist);
}
@@ -1113,57 +1096,100 @@ static int test_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct blk_dev_test_type *__bdt;
struct elevator_queue *eq;
+ struct test_iosched *tios;
+ const char *blk_dev_name;
+ int ret;
+ bool found = false;
eq = elevator_alloc(q, e);
if (!eq)
return -ENOMEM;
- ptd = kmalloc_node(sizeof(struct test_data), GFP_KERNEL,
- q->node);
- if (!ptd) {
- pr_err("%s: failed to allocate test data", __func__);
- return -ENOMEM;
+ tios = kzalloc_node(sizeof(*tios), GFP_KERNEL, q->node);
+ if (!tios) {
+ pr_err("%s: failed to allocate test iosched\n", __func__);
+ ret = -ENOMEM;
+ goto free_kobj;
}
- memset((void *)ptd, 0, sizeof(struct test_data));
- INIT_LIST_HEAD(&ptd->queue);
- INIT_LIST_HEAD(&ptd->test_queue);
- INIT_LIST_HEAD(&ptd->dispatched_queue);
- INIT_LIST_HEAD(&ptd->reinsert_queue);
- INIT_LIST_HEAD(&ptd->urgent_queue);
- init_waitqueue_head(&ptd->wait_q);
-
- eq->elevator_data = ptd;
- ptd->req_q = q;
- spin_lock_irq(q->queue_lock);
- q->elevator = eq;
- spin_unlock_irq(q->queue_lock);
+ eq->elevator_data = tios;
+
+ INIT_LIST_HEAD(&tios->queue);
+ INIT_LIST_HEAD(&tios->test_queue);
+ INIT_LIST_HEAD(&tios->dispatched_queue);
+ INIT_LIST_HEAD(&tios->reinsert_queue);
+ INIT_LIST_HEAD(&tios->urgent_queue);
+ init_waitqueue_head(&tios->wait_q);
+ tios->req_q = q;
+
+ spin_lock_init(&tios->lock);
+
+ ret = test_debugfs_init(tios);
+ if (ret) {
+ pr_err("%s: Failed to create debugfs files, ret=%d",
+ __func__, ret);
+ goto free_mem;
+ }
+ blk_dev_name = q->kobj.parent->name;
- spin_lock_init(&ptd->lock);
+ /* Traverse the block device test list and init matches */
+ mutex_lock(&blk_dev_test_list_lock);
- if (test_debugfs_init(ptd)) {
- pr_err("%s: Failed to create debugfs files", __func__);
- return -ENOMEM;
+ list_for_each_entry(__bdt, &blk_dev_test_list, list) {
+ pr_debug("%s: checking if %s is a match to device %s\n",
+ __func__, __bdt->type_prefix, blk_dev_name);
+ if (!strnstr(blk_dev_name, __bdt->type_prefix,
+ strlen(__bdt->type_prefix)))
+ continue;
+
+ pr_debug("%s: found the match!\n", __func__);
+ found = true;
+ break;
}
+ mutex_unlock(&blk_dev_test_list_lock);
- list_for_each_entry(__bdt, &blk_dev_test_list, list)
- __bdt->init_fn();
+ /* No match found */
+ if (!found) {
+ pr_err("%s: No matching block device test utility found\n",
+ __func__);
+ ret = -ENODEV;
+ goto free_debugfs;
+ } else {
+ ret = __bdt->init_fn(tios);
+ if (ret) {
+ pr_err("%s: failed to init block device test, ret=%d\n",
+ __func__, ret);
+ goto free_debugfs;
+ }
+ }
+
+ spin_lock_irq(q->queue_lock);
+ q->elevator = eq;
+ spin_unlock_irq(q->queue_lock);
return 0;
+
+free_debugfs:
+ test_debugfs_cleanup(tios);
+free_mem:
+ kfree(tios);
+free_kobj:
+ kobject_put(&eq->kobj);
+ return ret;
}
static void test_exit_queue(struct elevator_queue *e)
{
- struct test_data *td = e->elevator_data;
+ struct test_iosched *tios = e->elevator_data;
struct blk_dev_test_type *__bdt;
- BUG_ON(!list_empty(&td->queue));
+ BUG_ON(!list_empty(&tios->queue));
list_for_each_entry(__bdt, &blk_dev_test_list, list)
- __bdt->exit_fn();
+ __bdt->exit_fn(tios);
- test_debugfs_cleanup(td);
+ test_debugfs_cleanup(tios);
- kfree(td);
+ kfree(tios);
}
/**
@@ -1174,13 +1200,17 @@ static void test_exit_queue(struct elevator_queue *e)
* added.
*
*/
-void test_iosched_add_urgent_req(struct test_request *test_rq)
+void test_iosched_add_urgent_req(struct test_iosched *tios,
+ struct test_request *test_rq)
{
- spin_lock_irq(&ptd->lock);
+ if (!tios)
+ return;
+
+ spin_lock_irq(&tios->lock);
test_rq->rq->cmd_flags |= REQ_URGENT;
- list_add_tail(&test_rq->queuelist, &ptd->urgent_queue);
- ptd->urgent_count++;
- spin_unlock_irq(&ptd->lock);
+ list_add_tail(&test_rq->queuelist, &tios->urgent_queue);
+ tios->urgent_count++;
+ spin_unlock_irq(&tios->lock);
}
EXPORT_SYMBOL(test_iosched_add_urgent_req);
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 717a3ad760e7..8036d4b682fe 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,17 +30,17 @@
#define BIO_U32_SIZE 1024
#define TEST_BIO_SIZE PAGE_SIZE /* use one page bios */
-struct test_data;
+struct test_iosched;
-typedef int (prepare_test_fn) (struct test_data *);
-typedef int (run_test_fn) (struct test_data *);
-typedef int (check_test_result_fn) (struct test_data *);
-typedef int (post_test_fn) (struct test_data *);
-typedef char* (get_test_case_str_fn) (struct test_data *);
-typedef void (blk_dev_test_init_fn) (void);
-typedef void (blk_dev_test_exit_fn) (void);
-typedef struct gendisk* (get_rq_disk_fn) (void);
-typedef bool (check_test_completion_fn) (void);
+typedef int (prepare_test_fn) (struct test_iosched *);
+typedef int (run_test_fn) (struct test_iosched *);
+typedef int (check_test_result_fn) (struct test_iosched *);
+typedef int (post_test_fn) (struct test_iosched *);
+typedef char* (get_test_case_str_fn) (int);
+typedef int (blk_dev_test_init_fn) (struct test_iosched *);
+typedef void (blk_dev_test_exit_fn) (struct test_iosched *);
+typedef struct gendisk* (get_rq_disk_fn) (struct test_iosched *);
+typedef bool (check_test_completion_fn) (struct test_iosched *);
/**
* enum test_state - defines the state of the test
@@ -154,12 +154,14 @@ struct test_info {
/**
* struct blk_dev_test_type - identifies block device test
- * @list: list head pointer
- * @init_fn: block device test init callback
- * @exit_fn: block device test exit callback
+ * @list: list head pointer
+ * @type_prefix: prefix of device class name, i.e. "mmc"/ "sd"
+ * @init_fn: block device test init callback
+ * @exit_fn: block device test exit callback
*/
struct blk_dev_test_type {
struct list_head list;
+ const char *type_prefix;
blk_dev_test_init_fn *init_fn;
blk_dev_test_exit_fn *exit_fn;
};
@@ -205,8 +207,9 @@ struct blk_dev_test_type {
* test round was disturbed by an external
* flush request, therefore disqualifying
* the results
+ * @blk_dev_test_data: associated specific block device test utility
*/
-struct test_data {
+struct test_iosched {
struct list_head queue;
struct list_head test_queue;
struct list_head dispatched_queue;
@@ -230,42 +233,37 @@ struct test_data {
bool fs_wr_reqs_during_test;
bool ignore_round;
bool notified_urgent;
+ void *blk_dev_test_data;
};
-extern int test_iosched_start_test(struct test_info *t_info);
-extern void test_iosched_mark_test_completion(void);
-extern void check_test_completion(void);
-extern int test_iosched_add_unique_test_req(int is_err_expcted,
- enum req_unique_type req_unique,
- int start_sec, int nr_sects, rq_end_io_fn *end_req_io);
-extern int test_iosched_add_wr_rd_test_req(int is_err_expcted,
- int direction, int start_sec,
- int num_bios, int pattern, rq_end_io_fn *end_req_io);
-extern struct test_request *test_iosched_create_test_req(int is_err_expcted,
- int direction, int start_sec,
- int num_bios, int pattern, rq_end_io_fn *end_req_io);
+extern int test_iosched_start_test(struct test_iosched *,
+ struct test_info *t_info);
+extern void test_iosched_mark_test_completion(struct test_iosched *);
+extern void check_test_completion(struct test_iosched *);
+extern int test_iosched_add_unique_test_req(struct test_iosched *,
+ int is_err_expcted, enum req_unique_type req_unique,
+ int start_sec, int nr_sects, rq_end_io_fn *end_req_io);
+extern int test_iosched_add_wr_rd_test_req(struct test_iosched *,
+ int is_err_expcted, int direction, int start_sec, int num_bios,
+ int pattern, rq_end_io_fn *end_req_io);
+extern struct test_request *test_iosched_create_test_req(struct test_iosched *,
+ int is_err_expcted, int direction, int start_sec, int num_bios,
+ int pattern, rq_end_io_fn *end_req_io);
-extern struct dentry *test_iosched_get_debugfs_tests_root(void);
-extern struct dentry *test_iosched_get_debugfs_utils_root(void);
+extern void test_iosched_set_test_result(struct test_iosched*, int test_result);
-extern struct request_queue *test_iosched_get_req_queue(void);
+extern void test_iosched_set_ignore_round(struct test_iosched *,
+ bool ignore_round);
-extern void test_iosched_set_test_result(int);
+extern void test_iosched_register(struct blk_dev_test_type *bdt);
-void test_iosched_set_ignore_round(bool ignore_round);
+extern void test_iosched_unregister(struct blk_dev_test_type *bdt);
-void test_iosched_register(struct blk_dev_test_type *bdt);
+extern void test_iosched_add_urgent_req(struct test_iosched *,
+ struct test_request *);
-void test_iosched_unregister(struct blk_dev_test_type *bdt);
+extern void check_test_completion(struct test_iosched *);
-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);
-
-int compare_buffer_to_pattern(struct test_request *test_rq);
+extern int compare_buffer_to_pattern(struct test_request *test_rq);
#endif /* _LINUX_TEST_IOSCHED_H */