diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c index a083efe1e09f..df7bbb2d4c03 100644 --- a/drivers/mmc/card/mmc_block_test.c +++ b/drivers/mmc/card/mmc_block_test.c @@ -82,6 +82,25 @@ enum mmc_block_test_testcases { TEST_CMD23_BITS_16TO29_SET, TEST_CMD23_HDR_BLK_NOT_IN_COUNT, INVALID_CMD_MAX_TESTCASE = TEST_CMD23_HDR_BLK_NOT_IN_COUNT, + + /* + * Start of packing control test group. + * in these next testcases the abbreviation FB = followed by + */ + PACKING_CONTROL_MIN_TESTCASE, + TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ = + PACKING_CONTROL_MIN_TESTCASE, + TEST_PACKING_EXP_N_OVER_TRIGGER, + TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ, + TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N, + TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER, + TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS, + TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS, + TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER, + TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER, + TEST_PACK_MIX_PACKED_NO_PACKED_PACKED, + TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED, + PACKING_CONTROL_MAX_TESTCASE = TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED, }; enum mmc_block_test_group { @@ -90,6 +109,7 @@ enum mmc_block_test_group { TEST_SEND_WRITE_PACKING_GROUP, TEST_ERR_CHECK_GROUP, TEST_SEND_INVALID_GROUP, + TEST_PACKING_CONTROL_GROUP, }; struct mmc_block_test_debug { @@ -97,6 +117,7 @@ struct mmc_block_test_debug { struct dentry *err_check_test; struct dentry *send_invalid_packed_test; struct dentry *random_test_seed; + struct dentry *packing_control_test; }; struct mmc_block_test_data { @@ -453,6 +474,28 @@ static char *get_test_case_str(struct test_data *td) return "Test invalid - cmd23 bits [16-29] set"; case TEST_CMD23_HDR_BLK_NOT_IN_COUNT: return "Test invalid - cmd23 header block not in count"; + case TEST_PACKING_EXP_N_OVER_TRIGGER: + return "\nTest packing control - pack n"; + case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ: + return "\nTest packing control - pack n followed by read"; + case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N: + return "\nTest packing control - pack n followed by flush"; + case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ: + return "\nTest packing control - pack one followed by read"; + case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER: + return "\nTest packing control - pack threshold"; + case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS: + return "\nTest packing control - no packing"; + case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS: + return "\nTest packing control - no packing, trigger requests"; + case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER: + return "\nTest packing control - no pack, trigger-read-trigger"; + case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER: + return "\nTest packing control- no pack, trigger-flush-trigger"; + case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED: + return "\nTest packing control - mix: pack -> no pack -> pack"; + case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED: + return "\nTest packing control - mix: no pack->pack->no pack"; default: return "Unknown testcase"; } @@ -504,7 +547,7 @@ static int check_wr_packing_statistics(struct test_data *td) stop_reason = mmc_packed_stats->pack_stop_reason; - for (i = 1 ; i <= max_packed_reqs ; ++i) { + for (i = 1; i <= max_packed_reqs; ++i) { if (mmc_packed_stats->packing_events[i] != expected_stats.packing_events[i]) { test_pr_err( @@ -709,7 +752,7 @@ static int prepare_request_add_write_reqs(struct test_data *td, test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__, num_requests, td->wr_rd_next_req_id); - for (i = 1 ; i <= num_requests ; i++) { + for (i = 1; i <= num_requests; i++) { start_sec = td->start_sector + 4096 * td->num_of_write_bios; if (is_random) pseudo_rnd_num_of_bios(bio_seed, &num_bios); @@ -824,6 +867,185 @@ static int prepare_packed_requests(struct test_data *td, int is_err_expected, return 0; } +/* + * Prepare the write, read and flush requests for the packing control + * testcases + */ +static int prepare_packed_control_tests_requests(struct test_data *td, + int is_err_expected, int num_requests, int is_random) +{ + int ret = 0; + struct mmc_queue *mq; + int max_packed_reqs; + int temp_num_req = num_requests; + struct request_queue *req_q; + int test_packed_trigger; + int num_packed_reqs; + + if (!td) { + test_pr_err("%s: NULL td\n", __func__); + return -EINVAL; + } + + req_q = td->req_q; + + if (!req_q) { + test_pr_err("%s: NULL request queue\n", __func__); + return -EINVAL; + } + + mq = req_q->queuedata; + if (!mq) { + test_pr_err("%s: NULL mq", __func__); + return -EINVAL; + } + + max_packed_reqs = mq->card->ext_csd.max_packed_writes; + test_packed_trigger = mq->num_wr_reqs_to_start_packing; + num_packed_reqs = num_requests - test_packed_trigger; + + if (mbtd->random_test_seed == 0) { + mbtd->random_test_seed = + (unsigned int)(get_jiffies_64() & 0xFFFF); + test_pr_info("%s: got seed from jiffies %d", + __func__, mbtd->random_test_seed); + } + + mmc_blk_init_packed_statistics(mq->card); + + if (td->test_info.testcase == + TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) { + temp_num_req = num_requests; + num_requests = test_packed_trigger - 1; + } + + /* Verify that the packing is disabled before starting the test */ + mq->wr_packing_enabled = false; + mq->num_of_potential_packed_wr_reqs = 0; + + if (td->test_info.testcase == TEST_PACK_MIX_PACKED_NO_PACKED_PACKED) { + mq->num_of_potential_packed_wr_reqs = test_packed_trigger + 1; + mq->wr_packing_enabled = true; + num_requests = test_packed_trigger + 2; + } + + ret = prepare_request_add_write_reqs(td, num_requests, is_err_expected, + is_random); + if (ret) + goto exit; + + if (td->test_info.testcase == TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED) + num_requests = temp_num_req; + + memset((void *)mbtd->exp_packed_stats.pack_stop_reason, 0, + sizeof(mbtd->exp_packed_stats.pack_stop_reason)); + memset(mbtd->exp_packed_stats.packing_events, 0, + (max_packed_reqs + 1) * sizeof(u32)); + + switch (td->test_info.testcase) { + case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ: + case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ: + ret = prepare_request_add_read(td); + if (ret) + goto exit; + + mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1; + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + break; + case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N: + ret = prepare_request_add_flush(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, num_packed_reqs, + is_err_expected, is_random); + if (ret) + goto exit; + + mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1; + mbtd->exp_packed_stats.pack_stop_reason[FLUSH_OR_DISCARD] = 1; + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 2; + break; + case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER: + ret = prepare_request_add_read(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, test_packed_trigger, + is_err_expected, is_random); + if (ret) + goto exit; + + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + break; + case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER: + ret = prepare_request_add_flush(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, test_packed_trigger, + is_err_expected, is_random); + if (ret) + goto exit; + + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + break; + case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED: + ret = prepare_request_add_read(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, test_packed_trigger-1, + is_err_expected, is_random); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, num_requests, + is_err_expected, is_random); + if (ret) + goto exit; + + mbtd->exp_packed_stats.packing_events[num_requests] = 1; + mbtd->exp_packed_stats.packing_events[num_requests-1] = 1; + mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1; + mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1; + break; + case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED: + ret = prepare_request_add_read(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, num_requests, + is_err_expected, is_random); + if (ret) + goto exit; + + ret = prepare_request_add_read(td); + if (ret) + goto exit; + + ret = prepare_request_add_write_reqs(td, test_packed_trigger-1, + is_err_expected, is_random); + if (ret) + goto exit; + + mbtd->exp_packed_stats.pack_stop_reason[WRONG_DATA_DIR] = 1; + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + break; + case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS: + case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS: + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + break; + default: + mbtd->exp_packed_stats.pack_stop_reason[EMPTY_QUEUE] = 1; + mbtd->exp_packed_stats.packing_events[num_packed_reqs] = 1; + } + mbtd->num_requests = num_requests; + +exit: + return ret; +} + /* * Prepare requests for the TEST_RET_PARTIAL_FOLLOWED_BY_ABORT testcase. * In this testcase we have mixed error expectations from different @@ -847,13 +1069,14 @@ static int prepare_partial_followed_by_abort(struct test_data *td, mmc_blk_init_packed_statistics(mq->card); - for (i = 1 ; i <= num_requests ; i++) { + for (i = 1; i <= num_requests; i++) { if (i > (num_requests / 2)) is_err_expected = 1; - start_address = td->start_sector + 4096*td->num_of_write_bios; + start_address = td->start_sector + 4096 * td->num_of_write_bios; ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE, - start_address, (i%5)+1, TEST_PATTERN_5A, NULL); + start_address, (i % 5) + 1, TEST_PATTERN_5A, + NULL); if (ret) { test_pr_err("%s: failed to add a write request", __func__); @@ -887,6 +1110,8 @@ static int get_num_requests(struct test_data *td) int num_requests; int min_num_requests = 2; int is_random = mbtd->is_random; + int max_for_double; + int test_packed_trigger; req_q = test_iosched_get_req_queue(); if (req_q) @@ -903,16 +1128,52 @@ static int get_num_requests(struct test_data *td) max_num_requests = mq->card->ext_csd.max_packed_writes; num_requests = max_num_requests - 2; + test_packed_trigger = mq->num_wr_reqs_to_start_packing; + + /* + * Here max_for_double is intended for packed control testcases + * in which we issue many write requests. It's purpose is to prevent + * exceeding max number of req_queue requests. + */ + max_for_double = max_num_requests - 10; + + if (td->test_info.testcase == + TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS) + /* Don't expect packing, so issue up to trigger-1 reqs */ + num_requests = test_packed_trigger - 1; if (is_random) { if (td->test_info.testcase == TEST_RET_PARTIAL_FOLLOWED_BY_ABORT) + /* + * Here we don't want num_requests to be less than 1 + * as a consequence of division by 2. + */ min_num_requests = 3; + if (td->test_info.testcase == + TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS) + /* Don't expect packing, so issue up to trigger reqs */ + max_num_requests = test_packed_trigger; + num_requests = pseudo_random_seed(seed, min_num_requests, max_num_requests - 1); } + if (td->test_info.testcase == + TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS) + num_requests -= test_packed_trigger; + + if (td->test_info.testcase == TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N) + num_requests = + num_requests > max_for_double ? max_for_double : num_requests; + + if (mbtd->test_group == TEST_PACKING_CONTROL_GROUP) + num_requests += test_packed_trigger; + + if (td->test_info.testcase == TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS) + num_requests = test_packed_trigger; + return num_requests; } @@ -928,6 +1189,7 @@ static int prepare_test(struct test_data *td) int num_requests = 0; int ret = 0; int is_random = mbtd->is_random; + int test_packed_trigger = mq->num_wr_reqs_to_start_packing; if (!mq) { test_pr_err("%s: NULL mq", __func__); @@ -996,6 +1258,33 @@ static int prepare_test(struct test_data *td) case TEST_HDR_CMD23_PACKED_BIT_SET: ret = prepare_packed_requests(td, 1, num_requests, is_random); break; + case TEST_PACKING_EXP_N_OVER_TRIGGER: + case TEST_PACKING_EXP_N_OVER_TRIGGER_FB_READ: + case TEST_PACKING_NOT_EXP_TRIGGER_REQUESTS: + case TEST_PACKING_NOT_EXP_LESS_THAN_TRIGGER_REQUESTS: + case TEST_PACK_MIX_PACKED_NO_PACKED_PACKED: + case TEST_PACK_MIX_NO_PACKED_PACKED_NO_PACKED: + ret = prepare_packed_control_tests_requests(td, 0, num_requests, + is_random); + break; + case TEST_PACKING_EXP_THRESHOLD_OVER_TRIGGER: + ret = prepare_packed_control_tests_requests(td, 0, + max_num_requests, is_random); + break; + case TEST_PACKING_EXP_ONE_OVER_TRIGGER_FB_READ: + ret = prepare_packed_control_tests_requests(td, 0, + test_packed_trigger + 1, + is_random); + break; + case TEST_PACKING_EXP_N_OVER_TRIGGER_FLUSH_N: + ret = prepare_packed_control_tests_requests(td, 0, num_requests, + is_random); + break; + case TEST_PACKING_NOT_EXP_TRIGGER_READ_TRIGGER: + case TEST_PACKING_NOT_EXP_TRIGGER_FLUSH_TRIGGER: + ret = prepare_packed_control_tests_requests(td, 0, + test_packed_trigger, is_random); + break; default: test_pr_info("%s: Invalid test case...", __func__); return -EINVAL; @@ -1083,6 +1372,9 @@ static int validate_packed_commands_settings(void) /* disable the packing control */ host->caps2 &= ~MMC_CAP2_PACKED_WR_CONTROL; break; + case TEST_PACKING_CONTROL_GROUP: + host->caps2 |= MMC_CAP2_PACKED_WR_CONTROL; + break; default: break; } @@ -1134,12 +1426,12 @@ static ssize_t send_write_packing_test_write(struct file *file, mbtd->test_info.get_test_case_str_fn = get_test_case_str; mbtd->test_info.post_test_fn = post_test; - for (i = 0 ; i < number ; ++i) { + for (i = 0; i < number; ++i) { test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number); test_pr_info("%s: ====================", __func__); - for (j = SEND_WRITE_PACKING_MIN_TESTCASE ; - j <= SEND_WRITE_PACKING_MAX_TESTCASE ; j++) { + for (j = SEND_WRITE_PACKING_MIN_TESTCASE; + j <= SEND_WRITE_PACKING_MAX_TESTCASE; j++) { mbtd->test_info.testcase = j; mbtd->is_random = RANDOM_TEST; @@ -1232,7 +1524,7 @@ static ssize_t err_check_test_write(struct file *file, mbtd->test_info.get_test_case_str_fn = get_test_case_str; mbtd->test_info.post_test_fn = post_test; - for (i = 0 ; i < number ; ++i) { + for (i = 0; i < number; ++i) { test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number); test_pr_info("%s: ====================", __func__); @@ -1331,7 +1623,7 @@ static ssize_t send_invalid_packed_test_write(struct file *file, mbtd->test_info.get_test_case_str_fn = get_test_case_str; mbtd->test_info.post_test_fn = post_test; - for (i = 0 ; i < number ; ++i) { + for (i = 0; i < number; ++i) { test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number); test_pr_info("%s: ====================", __func__); @@ -1407,12 +1699,123 @@ const struct file_operations send_invalid_packed_test_ops = { .read = send_invalid_packed_test_read, }; +/* packing_control TEST */ +static ssize_t write_packing_control_test_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret = 0; + int i = 0; + int number = -1; + int j = 0; + struct mmc_queue *mq = test_iosched_get_req_queue()->queuedata; + int max_num_requests = mq->card->ext_csd.max_packed_writes; + int test_successful = 1; + + test_pr_info("%s: -- write_packing_control TEST --", __func__); + + sscanf(buf, "%d", &number); + + if (number <= 0) + number = 1; + + test_pr_info("%s: max_num_requests = %d ", __func__, + max_num_requests); + + memset(&mbtd->test_info, 0, sizeof(struct test_info)); + mbtd->test_group = TEST_PACKING_CONTROL_GROUP; + + if (validate_packed_commands_settings()) + return count; + + mbtd->test_info.data = mbtd; + mbtd->test_info.prepare_test_fn = prepare_test; + mbtd->test_info.check_test_result_fn = check_wr_packing_statistics; + mbtd->test_info.get_test_case_str_fn = get_test_case_str; + + for (i = 0; i < number; ++i) { + test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number); + test_pr_info("%s: ====================", __func__); + + for (j = PACKING_CONTROL_MIN_TESTCASE; + j <= PACKING_CONTROL_MAX_TESTCASE; j++) { + + test_successful = 1; + mbtd->test_info.testcase = j; + mbtd->is_random = RANDOM_TEST; + ret = test_iosched_start_test(&mbtd->test_info); + if (ret) { + test_successful = 0; + break; + } + /* Allow FS requests to be dispatched */ + msleep(1000); + + mbtd->test_info.testcase = j; + mbtd->is_random = NON_RANDOM_TEST; + ret = test_iosched_start_test(&mbtd->test_info); + if (ret) { + test_successful = 0; + break; + } + /* Allow FS requests to be dispatched */ + msleep(1000); + } + + if (!test_successful) + break; + } + + test_pr_info("%s: Completed all the test cases.", __func__); + + return count; +} + +static ssize_t write_packing_control_test_read(struct file *file, + char __user *buffer, + size_t count, + loff_t *offset) +{ + memset((void *)buffer, 0, count); + + snprintf(buffer, count, + "\nwrite_packing_control_test\n" + "=========\n" + "Description:\n" + "This test checks the following scenarios\n" + "- Packing expected - one over trigger\n" + "- Packing expected - N over trigger\n" + "- Packing expected - N over trigger followed by read\n" + "- Packing expected - N over trigger followed by flush\n" + "- Packing expected - threshold over trigger FB by flush\n" + "- Packing not expected - less than trigger\n" + "- Packing not expected - trigger requests\n" + "- Packing not expected - trigger, read, trigger\n" + "- Mixed state - packing -> no packing -> packing\n" + "- Mixed state - no packing -> packing -> no packing\n"); + + if (message_repeat == 1) { + message_repeat = 0; + return strnlen(buffer, count); + } else { + return 0; + } +} + +const struct file_operations write_packing_control_test_ops = { + .open = test_open, + .write = write_packing_control_test_write, + .read = write_packing_control_test_read, +}; + static void mmc_block_test_debugfs_cleanup(void) { debugfs_remove(mbtd->debug.random_test_seed); debugfs_remove(mbtd->debug.send_write_packing_test); debugfs_remove(mbtd->debug.err_check_test); debugfs_remove(mbtd->debug.send_invalid_packed_test); + debugfs_remove(mbtd->debug.packing_control_test); } static int mmc_block_test_debugfs_init(void) @@ -1464,6 +1867,16 @@ static int mmc_block_test_debugfs_init(void) if (!mbtd->debug.send_invalid_packed_test) goto err_nomem; + mbtd->debug.packing_control_test = debugfs_create_file( + "packing_control_test", + S_IRUGO | S_IWUGO, + tests_root, + NULL, + &write_packing_control_test_ops); + + if (!mbtd->debug.packing_control_test) + goto err_nomem; + return 0; err_nomem: