staticbool nobounce;
module_param(nobounce, bool, 0644);
MODULE_PARM_DESC(nobounce, "Prevent using swiotlb buffer (default: use swiotlb buffer)");
staticunsignedint test_buf_size = 16384;
module_param(test_buf_size, uint, 0644);
MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
staticchar test_device[32];
module_param_string(device, test_device, sizeof(test_device), 0644);
MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)");
staticunsignedint threads_per_chan = 1;
module_param(threads_per_chan, uint, 0644);
MODULE_PARM_DESC(threads_per_chan, "Number of threads to start per channel (default: 1)");
staticunsignedint max_channels;
module_param(max_channels, uint, 0644);
MODULE_PARM_DESC(max_channels, "Maximum number of channels to use (default: all)");
staticunsignedint iterations;
module_param(iterations, uint, 0644);
MODULE_PARM_DESC(iterations, "Iterations before stopping test (default: infinite)");
staticint alignment = -1;
module_param(alignment, int, 0644);
MODULE_PARM_DESC(alignment, "Custom data address alignment taken as 2^(alignment) (default: not used (-1))");
staticunsignedint transfer_size;
module_param(transfer_size, uint, 0644);
MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))");
staticbool polled;
module_param(polled, bool, 0644);
MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts");
/** * struct dmatest_params - test parameters. * @nobounce: prevent using swiotlb buffer * @buf_size: size of the memcpy test buffer * @channel: bus ID of the channel to test * @device: bus ID of the DMA Engine to test * @threads_per_chan: number of threads to start per channel * @max_channels: maximum number of channels to use * @iterations: iterations before stopping test * @xor_sources: number of xor source buffers * @pq_sources: number of p+q source buffers * @timeout: transfer timeout in msec, -1 for infinite timeout * @noverify: disable data verification * @norandom: disable random offset setup * @alignment: custom data address alignment taken as 2^alignment * @transfer_size: custom transfer size in bytes * @polled: use polling for completion instead of interrupts
*/ struct dmatest_params { bool nobounce; unsignedint buf_size; char channel[20]; char device[32]; unsignedint threads_per_chan; unsignedint max_channels; unsignedint iterations; unsignedint xor_sources; unsignedint pq_sources; int timeout; bool noverify; bool norandom; int alignment; unsignedint transfer_size; bool polled;
};
/** * struct dmatest_info - test information. * @params: test parameters * @channels: channels under test * @nr_channels: number of channels under test * @lock: access protection to the fields of this structure * @did_init: module has been initialized completely * @last_error: test has faced configuration issues
*/ staticstruct dmatest_info { /* Test parameters */ struct dmatest_params params;
/* Maximum amount of mismatched bytes in buffer to print */ #define MAX_ERROR_COUNT 32
/* * Initialization patterns. All bytes in the source buffer has bit 7 * set, all bytes in the destination buffer has bit 7 cleared. * * Bit 6 is set for all bytes which are to be copied by the DMA * engine. Bit 5 is set for all bytes which are to be overwritten by * the DMA engine. * * The remaining bits are the inverse of a counter which increments by * one for each byte address.
*/ #define PATTERN_SRC 0x80 #define PATTERN_DST 0x00 #define PATTERN_COPY 0x40 #define PATTERN_OVERWRITE 0x20 #define PATTERN_COUNT_MASK 0x1f #define PATTERN_MEMSET_IDX 0x01
staticvoid dmatest_callback(void *arg)
{ struct dmatest_done *done = arg; struct dmatest_thread *thread =
container_of(done, struct dmatest_thread, test_done); if (!thread->done) {
done->done = true;
wake_up_all(done->wait);
} else { /* * If thread->done, it means that this callback occurred * after the parent thread has cleaned up. This can * happen in the case that driver doesn't implement * the terminate_all() functionality and a dma operation * did not occur within the timeout period
*/
WARN(1, "dmatest: Kernel memory may be corrupted!!\n");
}
}
/* * This function repeatedly tests DMA transfers of various lengths and * offsets for a given operation type until it is told to exit by * kthread_stop(). There may be multiple threads running this function * in parallel for a single channel, and there may be multiple channels * being tested in parallel. * * Before each test, the source and destination buffer is initialized * with a known pattern. This pattern is different depending on * whether it's in an area which is supposed to be copied or * overwritten, and different in the source and destination buffers. * So if the DMA engine doesn't copy exactly what we tell it to copy, * we'll notice.
*/ staticint dmatest_func(void *data)
{ struct dmatest_thread *thread = data; struct dmatest_done *done = &thread->test_done; struct dmatest_info *info; struct dmatest_params *params; struct dma_chan *chan; struct dma_device *dev; struct device *dma_dev; unsignedint error_count; unsignedint failed_tests = 0; unsignedint total_tests = 0;
dma_cookie_t cookie; enum dma_status status; enum dma_ctrl_flags flags;
u8 *pq_coefs = NULL; int ret; unsignedint buf_size; struct dmatest_data *src; struct dmatest_data *dst; int i;
ktime_t ktime, start, diff;
ktime_t filltime = 0;
ktime_t comparetime = 0;
s64 runtime = 0; unsignedlonglong total_len = 0; unsignedlonglong iops = 0;
u8 align = 0; bool is_memset = false;
dma_addr_t *srcs;
dma_addr_t *dma_pq;
set_freezable();
ret = -ENOMEM;
smp_rmb();
thread->pending = false;
info = thread->info;
params = &info->params;
chan = thread->chan;
dev = chan->device;
dma_dev = dmaengine_get_dma_device(chan);
if (params->transfer_size) { if (params->transfer_size >= buf_size) {
pr_err("%u-byte transfer size must be lower than %u-buffer size\n",
params->transfer_size, buf_size); break;
}
len = params->transfer_size;
} elseif (params->norandom) {
len = buf_size;
} else {
len = dmatest_random() % buf_size + 1;
}
/* Do not alter transfer size explicitly defined by user */ if (!params->transfer_size) {
len = (len >> align) << align; if (!len)
len = 1 << align;
}
total_len += len;
if (params->norandom) {
src->off = 0;
dst->off = 0;
} else {
src->off = dmatest_random() % (buf_size - len + 1);
dst->off = dmatest_random() % (buf_size - len + 1);
if (type == DMA_MEMCPY)
op = "copy"; elseif (type == DMA_MEMSET)
op = "set"; elseif (type == DMA_XOR)
op = "xor"; elseif (type == DMA_PQ)
op = "pq"; else return -EINVAL;
for (i = 0; i < params->threads_per_chan; i++) {
thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); if (!thread) {
pr_warn("No memory for %s-%s%u\n",
dma_chan_name(chan), op, i); break;
}
thread->info = info;
thread->chan = dtc->chan;
thread->type = type;
thread->test_done.wait = &thread->done_wait;
init_waitqueue_head(&thread->done_wait);
smp_wmb();
thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
dma_chan_name(chan), op, i); if (IS_ERR(thread->task)) {
pr_warn("Failed to create thread %s-%s%u\n",
dma_chan_name(chan), op, i);
kfree(thread); break;
}
/* srcbuf and dstbuf are allocated by the thread itself */
get_task_struct(thread->task);
list_add_tail(&thread->node, &dtc->threads);
thread->pending = true;
}
chan = dma_request_channel(mask, filter, params); if (chan) { if (dmatest_add_channel(info, chan)) {
dma_release_channel(chan); break; /* add_channel failed, punt */
}
} else break; /* no more channels available */ if (params->max_channels &&
info->nr_channels >= params->max_channels) break; /* we have all we need */
}
}
staticvoid start_threaded_tests(struct dmatest_info *info)
{ /* we might be called early to set run=, defer running until all * parameters have been evaluated
*/ if (!info->did_init) return;
mutex_lock(&info->lock);
ret = param_set_bool(val, kp); if (ret) {
mutex_unlock(&info->lock); return ret;
} elseif (dmatest_run) { if (!is_threaded_test_pending(info)) { /* * We have nothing to run. This can be due to:
*/
ret = info->last_error; if (ret) { /* 1) Misconfiguration */
pr_err("Channel misconfigured, can't continue\n");
mutex_unlock(&info->lock); return ret;
} else { /* 2) We rely on defaults */
pr_info("No channels configured, continue with any\n"); if (!is_threaded_test_run(info))
stop_threaded_test(info);
add_threaded_test(info);
}
}
start_threaded_tests(info);
} else {
stop_threaded_test(info);
}
mutex_lock(&info->lock);
ret = param_set_copystring(val, kp); if (ret) {
mutex_unlock(&info->lock); return ret;
} /*Clear any previously run threads */ if (!is_threaded_test_run(info) && !is_threaded_test_pending(info))
stop_threaded_test(info); /* Reject channels that are already registered */ if (is_threaded_test_pending(info)) {
list_for_each_entry(dtc, &info->channels, node) { if (strcmp(dma_chan_name(dtc->chan),
strim(test_channel)) == 0) {
dtc = list_last_entry(&info->channels, struct dmatest_chan,
node);
strscpy(chan_reset_val,
dma_chan_name(dtc->chan), sizeof(chan_reset_val));
ret = -EBUSY; goto add_chan_err;
}
}
}
add_threaded_test(info);
/* Check if channel was added successfully */ if (!list_empty(&info->channels)) { /* * if new channel was not successfully added, revert the * "test_channel" string to the name of the last successfully * added channel. exception for when users issues empty string * to channel parameter.
*/
dtc = list_last_entry(&info->channels, struct dmatest_chan, node); if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0)
&& (strcmp("", strim(test_channel)) != 0)) {
ret = -EINVAL;
strscpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); goto add_chan_err;
}
} else { /* Clear test_channel if no channels were added successfully */
strscpy(chan_reset_val, "", sizeof(chan_reset_val));
ret = -EBUSY; goto add_chan_err;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.