linux/drivers/mmc/core/mmc_test.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright 2007-2008 Pierre Ossman
 */

#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/slab.h>

#include <linux/scatterlist.h>
#include <linux/list.h>

#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/module.h>

#include "core.h"
#include "card.h"
#include "host.h"
#include "bus.h"
#include "mmc_ops.h"

#define RESULT_OK
#define RESULT_FAIL
#define RESULT_UNSUP_HOST
#define RESULT_UNSUP_CARD

#define BUFFER_ORDER
#define BUFFER_SIZE

#define TEST_ALIGN_END

/*
 * Limit the test area size to the maximum MMC HC erase group size.  Note that
 * the maximum SD allocation unit size is just 4MiB.
 */
#define TEST_AREA_MAX_SIZE

/**
 * struct mmc_test_pages - pages allocated by 'alloc_pages()'.
 * @page: first page in the allocation
 * @order: order of the number of pages allocated
 */
struct mmc_test_pages {};

/**
 * struct mmc_test_mem - allocated memory.
 * @arr: array of allocations
 * @cnt: number of allocations
 */
struct mmc_test_mem {};

/**
 * struct mmc_test_area - information for performance tests.
 * @max_sz: test area size (in bytes)
 * @dev_addr: address on card at which to do performance tests
 * @max_tfr: maximum transfer size allowed by driver (in bytes)
 * @max_segs: maximum segments allowed by driver in scatterlist @sg
 * @max_seg_sz: maximum segment size allowed by driver
 * @blocks: number of (512 byte) blocks currently mapped by @sg
 * @sg_len: length of currently mapped scatterlist @sg
 * @mem: allocated memory
 * @sg: scatterlist
 * @sg_areq: scatterlist for non-blocking request
 */
struct mmc_test_area {};

/**
 * struct mmc_test_transfer_result - transfer results for performance tests.
 * @link: double-linked list
 * @count: amount of group of sectors to check
 * @sectors: amount of sectors to check in one group
 * @ts: time values of transfer
 * @rate: calculated transfer rate
 * @iops: I/O operations per second (times 100)
 */
struct mmc_test_transfer_result {};

/**
 * struct mmc_test_general_result - results for tests.
 * @link: double-linked list
 * @card: card under test
 * @testcase: number of test case
 * @result: result of test run
 * @tr_lst: transfer measurements if any as mmc_test_transfer_result
 */
struct mmc_test_general_result {};

/**
 * struct mmc_test_dbgfs_file - debugfs related file.
 * @link: double-linked list
 * @card: card under test
 * @file: file created under debugfs
 */
struct mmc_test_dbgfs_file {};

/**
 * struct mmc_test_card - test information.
 * @card: card under test
 * @scratch: transfer buffer
 * @buffer: transfer buffer
 * @highmem: buffer for highmem tests
 * @area: information for performance tests
 * @gr: pointer to results of current testcase
 */
struct mmc_test_card {};

enum mmc_test_prep_media {};

struct mmc_test_multiple_rw {};

/*******************************************************************/
/*  General helper functions                                       */
/*******************************************************************/

/*
 * Configure correct block size in card
 */
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
{}

static bool mmc_test_card_cmd23(struct mmc_card *card)
{}

static void mmc_test_prepare_sbc(struct mmc_test_card *test,
				 struct mmc_request *mrq, unsigned int blocks)
{}

/*
 * Fill in the mmc_request structure given a set of transfer parameters.
 */
static void mmc_test_prepare_mrq(struct mmc_test_card *test,
	struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
	unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
{}

static int mmc_test_busy(struct mmc_command *cmd)
{}

/*
 * Wait for the card to finish the busy state
 */
static int mmc_test_wait_busy(struct mmc_test_card *test)
{}

/*
 * Transfer a single sector of kernel addressable data
 */
static int mmc_test_buffer_transfer(struct mmc_test_card *test,
	u8 *buffer, unsigned addr, unsigned blksz, int write)
{}

static void mmc_test_free_mem(struct mmc_test_mem *mem)
{}

/*
 * Allocate a lot of memory, preferably max_sz but at least min_sz.  In case
 * there isn't much memory do not exceed 1/16th total lowmem pages.  Also do
 * not exceed a maximum number of segments and try not to make segments much
 * bigger than maximum segment size.
 */
static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
					       unsigned long max_sz,
					       unsigned int max_segs,
					       unsigned int max_seg_sz)
{}

/*
 * Map memory into a scatterlist.  Optionally allow the same memory to be
 * mapped more than once.
 */
static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long size,
			   struct scatterlist *sglist, int repeat,
			   unsigned int max_segs, unsigned int max_seg_sz,
			   unsigned int *sg_len, int min_sg_len)
{}

/*
 * Map memory into a scatterlist so that no pages are contiguous.  Allow the
 * same memory to be mapped more than once.
 */
static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
				       unsigned long sz,
				       struct scatterlist *sglist,
				       unsigned int max_segs,
				       unsigned int max_seg_sz,
				       unsigned int *sg_len)
{}

/*
 * Calculate transfer rate in bytes per second.
 */
static unsigned int mmc_test_rate(uint64_t bytes, struct timespec64 *ts)
{}

/*
 * Save transfer results for future usage
 */
static void mmc_test_save_transfer_result(struct mmc_test_card *test,
	unsigned int count, unsigned int sectors, struct timespec64 ts,
	unsigned int rate, unsigned int iops)
{}

/*
 * Print the transfer rate.
 */
static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
				struct timespec64 *ts1, struct timespec64 *ts2)
{}

/*
 * Print the average transfer rate.
 */
static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
				    unsigned int count, struct timespec64 *ts1,
				    struct timespec64 *ts2)
{}

/*
 * Return the card size in sectors.
 */
static unsigned int mmc_test_capacity(struct mmc_card *card)
{}

/*******************************************************************/
/*  Test preparation and cleanup                                   */
/*******************************************************************/

/*
 * Fill the first couple of sectors of the card with known data
 * so that bad reads/writes can be detected
 */
static int __mmc_test_prepare(struct mmc_test_card *test, int write, int val)
{}

static int mmc_test_prepare_write(struct mmc_test_card *test)
{}

static int mmc_test_prepare_read(struct mmc_test_card *test)
{}

static int mmc_test_cleanup(struct mmc_test_card *test)
{}

/*******************************************************************/
/*  Test execution helpers                                         */
/*******************************************************************/

/*
 * Modifies the mmc_request to perform the "short transfer" tests
 */
static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
	struct mmc_request *mrq, int write)
{}

/*
 * Checks that a normal transfer didn't have any errors
 */
static int mmc_test_check_result(struct mmc_test_card *test,
				 struct mmc_request *mrq)
{}

/*
 * Checks that a "short transfer" behaved as expected
 */
static int mmc_test_check_broken_result(struct mmc_test_card *test,
	struct mmc_request *mrq)
{}

struct mmc_test_req {};

/*
 * Tests nonblock transfer with certain parameters
 */
static void mmc_test_req_reset(struct mmc_test_req *rq)
{}

static struct mmc_test_req *mmc_test_req_alloc(void)
{}

static void mmc_test_wait_done(struct mmc_request *mrq)
{}

static int mmc_test_start_areq(struct mmc_test_card *test,
			       struct mmc_request *mrq,
			       struct mmc_request *prev_mrq)
{}

static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
				      unsigned int dev_addr, int write,
				      int count)
{}

/*
 * Tests a basic transfer with certain parameters
 */
static int mmc_test_simple_transfer(struct mmc_test_card *test,
	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
	unsigned blocks, unsigned blksz, int write)
{}

/*
 * Tests a transfer where the card will fail completely or partly
 */
