/* Copyright (c) 2012-2015, 2017 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.
 *
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt"\n"

/* 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


static DEFINE_MUTEX(blk_dev_test_list_lock);
static LIST_HEAD(blk_dev_test_list);


/**
 * test_iosched_mark_test_completion() - Wakeup the debugfs
 * thread, waiting on the test completion
 */
void test_iosched_mark_test_completion(struct test_iosched *tios)
{
	if (!tios)
		return;

	pr_info("%s: mark test is completed, test_count=%d, ", __func__,
		tios->test_count);
	pr_info("%s: urgent_count=%d, reinsert_count=%d,", __func__,
		tios->urgent_count, tios->reinsert_count);

	tios->test_state = TEST_COMPLETED;
	wake_up(&tios->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(struct test_iosched *tios)
{
	struct test_request *test_rq;

	if (!tios)
		goto exit;

	if (tios->test_info.check_test_completion_fn &&
		!tios->test_info.check_test_completion_fn(tios))
		goto exit;

	list_for_each_entry(test_rq, &tios->dispatched_queue, queuelist)
		if (!test_rq->req_completed)
			goto exit;

	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__,
			tios->test_count, tios->reinsert_count);
		pr_info("%s: dispatched_count=%d, urgent_count=%d", __func__,
			tios->dispatched_count,
			tios->urgent_count);
		goto exit;
	}

	tios->test_info.test_duration = ktime_sub(ktime_get(),
		tios->test_info.test_duration);

	test_iosched_mark_test_completion(tios);

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);
}

void test_iosched_free_test_req_data_buffer(struct test_request *test_rq)
{
	int i;

	if (!test_rq)
		return;

	for (i = 0; i < BLK_MAX_SEGMENTS; i++)
		if (test_rq->bios_buffer[i]) {
			free_page((unsigned long)test_rq->bios_buffer[i]);
			test_rq->bios_buffer[i] = NULL;
		}
}
EXPORT_SYMBOL(test_iosched_free_test_req_data_buffer);

