12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277 |
- /* Copyright (c) 2012-2013, 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * The test scheduler allows to test the block device by dispatching
- * specific requests according to the test case and declare PASS/FAIL
- * according to the requests completion error code.
- * Each test is exposed via debugfs and can be triggered by writing to
- * the debugfs file.
- *
- */
- /* elevator test iosched */
- #include <linux/blkdev.h>
- #include <linux/elevator.h>
- #include <linux/bio.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/debugfs.h>
- #include <linux/test-iosched.h>
- #include <linux/delay.h>
- #include "blk.h"
- #define MODULE_NAME "test-iosched"
- #define WR_RD_START_REQ_ID 1234
- #define UNIQUE_START_REQ_ID 5678
- #define TIMEOUT_TIMER_MS 40000
- #define TEST_MAX_TESTCASE_ROUNDS 15
- #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)
- #define test_pr_err(fmt, args...) pr_err("%s: "fmt"\n", MODULE_NAME, args)
- 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;
- 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)
- {
- 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);
- }
- EXPORT_SYMBOL(test_iosched_mark_test_completion);
- /**
- * check_test_completion() - Check if all the queued test
- * requests were completed
- */
- void check_test_completion(void)
- {
- struct test_request *test_rq;
- if (!ptd)
- goto exit;
- list_for_each_entry(test_rq, &ptd->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)) {
- 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);
- goto exit;
- }
- ptd->test_info.test_duration = ktime_sub(ktime_get(),
- ptd->test_info.test_duration);
- 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();
- exit:
- return;
- }
- EXPORT_SYMBOL(check_test_completion);
- /*
- * A callback to be called per bio completion.
- * Frees the bio memory.
- */
- static void end_test_bio(struct bio *bio, int err)
- {
- if (err)
- clear_bit(BIO_UPTODATE, &bio->bi_flags);
- bio_put(bio);
- }
- /*
- * A callback to be called per request completion.
- * the request memory is not freed here, will be freed later after the test
- * results checking.
- */
- static void end_test_req(struct request *rq, int err)
- {
- struct test_request *test_rq;
- test_rq = (struct test_request *)rq->elv.priv[0];
- BUG_ON(!test_rq);
- test_pr_debug("%s: request %d completed, err=%d",
- __func__, test_rq->req_id, err);
- test_rq->req_completed = true;
- test_rq->req_result = err;
- check_test_completion();
- }
- /**
- * test_iosched_add_unique_test_req() - Create and queue a non
- * read/write request (such as FLUSH/DISCRAD/SANITIZE).
- * @is_err_expcted: A flag to indicate if this request
- * should succeed or not
- * @req_unique: The type of request to add
- * @start_sec: start address of the first bio
- * @nr_sects: number of sectors in the request
- * @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)
- {
- struct bio *bio;
- struct request *rq;
- int rw_flags;
- struct test_request *test_rq;
- if (!ptd)
- return -ENODEV;
- bio = bio_alloc(GFP_KERNEL, 0);
- if (!bio) {
- test_pr_err("%s: Failed to allocate a bio", __func__);
- return -ENODEV;
- }
- bio_get(bio);
- bio->bi_end_io = end_test_bio;
- switch (req_unique) {
- case REQ_UNIQUE_FLUSH:
- bio->bi_rw = WRITE_FLUSH;
- break;
- case REQ_UNIQUE_DISCARD:
- bio->bi_rw = REQ_WRITE | REQ_DISCARD;
- bio->bi_size = nr_sects << 9;
- bio->bi_sector = start_sec;
- break;
- case REQ_UNIQUE_SANITIZE:
- bio->bi_rw = REQ_WRITE | REQ_SANITIZE;
- break;
- default:
- test_pr_err("%s: Invalid request type %d", __func__,
- req_unique);
- bio_put(bio);
- return -ENODEV;
- }
- rw_flags = bio_data_dir(bio);
- if (bio->bi_rw & REQ_SYNC)
- rw_flags |= REQ_SYNC;
- rq = blk_get_request(ptd->req_q, rw_flags, GFP_KERNEL);
- if (!rq) {
- test_pr_err("%s: Failed to allocate a request", __func__);
- bio_put(bio);
- return -ENODEV;
- }
- init_request_from_bio(rq, bio);
- if (end_req_io)
- rq->end_io = end_req_io;
- else
- rq->end_io = end_test_req;
- test_rq = kzalloc(sizeof(struct test_request), GFP_KERNEL);
- if (!test_rq) {
- test_pr_err("%s: Failed to allocate a test request", __func__);
- bio_put(bio);
- blk_put_request(rq);
- return -ENODEV;
- }
- test_rq->req_completed = false;
- test_rq->req_result = -EINVAL;
- 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_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);
- return 0;
- }
- EXPORT_SYMBOL(test_iosched_add_unique_test_req);
- /*
- * Get a pattern to be filled in the request data buffer.
- * If the pattern used is (-1) the buffer will be filled with sequential
- * numbers
- */
- static void fill_buf_with_pattern(int *buf, int num_bytes, int pattern)
- {
- int i = 0;
- int num_of_dwords = num_bytes/sizeof(int);
- if (pattern == TEST_NO_PATTERN)
- return;
- /* num_bytes should be aligned to sizeof(int) */
- BUG_ON((num_bytes % sizeof(int)) != 0);
- if (pattern == TEST_PATTERN_SEQUENTIAL) {
- for (i = 0; i < num_of_dwords; i++)
- buf[i] = i;
- } else {
- for (i = 0; i < num_of_dwords; i++)
- buf[i] = pattern;
- }
- }
- /**
- * 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
- * @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. 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.
- */
- 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;
- 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 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 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 NULL;
- }
- buf_size = sizeof(unsigned int) * BIO_U32_SIZE * num_bios;
- test_rq->bios_buffer = kzalloc(buf_size, GFP_KERNEL);
- if (!test_rq->bios_buffer) {
- test_pr_err("%s: Failed to allocate the data buf", __func__);
- goto err;
- }
- test_rq->buf_size = buf_size;
- if (direction == WRITE)
- fill_buf_with_pattern(test_rq->bios_buffer,
- buf_size, pattern);
- test_rq->wr_rd_data_pattern = pattern;
- bio_ptr = test_rq->bios_buffer;
- for (i = 0; i < num_bios; ++i) {
- ret = blk_rq_map_kern(ptd->req_q, rq,
- (void *)bio_ptr,
- sizeof(unsigned int)*BIO_U32_SIZE,
- GFP_KERNEL);
- if (ret) {
- test_pr_err("%s: blk_rq_map_kern returned error %d",
- __func__, ret);
- goto err;
- }
- bio_ptr += BIO_U32_SIZE;
- }
- if (end_req_io)
- rq->end_io = end_req_io;
- else
- rq->end_io = end_test_req;
- rq->__sector = start_sec;
- rq->cmd_type |= REQ_TYPE_FS;
- rq->cmd_flags |= REQ_SORTED;
- if (rq->bio) {
- rq->bio->bi_sector = start_sec;
- rq->bio->bi_end_io = end_test_bio;
- bio = rq->bio;
- while ((bio = bio->bi_next) != NULL)
- bio->bi_end_io = end_test_bio;
- }
- ptd->num_of_write_bios += num_bios;
- test_rq->req_id = ptd->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();
- test_rq->is_err_expected = is_err_expcted;
- rq->elv.priv[0] = (void *)test_rq;
- test_pr_debug("%s: created test request %d, buf_size=%d",
- __func__, test_rq->req_id, buf_size);
- 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);
- /* Converts the testcase number into a string */
- static char *get_test_case_str(struct test_data *td)
- {
- if (td->test_info.get_test_case_str_fn)
- return td->test_info.get_test_case_str_fn(td);
- return "Unknown testcase";
- }
- /*
- * Verify that the test request data buffer includes the expected
- * pattern
- */
- static int compare_buffer_to_pattern(struct test_request *test_rq)
- {
- int i = 0;
- int num_of_dwords = test_rq->buf_size/sizeof(int);
- /* num_bytes should be aligned to sizeof(int) */
- BUG_ON((test_rq->buf_size % sizeof(int)) != 0);
- BUG_ON(test_rq->bios_buffer == NULL);
- if (test_rq->wr_rd_data_pattern == TEST_NO_PATTERN)
- return 0;
- if (test_rq->wr_rd_data_pattern == TEST_PATTERN_SEQUENTIAL) {
- for (i = 0; i < num_of_dwords; i++) {
- if (test_rq->bios_buffer[i] != i) {
- test_pr_err(
- "%s: wrong pattern 0x%x in index %d",
- __func__, test_rq->bios_buffer[i], i);
- return -EINVAL;
- }
- }
- } else {
- for (i = 0; i < num_of_dwords; i++) {
- if (test_rq->bios_buffer[i] !=
- test_rq->wr_rd_data_pattern) {
- test_pr_err(
- "%s: wrong pattern 0x%x in index %d",
- __func__, test_rq->bios_buffer[i], i);
- return -EINVAL;
- }
- }
- }
- return 0;
- }
- /*
- * Determine if the test passed or failed.
- * The function checks the test request completion value and calls
- * check_testcase_result for result checking that are specific
- * to a test case.
- */
- static int check_test_result(struct test_data *td)
- {
- struct test_request *test_rq;
- int res = 0;
- static int run;
- 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);
- res = -EINVAL;
- goto err;
- }
- if ((test_rq->req_result < 0) && !test_rq->is_err_expected) {
- test_pr_err(
- "%s: rq %d completed with err, not as expected",
- __func__, test_rq->req_id);
- res = -EINVAL;
- goto err;
- }
- if ((test_rq->req_result == 0) && test_rq->is_err_expected) {
- test_pr_err("%s: rq %d succeeded, not as expected",
- __func__, test_rq->req_id);
- res = -EINVAL;
- goto err;
- }
- if (rq_data_dir(test_rq->rq) == READ) {
- res = compare_buffer_to_pattern(test_rq);
- if (res) {
- test_pr_err("%s: read pattern not as expected",
- __func__);
- res = -EINVAL;
- goto err;
- }
- }
- }
- if (td->test_info.check_test_result_fn) {
- res = td->test_info.check_test_result_fn(td);
- if (res)
- goto err;
- }
- test_pr_info("%s: %s, run# %03d, PASSED",
- __func__, get_test_case_str(td), ++run);
- td->test_result = TEST_PASSED;
- return 0;
- err:
- test_pr_err("%s: %s, run# %03d, FAILED",
- __func__, get_test_case_str(td), ++run);
- td->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)
- {
- int ret = 0;
- if (td->test_info.prepare_test_fn) {
- ret = td->test_info.prepare_test_fn(td);
- return ret;
- }
- return 0;
- }
- /* Run the test */
- static int run_test(struct test_data *td)
- {
- int ret = 0;
- if (td->test_info.run_test_fn) {
- ret = td->test_info.run_test_fn(td);
- return ret;
- }
- blk_run_queue(td->req_q);
- return 0;
- }
- /*
- * 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(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
- * and remove it from the packed list
- */
- if (!test_rq->req_completed) {
- test_pr_info(
- "%s: Freeing memory of an uncompleted request",
- __func__);
- list_del_init(&test_rq->rq->queuelist);
- while ((bio = test_rq->rq->bio) != NULL) {
- test_rq->rq->bio = bio->bi_next;
- bio_put(bio);
- }
- }
- blk_put_request(test_rq->rq);
- kfree(test_rq->bios_buffer);
- kfree(test_rq);
- }
- }
- /*
- * 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)
- {
- int ret = 0;
- if (td->test_info.post_test_fn)
- ret = td->test_info.post_test_fn(td);
- ptd->test_info.testcase = 0;
- ptd->test_state = TEST_IDLE;
- free_test_requests(td);
- return ret;
- }
- /*
- * The timer verifies that the test will be completed even if we don't get
- * the completion callback for all the requests.
- */
- static void test_timeout_handler(unsigned long data)
- {
- struct test_data *td = (struct test_data *)data;
- test_pr_info("%s: TIMEOUT timer expired", __func__);
- td->test_state = TEST_COMPLETED;
- wake_up(&td->wait_q);
- return;
- }
- static unsigned int get_timeout_msec(struct test_data *td)
- {
- if (td->test_info.timeout_msec)
- return td->test_info.timeout_msec;
- else
- return TIMEOUT_TIMER_MS;
- }
- /**
- * test_iosched_start_test() - Prepares and runs the test.
- * The members test_duration and test_byte_count of the input
- * parameter t_info are modified by this function.
- * @t_info: the current test testcase and callbacks
- * functions
- *
- * The function also checks the test result upon test completion
- */
- int test_iosched_start_test(struct test_info *t_info)
- {
- int ret = 0;
- unsigned timeout_msec;
- int counter = 0;
- char *test_name = NULL;
- if (!ptd)
- return -ENODEV;
- if (!t_info) {
- ptd->test_result = TEST_FAILED;
- return -EINVAL;
- }
- do {
- if (ptd->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);
- if (ptd->test_state != TEST_IDLE) {
- test_pr_info(
- "%s: Another test is running, try again later",
- __func__);
- spin_unlock(&ptd->lock);
- return -EBUSY;
- }
- if (ptd->start_sector == 0) {
- test_pr_err("%s: Invalid start sector", __func__);
- ptd->test_result = TEST_FAILED;
- spin_unlock(&ptd->lock);
- return -EINVAL;
- }
- memcpy(&ptd->test_info, t_info, sizeof(struct test_info));
- ptd->test_result = TEST_NO_RESULT;
- ptd->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;
- ptd->ignore_round = false;
- ptd->fs_wr_reqs_during_test = false;
- ptd->test_state = TEST_RUNNING;
- spin_unlock(&ptd->lock);
- /*
- * Give an already dispatch request from
- * FS a chanse to complete
- */
- msleep(2000);
- timeout_msec = get_timeout_msec(ptd);
- mod_timer(&ptd->timeout_timer, jiffies +
- msecs_to_jiffies(timeout_msec));
- if (ptd->test_info.get_test_case_str_fn)
- test_name = ptd->test_info.get_test_case_str_fn(ptd);
- else
- test_name = "Unknown testcase";
- test_pr_info("%s: Starting test %s", __func__, test_name);
- ret = prepare_test(ptd);
- if (ret) {
- test_pr_err("%s: failed to prepare the test\n",
- __func__);
- goto error;
- }
- ptd->test_info.test_duration = ktime_get();
- ret = run_test(ptd);
- if (ret) {
- test_pr_err("%s: failed to run the test\n", __func__);
- goto error;
- }
- test_pr_info("%s: Waiting for the test completion", __func__);
- wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
- 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",
- __func__);
- goto error;
- }
- ret = post_test(ptd);
- if (ret) {
- test_pr_err("%s: post_test failed\n", __func__);
- goto error;
- }
- /*
- * Wakeup the queue thread to fetch FS requests that might got
- * postponded due to the test
- */
- blk_run_queue(ptd->req_q);
- if (ptd->ignore_round)
- test_pr_info(
- "%s: Round canceled (Got wr reqs in the middle)",
- __func__);
- if (++counter == TEST_MAX_TESTCASE_ROUNDS) {
- test_pr_info("%s: Too many rounds, did not succeed...",
- __func__);
- ptd->test_result = TEST_FAILED;
- }
- } while ((ptd->ignore_round) && (counter < TEST_MAX_TESTCASE_ROUNDS));
- if (ptd->test_result == TEST_PASSED)
- return 0;
- else
- return -EINVAL;
- error:
- post_test(ptd);
- ptd->test_result = TEST_FAILED;
- return ret;
- }
- EXPORT_SYMBOL(test_iosched_start_test);
- /**
- * test_iosched_register() - register a block device test
- * utility.
- * @bdt: the block device test type to register
- */
- void test_iosched_register(struct blk_dev_test_type *bdt)
- {
- spin_lock(&blk_dev_test_list_lock);
- list_add_tail(&bdt->list, &blk_dev_test_list);
- spin_unlock(&blk_dev_test_list_lock);
- }
- EXPORT_SYMBOL_GPL(test_iosched_register);
- /**
- * test_iosched_unregister() - unregister a block device test
- * utility.
- * @bdt: the block device test type to unregister
- */
- void test_iosched_unregister(struct blk_dev_test_type *bdt)
- {
- spin_lock(&blk_dev_test_list_lock);
- list_del_init(&bdt->list);
- spin_unlock(&blk_dev_test_list_lock);
- }
- EXPORT_SYMBOL_GPL(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)
- {
- if (!ptd)
- return;
- ptd->test_result = test_result;
- }
- EXPORT_SYMBOL(test_iosched_set_test_result);
- /**
- * test_iosched_set_ignore_round() - Set the ignore_round flag
- * @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)
- {
- if (!ptd)
- return;
- ptd->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)
- {
- if (!ptd)
- return NULL;
- 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);
- static int test_debugfs_init(struct test_data *td)
- {
- td->debug.debug_root = debugfs_create_dir("test-iosched", NULL);
- if (!td->debug.debug_root)
- return -ENOENT;
- td->debug.debug_tests_root = debugfs_create_dir("tests",
- td->debug.debug_root);
- if (!td->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)
- goto err;
- td->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)
- goto err;
- td->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)
- goto err;
- return 0;
- err:
- debugfs_remove_recursive(td->debug.debug_root);
- return -ENOENT;
- }
- static void test_debugfs_cleanup(struct test_data *td)
- {
- debugfs_remove_recursive(td->debug.debug_root);
- }
- static void print_req(struct request *req)
- {
- struct bio *bio;
- struct test_request *test_rq;
- if (!req)
- return;
- test_rq = (struct test_request *)req->elv.priv[0];
- if (test_rq) {
- test_pr_debug("%s: Dispatch request %d: __sector=0x%lx",
- __func__, test_rq->req_id, (unsigned long)req->__sector);
- test_pr_debug("%s: nr_phys_segments=%d, num_of_sectors=%d",
- __func__, req->nr_phys_segments, blk_rq_sectors(req));
- bio = req->bio;
- test_pr_debug("%s: bio: bi_size=%d, bi_sector=0x%lx",
- __func__, bio->bi_size,
- (unsigned long)bio->bi_sector);
- while ((bio = bio->bi_next) != NULL) {
- test_pr_debug("%s: bio: bi_size=%d, bi_sector=0x%lx",
- __func__, bio->bi_size,
- (unsigned long)bio->bi_sector);
- }
- }
- }
- static void test_merged_requests(struct request_queue *q,
- struct request *rq, struct request *next)
- {
- 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);
- ptd->test_info.test_byte_count += test_rq->buf_size;
- 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
- * a request that was queued by the FS to keep the card functional.
- */
- static int test_dispatch_requests(struct request_queue *q, int force)
- {
- struct test_data *td = q->elevator->elevator_data;
- struct request *rq = NULL;
- int ret = 0;
- switch (td->test_state) {
- case TEST_IDLE:
- if (!list_empty(&td->queue)) {
- rq = list_entry(td->queue.next, struct request,
- queuelist);
- list_del_init(&rq->queuelist);
- elv_dispatch_sort(q, rq);
- ret = 1;
- goto exit;
- }
- break;
- case TEST_RUNNING:
- 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:
- break;
- }
- exit:
- return ret;
- }
- static void test_add_request(struct request_queue *q, struct request *rq)
- {
- struct test_data *td = q->elevator->elevator_data;
- list_add_tail(&rq->queuelist, &td->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)) {
- test_pr_debug("%s: got WRITE req in the middle of the test",
- __func__);
- td->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;
- if (rq->queuelist.prev == &td->queue)
- return NULL;
- return list_entry(rq->queuelist.prev, struct request, queuelist);
- }
- static struct request *
- test_latter_request(struct request_queue *q, struct request *rq)
- {
- struct test_data *td = q->elevator->elevator_data;
- if (rq->queuelist.next == &td->queue)
- return NULL;
- return list_entry(rq->queuelist.next, struct request, queuelist);
- }
- static void *test_init_queue(struct request_queue *q)
- {
- struct blk_dev_test_type *__bdt;
- ptd = kmalloc_node(sizeof(struct test_data), GFP_KERNEL,
- q->node);
- if (!ptd) {
- test_pr_err("%s: failed to allocate test data", __func__);
- return NULL;
- }
- 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;
- setup_timer(&ptd->timeout_timer, test_timeout_handler,
- (unsigned long)ptd);
- spin_lock_init(&ptd->lock);
- if (test_debugfs_init(ptd)) {
- test_pr_err("%s: Failed to create debugfs files", __func__);
- return NULL;
- }
- list_for_each_entry(__bdt, &blk_dev_test_list, list)
- __bdt->init_fn();
- return ptd;
- }
- static void test_exit_queue(struct elevator_queue *e)
- {
- struct test_data *td = e->elevator_data;
- struct blk_dev_test_type *__bdt;
- BUG_ON(!list_empty(&td->queue));
- list_for_each_entry(__bdt, &blk_dev_test_list, list)
- __bdt->exit_fn();
- test_debugfs_cleanup(td);
- 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);
- test_rq->rq->cmd_flags |= REQ_URGENT;
- 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,
- .elevator_add_req_fn = test_add_request,
- .elevator_former_req_fn = test_former_request,
- .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,
- };
- static int __init test_init(void)
- {
- elv_register(&elevator_test_iosched);
- return 0;
- }
- static void __exit test_exit(void)
- {
- elv_unregister(&elevator_test_iosched);
- }
- module_init(test_init);
- module_exit(test_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Test IO scheduler");
|