static int mmc_test_broken_transfer(struct mmc_test_card *test,
	unsigned blocks, unsigned blksz, int write)
{}

/*
 * Does a complete transfer test where data is also validated
 *
 * Note: mmc_test_prepare() must have been done before this call
 */
static int mmc_test_transfer(struct mmc_test_card *test,
	struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
	unsigned blocks, unsigned blksz, int write)
{}

/*******************************************************************/
/*  Tests                                                          */
/*******************************************************************/

struct mmc_test_case {};

static int mmc_test_basic_write(struct mmc_test_card *test)
{}

static int mmc_test_basic_read(struct mmc_test_card *test)
{}

static int mmc_test_verify_write(struct mmc_test_card *test)
{}

static int mmc_test_verify_read(struct mmc_test_card *test)
{}

static int mmc_test_multi_write(struct mmc_test_card *test)
{}

static int mmc_test_multi_read(struct mmc_test_card *test)
{}

static int mmc_test_pow2_write(struct mmc_test_card *test)
{}

static int mmc_test_pow2_read(struct mmc_test_card *test)
{}

static int mmc_test_weird_write(struct mmc_test_card *test)
{}

static int mmc_test_weird_read(struct mmc_test_card *test)
{}

static int mmc_test_align_write(struct mmc_test_card *test)
{}

static int mmc_test_align_read(struct mmc_test_card *test)
{}

static int mmc_test_align_multi_write(struct mmc_test_card *test)
{}

static int mmc_test_align_multi_read(struct mmc_test_card *test)
{}

static int mmc_test_xfersize_write(struct mmc_test_card *test)
{}

static int mmc_test_xfersize_read(struct mmc_test_card *test)
{}

static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
{}

static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
{}

#ifdef CONFIG_HIGHMEM

static int mmc_test_write_high(struct mmc_test_card *test)
{
	struct scatterlist sg;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, 512, 0);

	return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
}

static int mmc_test_read_high(struct mmc_test_card *test)
{
	struct scatterlist sg;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, 512, 0);

	return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
}

static int mmc_test_multi_write_high(struct mmc_test_card *test)
{
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, size, 0);

	return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 1);
}

static int mmc_test_multi_read_high(struct mmc_test_card *test)
{
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, size, 0);

	return mmc_test_transfer(test, &sg, 1, 0, size / 512, 512, 0);
}

#else

static int mmc_test_no_highmem(struct mmc_test_card *test)
{}

#endif /* CONFIG_HIGHMEM */

/*
 * Map sz bytes so that it can be transferred.
 */
static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
			     int max_scatter, int min_sg_len, bool nonblock)
{}

/*
 * Transfer bytes mapped by mmc_test_area_map().
 */
static int mmc_test_area_transfer(struct mmc_test_card *test,
				  unsigned int dev_addr, int write)
{}

/*
 * Map and transfer bytes for multiple transfers.
 */
static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz,
				unsigned int dev_addr, int write,
				int max_scatter, int timed, int count,
				bool nonblock, int min_sg_len)
{}

static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
			    unsigned int dev_addr, int write, int max_scatter,
			    int timed)
{}

/*
 * Write the test area entirely.
 */
static int mmc_test_area_fill(struct mmc_test_card *test)
{}

/*
 * Erase the test area entirely.
 */
static int mmc_test_area_erase(struct mmc_test_card *test)
{}

/*
 * Cleanup struct mmc_test_area.
 */
static int mmc_test_area_cleanup(struct mmc_test_card *test)
{}

/*
 * Initialize an area for testing large transfers.  The test area is set to the
 * middle of the card because cards may have different characteristics at the
 * front (for FAT file system optimization).  Optionally, the area is erased
 * (if the card supports it) which may improve write performance.  Optionally,
 * the area is filled with data for subsequent read tests.
 */
static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
{}

/*
 * Prepare for large transfers.  Do not erase the test area.
 */