/*
 * 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;
	struct test_iosched *tios = rq->q->elevator->elevator_data;
	test_rq = (struct test_request *)rq->elv.priv[0];
	BUG_ON(!test_rq);

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

/**
 * 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(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;
	unsigned long flags;

	if (!tios)
		return -ENODEV;

	bio = bio_alloc(GFP_KERNEL, 0);
	if (!bio) {
		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_iter.bi_size = nr_sects << 9;
		bio->bi_iter.bi_sector = start_sec;
		break;
	default:
		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(tios->req_q, rw_flags, GFP_KERNEL);
	if (!rq) {
		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) {
		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 = 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_irqsave(tios->req_q->queue_lock, flags);
	list_add_tail(&test_rq->queuelist, &tios->test_queue);
	tios->test_count++;
	spin_unlock_irqrestore(tios->req_q->queue_lock, flags);

	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(
	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;
	struct bio *bio = NULL;
	int i;
	int ret;

	if (!tios)
		return NULL;

	rq = blk_get_request(tios->req_q, direction, GFP_KERNEL);
	if (!rq) {
		pr_err("%s: Failed to allocate a request", __func__);
		return NULL;
	}

	test_rq = kzalloc(sizeof(struct test_request), GFP_KERNEL);
	if (!test_rq) {
		pr_err("%s: Failed to allocate test request", __func__);
		goto err;
	}

	/* Restrict num_bios value as it may lead to bios_buffer overflow */
	if (num_bios >= BLK_MAX_SEGMENTS) {
		pr_warn("%s: num_bios %d is changed to BLK_MAX_SEGMENTS\n",
				__func__, num_bios);
		num_bios = BLK_MAX_SEGMENTS;
	}

	test_rq->buf_size = TEST_BIO_SIZE * num_bios;
	test_rq->wr_rd_data_pattern = pattern;

	for (i = 0; i < num_bios; i++) {
		test_rq->bios_buffer[i] = (void *)get_zeroed_page(GFP_KERNEL);
		if (!test_rq->bios_buffer[i]) {
			pr_err("%s: failed to kmap page for bio #%d/%d\n",
				__func__, i, num_bios);
			goto free_bios;
		}
		ret = blk_rq_map_kern(tios->req_q, rq, test_rq->bios_buffer[i],
			TEST_BIO_SIZE, GFP_KERNEL);
		if (ret) {
			pr_err("%s: blk_rq_map_kern returned error %d",
				__func__, ret);
			goto free_bios;
		}
		if (direction == WRITE)
			fill_buf_with_pattern(test_rq->bios_buffer[i],
				TEST_BIO_SIZE, pattern);
	}

	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;
	rq->cmd_flags &= ~REQ_IO_STAT;

	if (rq->bio) {
		rq->bio->bi_iter.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;
	}

	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 (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;
	return test_rq;

free_bios:
	test_iosched_free_test_req_data_buffer(test_rq);
	kfree(test_rq);
err:
	blk_put_request(rq);
	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(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;
	unsigned long flags;

	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_irqsave(tios->req_q->queue_lock, flags);
		list_add_tail(&test_rq->queuelist, &tios->test_queue);
		tios->test_count++;
		spin_unlock_irqrestore(tios->req_q->queue_lock, flags);
		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_iosched *tios)
{
	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";
}

/*
 * Verify that the test request data buffer includes the expected
 * pattern
 */
int compare_buffer_to_pattern(struct test_request *test_rq)
{
	int i;
	int j;
	unsigned int *buf;

	/* 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;

	for (i = 0; i < test_rq->buf_size / TEST_BIO_SIZE; i++) {
		buf = test_rq->bios_buffer[i];
		for (j = 0; j < TEST_BIO_SIZE / sizeof(int); j++)
			if ((test_rq->wr_rd_data_pattern ==
				TEST_PATTERN_SEQUENTIAL && buf[j] != j) ||
				(test_rq->wr_rd_data_pattern !=
				TEST_PATTERN_SEQUENTIAL &&
				buf[j] != test_rq->wr_rd_data_pattern)) {
				pr_err("%s: wrong pattern 0x%x in index %d",
					__func__, buf[j], j);
				return -EINVAL;
			}
	}

	return 0;
}
EXPORT_SYMBOL(compare_buffer_to_pattern);

/*
 * 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_iosched *tios)
{
	struct test_request *trq;
	int res = 0;
	static int run;

	list_for_each_entry(trq, &tios->dispatched_queue, queuelist) {
		if (!trq->rq) {
			pr_info("%s: req_id %d is contains empty req",
					__func__, trq->req_id);
			continue;
		}
		if (!trq->req_completed) {
			pr_err("%s: rq %d not completed", __func__,
				    trq->req_id);
			res = -EINVAL;
			goto err;
		}

		if ((trq->req_result < 0) && !trq->is_err_expected) {
			pr_err(
				"%s: rq %d completed with err, not as expected",
				__func__, trq->req_id);
			res = -EINVAL;
			goto err;
		}
		if ((trq->req_result == 0) && trq->is_err_expected) {
			pr_err("%s: rq %d succeeded, not as expected",
				    __func__, trq->req_id);
			res = -EINVAL;
			goto err;
		}
		if (rq_data_dir(trq->rq) == READ) {
			res = compare_buffer_to_pattern(trq);
			if (res) {
				pr_err("%s: read pattern not as expected",
					    __func__);
				res = -EINVAL;
				goto err;
			}
		}
	}

	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(tios), ++run);
	tios->test_result = TEST_PASSED;

	return 0;
err:
	pr_err("%s: %s, run# %03d, 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_iosched *tios)
{
	int ret = 0;

	if (tios->test_info.prepare_test_fn) {
		ret = tios->test_info.prepare_test_fn(tios);
		return ret;
	}

	return 0;
}

/* Run the test */
static int run_test(struct test_iosched *tios)
{
	int ret = 0;

	if (tios->test_info.run_test_fn) {
		ret = tios->test_info.run_test_fn(tios);
		return ret;
	}

	blk_run_queue(tios->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) {
			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);
		test_iosched_free_test_req_data_buffer(test_rq);
		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_iosched *tios)
{
	if (!tios)
		return;

	if (tios->urgent_count) {
		free_test_queue(&tios->urgent_queue);
		tios->urgent_count = 0;
	}
	if (tios->test_count) {
		free_test_queue(&tios->test_queue);
		tios->test_count = 0;
	}
	if (tios->dispatched_count) {
		free_test_queue(&tios->dispatched_queue);
		tios->dispatched_count = 0;
	}
	if (tios->reinsert_count) {
		free_test_queue(&tios->reinsert_queue);
		tios->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_iosched *tios)
{
	int ret = 0;

	if (tios->test_info.post_test_fn)
		ret = tios->test_info.post_test_fn(tios);

	tios->test_info.testcase = 0;
	tios->test_state = TEST_IDLE;

	free_test_requests(tios);

	return ret;
}

static unsigned int get_timeout_msec(struct test_iosched *tios)
{
	if (tios->test_info.timeout_msec)
		return tios->test_info.timeout_msec;
	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_iosched *tios,
	struct test_info *t_info)
{
	int ret = 0;
	unsigned long timeout;
	int counter = 0;
	char *test_name = NULL;

	if (!tios)
		return -ENODEV;

	if (!t_info) {
		tios->test_result = TEST_FAILED;
		return -EINVAL;
	}

	timeout = msecs_to_jiffies(get_timeout_msec(tios));

	do {
		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(&tios->lock);

		if (tios->test_state != TEST_IDLE) {
			pr_info(
				"%s: Another test is running, try again later",
				__func__);
			spin_unlock(&tios->lock);
			return -EBUSY;
		}

		if (tios->start_sector == 0) {
			pr_err("%s: Invalid start sector", __func__);
			tios->test_result = TEST_FAILED;
			spin_unlock(&tios->lock);
			return -EINVAL;
		}

		memcpy(&tios->test_info, t_info, sizeof(*t_info));

		tios->test_result = TEST_NO_RESULT;
		tios->num_of_write_bios = 0;

		tios->unique_next_req_id = UNIQUE_START_REQ_ID;
		tios->wr_rd_next_req_id = WR_RD_START_REQ_ID;

		tios->ignore_round = false;
		tios->fs_wr_reqs_during_test = false;

		tios->test_state = TEST_RUNNING;

		spin_unlock(&tios->lock);
		/*
		 * Give an already dispatch request from
		 * FS a chanse to complete
		 */
		msleep(2000);

		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(tios);
		if (ret) {
			pr_err("%s: failed to prepare the test",
				    __func__);
			goto error;
		}

		tios->test_info.test_duration = ktime_get();
		ret = run_test(tios);
		if (ret) {
			pr_err("%s: failed to run the test", __func__);
			goto error;
		}

		pr_info("%s: Waiting for the test completion", __func__);

		ret = wait_event_interruptible_timeout(tios->wait_q,
			(tios->test_state == TEST_COMPLETED), timeout);
		if (ret <= 0) {
			tios->test_state = TEST_COMPLETED;
			if (!ret)
				pr_info("%s: Test timeout\n", __func__);
			else
				pr_err("%s: Test error=%d\n", __func__, ret);
			goto error;
		}

		memcpy(t_info, &tios->test_info, sizeof(*t_info));

		ret = check_test_result(tios);
		if (ret) {
			pr_err("%s: check_test_result failed", __func__);
			goto error;
		}

		ret = post_test(tios);
		if (ret) {
			pr_err("%s: post_test failed", __func__);
			goto error;
		}

		/*
		 * Wakeup the queue thread to fetch FS requests that might got
		 * postponded due to the test
		 */
		blk_run_queue(tios->req_q);

		if (tios->ignore_round)
			pr_info(
			"%s: Round canceled (Got wr reqs in the middle)",
			__func__);

		if (++counter == TEST_MAX_TESTCASE_ROUNDS) {
			pr_info("%s: Too many rounds, did not succeed...",
			     __func__);
			tios->test_result = TEST_FAILED;
		}

	} while ((tios->ignore_round) &&
		(counter < TEST_MAX_TESTCASE_ROUNDS));

	if (tios->test_result == TEST_PASSED)
		return 0;
	else
		return -EINVAL;

error:
	post_test(tios);
	tios->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)
{
	if (!bdt)
		return;

	mutex_lock(&blk_dev_test_list_lock);
	list_add_tail(&bdt->list, &blk_dev_test_list);
	mutex_unlock(&blk_dev_test_list_lock);

}
EXPORT_SYMBOL(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)
{
	if (!bdt)
		return;

	mutex_lock(&blk_dev_test_list_lock);
	list_del_init(&bdt->list);
	mutex_unlock(&blk_dev_test_list_lock);
}
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(struct test_iosched *tios,
	int test_result)
{
	if (!tios)
		return;

	tios->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(struct test_iosched *tios,
	bool ignore_round)
{
	if (!tios)
		return;

	tios->ignore_round = ignore_round;
}
EXPORT_SYMBOL(test_iosched_set_ignore_round);

static int test_debugfs_init(struct test_iosched *tios)
{
	char name[2*BDEVNAME_SIZE];


	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);

	tios->debug.debug_root = debugfs_create_dir(name, NULL);
	if (!tios->debug.debug_root)
		return -ENOENT;

	tios->debug.debug_tests_root = debugfs_create_dir("tests",
		tios->debug.debug_root);
	if (!tios->debug.debug_tests_root)
		goto err;

	tios->debug.debug_utils_root = debugfs_create_dir("utils",
		tios->debug.debug_root);
	if (!tios->debug.debug_utils_root)
		goto err;

	tios->debug.debug_test_result = debugfs_create_u32(
					"test_result",
					S_IRUGO | S_IWUGO,
					tios->debug.debug_utils_root,
					&tios->test_result);
	if (!tios->debug.debug_test_result)
		goto err;

	tios->debug.start_sector = debugfs_create_u32(
					"start_sector",
					S_IRUGO | S_IWUGO,
					tios->debug.debug_utils_root,
					&tios->start_sector);
	if (!tios->debug.start_sector)
		goto err;

	tios->debug.sector_range = debugfs_create_u32(
						"sector_range",
						S_IRUGO | S_IWUGO,
						tios->debug.debug_utils_root,
						&tios->sector_range);
	if (!tios->debug.sector_range)
		goto err;

	return 0;

err:
	debugfs_remove_recursive(tios->debug.debug_root);
	return -ENOENT;
}

static void test_debugfs_cleanup(struct test_iosched *tios)
{
	debugfs_remove_recursive(tios->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) {
		pr_debug("%s: Dispatch request %d: __sector=0x%lx",
		       __func__, test_rq->req_id, (unsigned long)req->__sector);
		pr_debug("%s: nr_phys_segments=%d, num_of_sectors=%d",
		       __func__, req->nr_phys_segments, blk_rq_sectors(req));
		bio = req->bio;
		pr_debug("%s: bio: bi_size=%d, bi_sector=0x%lx",
			      __func__, bio->bi_iter.bi_size,
			      (unsigned long)bio->bi_iter.bi_sector);
		while ((bio = bio->bi_next) != NULL) {
			pr_debug("%s: bio: bi_size=%d, bi_sector=0x%lx",
				      __func__, bio->bi_iter.bi_size,
				      (unsigned long)bio->bi_iter.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 the 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;
	struct test_iosched *tios = q->elevator->elevator_data;
	unsigned long flags;

	if (!tios)
		goto err;

	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(&tios->lock, flags);
			goto err;
		}
		list_move_tail(&test_rq->queuelist,
			&tios->dispatched_queue);
		tios->dispatched_count++;
		(*count)--;
		spin_unlock_irqrestore(&tios->lock, flags);

		print_req(rq);
		elv_dispatch_sort(q, rq);
		tios->test_info.test_byte_count += test_rq->buf_size;
		ret = 1;
		goto err;
	}
	spin_unlock_irqrestore(&tios->lock, flags);

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_iosched *tios = q->elevator->elevator_data;
	struct request *rq = NULL;
	int ret = 0;

	switch (tios->test_state) {
	case TEST_IDLE:
		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;
			goto exit;
		}
		break;
	case TEST_RUNNING:
		if (test_dispatch_from(q, &tios->urgent_queue,
				       &tios->urgent_count)) {
			pr_debug("%s: Dispatched from urgent_count=%d",
					__func__, tios->urgent_count);
			ret = 1;
			goto exit;
		}
		if (test_dispatch_from(q, &tios->reinsert_queue,
				       &tios->reinsert_count)) {
			pr_debug("%s: Dispatched from reinsert_count=%d",
					__func__, tios->reinsert_count);
			ret = 1;
			goto exit;
		}
		if (test_dispatch_from(q, &tios->test_queue,
			&tios->test_count)) {
			pr_debug("%s: Dispatched from test_count=%d",
					__func__, tios->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_iosched *tios = q->elevator->elevator_data;

	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 &&
		tios->test_state == TEST_RUNNING) {
		pr_debug("%s: got WRITE req in the middle of the test",
			__func__);
		tios->fs_wr_reqs_during_test = true;
	}
}

static struct request *
test_former_request(struct request_queue *q, struct request *rq)
{
	struct test_iosched *tios = q->elevator->elevator_data;

	if (rq->queuelist.prev == &tios->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_iosched *tios = q->elevator->elevator_data;

	if (rq->queuelist.next == &tios->queue)
		return NULL;
	return list_entry(rq->queuelist.next, struct request, queuelist);
}

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;
	unsigned long flags;

	eq = elevator_alloc(q, e);
	if (!eq)
		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;
	}
	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;

	/* Traverse the block device test list and init matches */
	mutex_lock(&blk_dev_test_list_lock);

	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);

	/* 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_irqsave(q->queue_lock, flags);
	q->elevator = eq;
	spin_unlock_irqrestore(q->queue_lock, flags);

	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_iosched *tios = e->elevator_data;
	struct blk_dev_test_type *__bdt;

	BUG_ON(!list_empty(&tios->queue));

	list_for_each_entry(__bdt, &blk_dev_test_list, list)
		__bdt->exit_fn(tios);

	test_debugfs_cleanup(tios);

	kfree(tios);
}

/**
 * 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_iosched *tios,
	struct test_request *test_rq)
{
	unsigned long flags;

	if (!tios)
		return;

	spin_lock_irqsave(&tios->lock, flags);
	test_rq->rq->cmd_flags |= REQ_URGENT;
	list_add_tail(&test_rq->queuelist, &tios->urgent_queue);
	tios->urgent_count++;
	spin_unlock_irqrestore(&tios->lock, flags);
}
EXPORT_SYMBOL(test_iosched_add_urgent_req);

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_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");
