Merge "block: test-iosched infrastructure enhancement"
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 71e8669..d2716c84 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -43,18 +43,7 @@
static LIST_HEAD(blk_dev_test_list);
static struct test_data *ptd;
-/* Get the request after `test_rq' in the test requests list */
-static struct test_request *
-latter_test_request(struct request_queue *q,
- struct test_request *test_rq)
-{
- struct test_data *td = q->elevator->elevator_data;
- if (test_rq->queuelist.next == &td->test_queue)
- return NULL;
- return list_entry(test_rq->queuelist.next, struct test_request,
- queuelist);
-}
/**
* test_iosched_get_req_queue() - returns the request queue
@@ -77,6 +66,10 @@
{
if (!ptd)
return;
+ test_pr_info("%s: mark test is completed, test_count=%d,",
+ __func__, ptd->test_count);
+ test_pr_info("%s: reinsert_count=%d, dispatched_count=%d",
+ __func__, ptd->reinsert_count, ptd->dispatched_count);
ptd->test_state = TEST_COMPLETED;
wake_up(&ptd->wait_q);
@@ -87,18 +80,32 @@
static void check_test_completion(void)
{
struct test_request *test_rq;
- struct request *rq;
- list_for_each_entry(test_rq, &ptd->test_queue, queuelist) {
- rq = test_rq->rq;
+ if (!ptd)
+ return;
+
+ list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist)
if (!test_rq->req_completed)
return;
+
+ if (!list_empty(&ptd->test_queue)
+ || !list_empty(&ptd->reinsert_queue)
+ || !list_empty(&ptd->urgent_queue)) {
+ test_pr_info("%s: Test still not completed,", __func__);
+ test_pr_info("%s: test_count=%d, reinsert_count=%d",
+ __func__, ptd->test_count, ptd->reinsert_count);
+ test_pr_info("%s: dispatched_count=%d, urgent_count=%d",
+ __func__, ptd->dispatched_count, ptd->urgent_count);
+ return;
}
ptd->test_info.test_duration = jiffies -
ptd->test_info.test_duration;
- test_pr_info("%s: Test is completed", __func__);
+ test_pr_info("%s: Test is completed, test_count=%d, reinsert_count=%d,",
+ __func__, ptd->test_count, ptd->reinsert_count);
+ test_pr_info("%s: dispatched_count=%d",
+ __func__, ptd->dispatched_count);
test_iosched_mark_test_completion();
}
@@ -111,7 +118,6 @@
{
if (err)
clear_bit(BIO_UPTODATE, &bio->bi_flags);
-
bio_put(bio);
}
@@ -221,7 +227,10 @@
"%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);
return 0;
}
@@ -253,8 +262,7 @@
}
/**
- * test_iosched_add_wr_rd_test_req() - Create and queue a
- * read/write request.
+ * test_iosched_create_test_req() - Create a read/write request.
* @is_err_expcted: A flag to indicate if this request
* should succeed or not
* @direction: READ/WRITE
@@ -278,34 +286,33 @@
* request 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,
+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 request *rq = NULL;
- struct test_request *test_rq = NULL;
- int rw_flags = 0;
- int buf_size = 0;
- int ret = 0, i = 0;
+ struct request *rq;
+ struct test_request *test_rq;
+ int rw_flags, buf_size;
+ int ret = 0, i;
unsigned int *bio_ptr = NULL;
struct bio *bio = NULL;
if (!ptd)
- return -ENODEV;
+ return NULL;
rw_flags = direction;
rq = blk_get_request(ptd->req_q, rw_flags, GFP_KERNEL);
if (!rq) {
test_pr_err("%s: Failed to allocate a request", __func__);
- return -ENODEV;
+ return NULL;
}
test_rq = kzalloc(sizeof(struct test_request), GFP_KERNEL);
if (!test_rq) {
test_pr_err("%s: Failed to allocate test request", __func__);
blk_put_request(rq);
- return -ENODEV;
+ return NULL;
}
buf_size = sizeof(unsigned int) * BIO_U32_SIZE * num_bios;
@@ -341,6 +348,7 @@
rq->end_io = end_test_req;
rq->__sector = start_sec;
rq->cmd_type |= REQ_TYPE_FS;
+ rq->cmd_flags |= REQ_SORTED; /* do we need this?*/
if (rq->bio) {
rq->bio->bi_sector = start_sec;
@@ -359,16 +367,61 @@
test_rq->is_err_expected = is_err_expcted;
rq->elv.priv[0] = (void *)test_rq;
- test_pr_debug(
- "%s: added request %d to the test requests list, buf_size=%d",
- __func__, test_rq->req_id, buf_size);
+ test_pr_debug("%s: created test request %d, buf_size=%d",
+ __func__, test_rq->req_id, buf_size);
- list_add_tail(&test_rq->queuelist, &ptd->test_queue);
-
- return 0;
+ return test_rq;
err:
blk_put_request(rq);
kfree(test_rq->bios_buffer);
+ return NULL;
+}
+EXPORT_SYMBOL(test_iosched_create_test_req);
+
+
+/**
+ * test_iosched_add_wr_rd_test_req() - Create and queue a
+ * read/write request.
+ * @is_err_expcted: A flag to indicate if this request
+ * should succeed or not
+ * @direction: READ/WRITE
+ * @start_sec: start address of the first bio
+ * @num_bios: number of BIOs to be allocated for the
+ * request
+ * @pattern: A pattern, to be written into the write
+ * requests data buffer. In case of READ
+ * request, the given pattern is kept as
+ * the expected pattern. The expected
+ * pattern will be compared in the test
+ * check result function. If no comparisson
+ * is required, set pattern to
+ * TEST_NO_PATTERN.
+ * @end_req_io: specific completion callback. When not
+ * set,the default callback will be used
+ *
+ * This function allocates the test request and the block
+ * request and calls blk_rq_map_kern which allocates the
+ * required BIO. Upon success the new request is added to the
+ * test_queue. The allocated test request and the block request
+ * 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)
+{
+ 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);
+ 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);
+ return 0;
+ }
return -ENODEV;
}
EXPORT_SYMBOL(test_iosched_add_wr_rd_test_req);
@@ -431,12 +484,18 @@
static int check_test_result(struct test_data *td)
{
struct test_request *test_rq;
- struct request *rq;
int res = 0;
static int run;
- list_for_each_entry(test_rq, &ptd->test_queue, queuelist) {
- rq = test_rq->rq;
+ if (!ptd)
+ goto err;
+
+ list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist) {
+ if (!test_rq->rq) {
+ test_pr_info("%s: req_id %d is contains empty req",
+ __func__, test_rq->req_id);
+ continue;
+ }
if (!test_rq->req_completed) {
test_pr_err("%s: rq %d not completed", __func__,
test_rq->req_id);
@@ -509,27 +568,25 @@
return ret;
}
- /*
- * Set the next_req pointer to the first request in the test requests
- * list
- */
- if (!list_empty(&td->test_queue))
- td->next_req = list_entry(td->test_queue.next,
- struct test_request, queuelist);
__blk_run_queue(td->req_q);
return 0;
}
-/* Free the allocated test requests, their requests and BIOs buffer */
-static void free_test_requests(struct test_data *td)
+/*
+ * free_test_queue() - Free all allocated test requests in the given test_queue:
+ * free their requests and BIOs buffer
+ * @test_queue the test queue to be freed
+ */
+static void free_test_queue(struct list_head *test_queue)
{
struct test_request *test_rq;
struct bio *bio;
- while (!list_empty(&td->test_queue)) {
- test_rq = list_entry(td->test_queue.next, struct test_request,
- queuelist);
+ while (!list_empty(test_queue)) {
+ test_rq = list_entry(test_queue->next, struct test_request,
+ queuelist);
+
list_del_init(&test_rq->queuelist);
/*
* If the request was not completed we need to free its BIOs
@@ -538,7 +595,7 @@
if (!test_rq->req_completed) {
test_pr_info(
"%s: Freeing memory of an uncompleted request",
- __func__);
+ __func__);
list_del_init(&test_rq->rq->queuelist);
while ((bio = test_rq->rq->bio) != NULL) {
test_rq->rq->bio = bio->bi_next;
@@ -552,8 +609,39 @@
}
/*
- * Do post test operations.
- * Free the allocated test requests, their requests and BIOs buffer.
+ * free_test_requests() - Free all allocated test requests in
+ * all test queues in given test_data.
+ * @td The test_data struct whos test requests will be
+ * freed.
+ */
+static void free_test_requests(struct test_data *td)
+{
+ if (!td)
+ return;
+
+ if (td->urgent_count) {
+ free_test_queue(&td->urgent_queue);
+ td->urgent_count = 0;
+ }
+ if (td->test_count) {
+ free_test_queue(&td->test_queue);
+ td->test_count = 0;
+ }
+ if (td->dispatched_count) {
+ free_test_queue(&td->dispatched_queue);
+ td->dispatched_count = 0;
+ }
+ if (td->reinsert_count) {
+ free_test_queue(&td->reinsert_queue);
+ td->reinsert_count = 0;
+ }
+}
+
+/*
+ * post_test() - Do post test operations. Free the allocated
+ * test requests, their requests and BIOs buffer.
+ * @td The test_data struct for the test that has
+ * ended.
*/
static int post_test(struct test_data *td)
{
@@ -641,7 +729,6 @@
memcpy(&ptd->test_info, t_info, sizeof(struct test_info));
- ptd->next_req = NULL;
ptd->test_result = TEST_NO_RESULT;
ptd->num_of_write_bios = 0;
@@ -886,6 +973,45 @@
{
list_del_init(&next->queuelist);
}
+/*
+ * test_dispatch_from(): Dispatch request from @queue to the @dispatched_queue.
+ * Also update th dispatched_count counter.
+ */
+static int test_dispatch_from(struct request_queue *q,
+ struct list_head *queue, unsigned int *count)
+{
+ struct test_request *test_rq;
+ struct request *rq;
+ int ret = 0;
+
+ if (!ptd)
+ goto err;
+
+ spin_lock_irq(&ptd->lock);
+ 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_irq(&ptd->lock);
+ goto err;
+ }
+ list_move_tail(&test_rq->queuelist, &ptd->dispatched_queue);
+ ptd->dispatched_count++;
+ (*count)--;
+ spin_unlock_irq(&ptd->lock);
+
+ print_req(rq);
+ elv_dispatch_sort(q, rq);
+ ret = 1;
+ goto err;
+ }
+ spin_unlock_irq(&ptd->lock);
+
+err:
+ return ret;
+}
/*
* Dispatch a test request in case there is a running test Otherwise, dispatch
@@ -895,6 +1021,7 @@
{
struct test_data *td = q->elevator->elevator_data;
struct request *rq = NULL;
+ int ret = 0;
switch (td->test_state) {
case TEST_IDLE:
@@ -903,27 +1030,39 @@
queuelist);
list_del_init(&rq->queuelist);
elv_dispatch_sort(q, rq);
- return 1;
+ ret = 1;
+ goto exit;
}
break;
case TEST_RUNNING:
- if (td->next_req) {
- rq = td->next_req->rq;
- td->next_req =
- latter_test_request(td->req_q, td->next_req);
- if (!rq)
- return 0;
- print_req(rq);
- elv_dispatch_sort(q, rq);
- return 1;
+ if (test_dispatch_from(q, &td->urgent_queue,
+ &td->urgent_count)) {
+ test_pr_debug("%s: Dispatched from urgent_count=%d",
+ __func__, ptd->urgent_count);
+ ret = 1;
+ goto exit;
+ }
+ if (test_dispatch_from(q, &td->reinsert_queue,
+ &td->reinsert_count)) {
+ test_pr_debug("%s: Dispatched from reinsert_count=%d",
+ __func__, ptd->reinsert_count);
+ ret = 1;
+ goto exit;
+ }
+ if (test_dispatch_from(q, &td->test_queue, &td->test_count)) {
+ test_pr_debug("%s: Dispatched from test_count=%d",
+ __func__, ptd->test_count);
+ ret = 1;
+ goto exit;
}
break;
case TEST_COMPLETED:
default:
- return 0;
+ break;
}
- return 0;
+exit:
+ return ret;
}
static void test_add_request(struct request_queue *q, struct request *rq)
@@ -976,6 +1115,9 @@
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);
ptd->req_q = q;
@@ -1010,7 +1152,79 @@
kfree(td);
}
+/**
+ * test_get_test_data() - Returns a pointer to the test_data
+ * struct which keeps the current test data.
+ *
+ */
+struct test_data *test_get_test_data(void)
+{
+ return ptd;
+}
+EXPORT_SYMBOL(test_get_test_data);
+
+static bool test_urgent_pending(struct request_queue *q)
+{
+ return !list_empty(&ptd->urgent_queue);
+}
+
+/**
+ * test_iosched_add_urgent_req() - Add an urgent test_request.
+ * First mark the request as urgent, then add it to the
+ * urgent_queue test queue.
+ * @test_rq: pointer to the urgent test_request to be
+ * added.
+ *
+ */
+void test_iosched_add_urgent_req(struct test_request *test_rq)
+{
+ spin_lock_irq(&ptd->lock);
+ blk_mark_rq_urgent(test_rq->rq);
+ list_add_tail(&test_rq->queuelist, &ptd->urgent_queue);
+ ptd->urgent_count++;
+ spin_unlock_irq(&ptd->lock);
+}
+EXPORT_SYMBOL(test_iosched_add_urgent_req);
+
+/**
+ * test_reinsert_req() - Moves the @rq request from
+ * @dispatched_queue into @reinsert_queue.
+ * The @rq must be in @dispatched_queue
+ * @q: request queue
+ * @rq: request to be inserted
+ *
+ *
+ */
+static int test_reinsert_req(struct request_queue *q,
+ struct request *rq)
+{
+ struct test_request *test_rq;
+ int ret = -EINVAL;
+
+ if (!ptd)
+ goto exit;
+
+ if (list_empty(&ptd->dispatched_queue)) {
+ test_pr_err("%s: dispatched_queue is empty", __func__);
+ goto exit;
+ }
+
+ list_for_each_entry(test_rq, &ptd->dispatched_queue, queuelist) {
+ if (test_rq->rq == rq) {
+ list_move(&test_rq->queuelist, &ptd->reinsert_queue);
+ ptd->dispatched_count--;
+ ptd->reinsert_count++;
+ ret = 0;
+ break;
+ }
+ }
+
+exit:
+ return ret;
+}
+
static struct elevator_type elevator_test_iosched = {
+
.ops = {
.elevator_merge_req_fn = test_merged_requests,
.elevator_dispatch_fn = test_dispatch_requests,
@@ -1019,6 +1233,8 @@
.elevator_latter_req_fn = test_latter_request,
.elevator_init_fn = test_init_queue,
.elevator_exit_fn = test_exit_queue,
+ .elevator_is_urgent_fn = test_urgent_pending,
+ .elevator_reinsert_req_fn = test_reinsert_req,
},
.elevator_name = "test-iosched",
.elevator_owner = THIS_MODULE,
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 610a822..e0b7e35 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -1877,8 +1877,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.prev,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
mbtd->bkops_stage == BKOPS_STAGE_4);
@@ -1908,8 +1906,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.prev,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
mbtd->bkops_stage == BKOPS_STAGE_4);
@@ -1939,8 +1935,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.prev,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
mbtd->bkops_stage == BKOPS_STAGE_2);
@@ -1958,8 +1952,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.prev,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
@@ -1999,8 +1991,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.next,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
mbtd->bkops_stage == BKOPS_STAGE_2);
@@ -2018,8 +2008,6 @@
break;
}
- td->next_req = list_entry(td->test_queue.prev,
- struct test_request, queuelist);
__blk_run_queue(q);
wait_event(mbtd->bkops_wait_q,
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index b52762c..e843f4f 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -161,8 +161,17 @@
* struct test_data - global test iosched data
* @queue: The test IO scheduler requests list
* @test_queue: The test requests list
- * @next_req: Points to the next request to be
- * dispatched from the test requests list
+ * @dispatched_queue: The queue contains requests dispatched
+ * from @test_queue
+ * @reinsert_queue: The queue contains reinserted from underlying
+ * driver requests
+ * @urgent_queue: The queue contains requests for urgent delivery
+ * These requests will be delivered before @test_queue
+ * and @reinsert_queue requests
+ * @test_count: Number of requests in the @test_queue
+ * @dispatched_count: Number of requests in the @dispatched_queue
+ * @reinsert_count: Number of requests in the @reinsert_queue
+ * @urgent_count: Number of requests in the @urgent_queue
* @wait_q: A wait queue for waiting for the test
* requests completion
* @test_state: Indicates if there is a running test.
@@ -195,7 +204,13 @@
struct test_data {
struct list_head queue;
struct list_head test_queue;
- struct test_request *next_req;
+ struct list_head dispatched_queue;
+ struct list_head reinsert_queue;
+ struct list_head urgent_queue;
+ unsigned int test_count;
+ unsigned int dispatched_count;
+ unsigned int reinsert_count;
+ unsigned int urgent_count;
wait_queue_head_t wait_q;
enum test_state test_state;
enum test_results test_result;
@@ -210,6 +225,7 @@
struct test_info test_info;
bool fs_wr_reqs_during_test;
bool ignore_round;
+ bool notified_urgent;
};
extern int test_iosched_start_test(struct test_info *t_info);
@@ -220,6 +236,9 @@
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 struct dentry *test_iosched_get_debugfs_tests_root(void);
extern struct dentry *test_iosched_get_debugfs_utils_root(void);
@@ -234,4 +253,9 @@
void test_iosched_unregister(struct blk_dev_test_type *bdt);
+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);
#endif /* _LINUX_TEST_IOSCHED_H */