static int mmc_test_area_prepare(struct mmc_test_card *test)
{}

/*
 * Prepare for large transfers.  Do erase the test area.
 */
static int mmc_test_area_prepare_erase(struct mmc_test_card *test)
{}

/*
 * Prepare for large transfers.  Erase and fill the test area.
 */
static int mmc_test_area_prepare_fill(struct mmc_test_card *test)
{}

/*
 * Test best-case performance.  Best-case performance is expected from
 * a single large transfer.
 *
 * An additional option (max_scatter) allows the measurement of the same
 * transfer but with no contiguous pages in the scatter list.  This tests
 * the efficiency of DMA to handle scattered pages.
 */
static int mmc_test_best_performance(struct mmc_test_card *test, int write,
				     int max_scatter)
{}

/*
 * Best-case read performance.
 */
static int mmc_test_best_read_performance(struct mmc_test_card *test)
{}

/*
 * Best-case write performance.
 */
static int mmc_test_best_write_performance(struct mmc_test_card *test)
{}

/*
 * Best-case read performance into scattered pages.
 */
static int mmc_test_best_read_perf_max_scatter(struct mmc_test_card *test)
{}

/*
 * Best-case write performance from scattered pages.
 */
static int mmc_test_best_write_perf_max_scatter(struct mmc_test_card *test)
{}

/*
 * Single read performance by transfer size.
 */
static int mmc_test_profile_read_perf(struct mmc_test_card *test)
{}

/*
 * Single write performance by transfer size.
 */
static int mmc_test_profile_write_perf(struct mmc_test_card *test)
{}

/*
 * Single trim performance by transfer size.
 */
static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
{}

static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
{}

/*
 * Consecutive read performance by transfer size.
 */
static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
{}

static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
{}

/*
 * Consecutive write performance by transfer size.
 */
static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
{}

/*
 * Consecutive trim performance by transfer size.
 */
static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
{}

static unsigned int rnd_next =;

static unsigned int mmc_test_rnd_num(unsigned int rnd_cnt)
{}

static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print,
			     unsigned long sz, int secs, int force_retuning)
{}

static int mmc_test_random_perf(struct mmc_test_card *test, int write)
{}

static int mmc_test_retuning(struct mmc_test_card *test)
{}

/*
 * Random read performance by transfer size.
 */
static int mmc_test_random_read_perf(struct mmc_test_card *test)
{}

/*
 * Random write performance by transfer size.
 */
static int mmc_test_random_write_perf(struct mmc_test_card *test)
{}

static int mmc_test_seq_perf(struct mmc_test_card *test, int write,
			     unsigned int tot_sz, int max_scatter)
{}

static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write)
{}

/*
 * Large sequential read performance.
 */
static int mmc_test_large_seq_read_perf(struct mmc_test_card *test)
{}

/*
 * Large sequential write performance.
 */
static int mmc_test_large_seq_write_perf(struct mmc_test_card *test)
{}

static int mmc_test_rw_multiple(struct mmc_test_card *test,
				struct mmc_test_multiple_rw *tdata,
				unsigned int reqsize, unsigned int size,
				int min_sg_len)
{}

static int mmc_test_rw_multiple_size(struct mmc_test_card *test,
				     struct mmc_test_multiple_rw *rw)
{}

static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test,
				       struct mmc_test_multiple_rw *rw)
{}

/*
 * Multiple blocking write 4k to 4 MB chunks
 */
static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test)
{
	unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16,
			     1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22};
	struct mmc_test_multiple_rw test_data = {
		.bs = bs,
		.size = TEST_AREA_MAX_SIZE,
		.len = ARRAY_SIZE(bs),
		.do_write = true,
		.do_nonblock_req = false,
		.prepare = MMC_TEST_PREP_ERASE,
	};

	return mmc_test_rw_multiple_size(test, &test_data);
};

/*
 * Multiple non-blocking write 4k to 4 MB chunks
 */
static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test)
{}

/*
 * Multiple blocking read 4k to 4 MB chunks
 */
static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test)
{}

/*
 * Multiple non-blocking read 4k to 4 MB chunks
 */
static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test)
{}

/*
 * Multiple blocking write 1 to 512 sg elements
 */
static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test)
{
	unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6,
				 1 << 7, 1 << 8, 1 << 9};
	struct mmc_test_multiple_rw test_data = {
		.sg_len = sg_len,
		.size = TEST_AREA_MAX_SIZE,
		.len = ARRAY_SIZE(sg_len),
		.do_write = true,
		.do_nonblock_req = false,
		.prepare = MMC_TEST_PREP_ERASE,
	};

	return mmc_test_rw_multiple_sg_len(test, &test_data);
};

/*
 * Multiple non-blocking write 1 to 512 sg elements
 */
static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test)
{}

/*
 * Multiple blocking read 1 to 512 sg elements
 */
static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test)
{}

/*
 * Multiple non-blocking read 1 to 512 sg elements
 */
static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test)
{}

/*
 * eMMC hardware reset.
 */
static int mmc_test_reset(struct mmc_test_card *test)
{}

static int mmc_test_send_status(struct mmc_test_card *test,
				struct mmc_command *cmd)
{}

static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
				     unsigned int dev_addr, int use_sbc,
				     int repeat_cmd, int write, int use_areq)
{}

static int __mmc_test_cmds_during_tfr(struct mmc_test_card *test,
				      unsigned long sz, int use_sbc, int write,
				      int use_areq)
{}

static int mmc_test_cmds_during_tfr(struct mmc_test_card *test, int use_sbc,
				    int write, int use_areq)
{}

/*
 * Commands during read - no Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_read(struct mmc_test_card *test)
{}

/*
 * Commands during write - no Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_write(struct mmc_test_card *test)
{}

/*
 * Commands during read - use Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_read_cmd23(struct mmc_test_card *test)
{}

/*
 * Commands during write - use Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_write_cmd23(struct mmc_test_card *test)
{}

/*
 * Commands during non-blocking read - use Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_read_cmd23_nonblock(struct mmc_test_card *test)
{}

/*
 * Commands during non-blocking write - use Set Block Count (CMD23).
 */
static int mmc_test_cmds_during_write_cmd23_nonblock(struct mmc_test_card *test)
{}

static const struct mmc_test_case mmc_test_cases[] =;

static DEFINE_MUTEX(mmc_test_lock);

static LIST_HEAD(mmc_test_result);

static void mmc_test_run(struct mmc_test_card *test, int testcase)
{}

static void mmc_test_free_result(struct mmc_card *card)
{}

static LIST_HEAD(mmc_test_file_test);

static int mtf_test_show(struct seq_file *sf, void *data)
{}

static int mtf_test_open(struct inode *inode, struct file *file)
{}

static ssize_t mtf_test_write(struct file *file, const char __user *buf,
	size_t count, loff_t *pos)
{}

static const struct file_operations mmc_test_fops_test =;

static int mtf_testlist_show(struct seq_file *sf, void *data)
{}

DEFINE_SHOW_ATTRIBUTE();

static void mmc_test_free_dbgfs_file(struct mmc_card *card)
{}

static int __mmc_test_register_dbgfs_file(struct mmc_card *card,
	const char *name, umode_t mode, const struct file_operations *fops)
{}

static int mmc_test_register_dbgfs_file(struct mmc_card *card)
{}

static int mmc_test_probe(struct mmc_card *card)
{}

static void mmc_test_remove(struct mmc_card *card)
{}

static struct mmc_driver mmc_driver =;

static int __init mmc_test_init(void)
{}

static void __exit mmc_test_exit(void)
{}

module_init();
module_exit(mmc_test_exit);

MODULE_LICENSE();
MODULE_DESCRIPTION();
MODULE_AUTHOR();