/* * for a non-coherent device, if we don't stain them in the * cache, this will give an underestimate of the real-world * overhead of BIDIRECTIONAL or TO_DEVICE mappings; * 66 means evertything goes well! 66 is lucky.
*/ if (map->dir != DMA_FROM_DEVICE)
memset(buf, 0x66, size);
/* * We may test for a long time so periodically check whether * we need to schedule to avoid starving the others. Otherwise * we may hangup the kernel in a non-preemptible kernel when * the test kthreads number >= CPU number, the test kthreads * will run endless on every CPU since the thread resposible * for notifying the kthread stop (in do_map_benchmark()) * could not be scheduled. * * Note this may degrade the test concurrency since the test * threads may need to share the CPU time with other load * in the system. So it's recommended to run this benchmark * on an idle system.
*/
cond_resched();
}
out:
free_pages_exact(buf, size); return ret;
}
staticint do_map_benchmark(struct map_benchmark_data *map)
{ struct task_struct **tsk; int threads = map->bparam.threads; int node = map->bparam.node;
u64 loops; int ret = 0; int i;
tsk = kmalloc_array(threads, sizeof(*tsk), GFP_KERNEL); if (!tsk) return -ENOMEM;
get_device(map->dev);
for (i = 0; i < threads; i++) {
tsk[i] = kthread_create_on_node(map_benchmark_thread, map,
map->bparam.node, "dma-map-benchmark/%d", i); if (IS_ERR(tsk[i])) {
pr_err("create dma_map thread failed\n");
ret = PTR_ERR(tsk[i]); while (--i >= 0)
kthread_stop(tsk[i]); goto out;
}
if (node != NUMA_NO_NODE)
kthread_bind_mask(tsk[i], cpumask_of_node(node));
}
/* clear the old value in the previous benchmark */
atomic64_set(&map->sum_map_100ns, 0);
atomic64_set(&map->sum_unmap_100ns, 0);
atomic64_set(&map->sum_sq_map, 0);
atomic64_set(&map->sum_sq_unmap, 0);
atomic64_set(&map->loops, 0);
for (i = 0; i < threads; i++) {
get_task_struct(tsk[i]);
wake_up_process(tsk[i]);
}
msleep_interruptible(map->bparam.seconds * 1000);
/* wait for the completion of all started benchmark threads */ for (i = 0; i < threads; i++) { int kthread_ret = kthread_stop_put(tsk[i]);
switch (map->bparam.dma_dir) { case DMA_MAP_BIDIRECTIONAL:
map->dir = DMA_BIDIRECTIONAL; break; case DMA_MAP_FROM_DEVICE:
map->dir = DMA_FROM_DEVICE; break; case DMA_MAP_TO_DEVICE:
map->dir = DMA_TO_DEVICE; break; default:
pr_err("invalid DMA direction\n"); return -EINVAL;
}
old_dma_mask = dma_get_mask(map->dev);
ret = dma_set_mask(map->dev,
DMA_BIT_MASK(map->bparam.dma_bits)); if (ret) {
pr_err("failed to set dma_mask on device %s\n",
dev_name(map->dev)); return -EINVAL;
}
ret = do_map_benchmark(map);
/* * restore the original dma_mask as many devices' dma_mask are * set by architectures, acpi, busses. When we bind them back * to their original drivers, those drivers shouldn't see * dma_mask changed by benchmark
*/
dma_set_mask(map->dev, old_dma_mask);
if (ret) return ret; break; default: return -EINVAL;
}
if (copy_to_user(argp, &map->bparam, sizeof(map->bparam))) return -EFAULT;
ret = devm_add_action(dev, map_benchmark_remove_debugfs, map); if (ret) {
pr_err("Can't add debugfs remove action\n"); return ret;
}
/* * we only permit a device bound with this driver, 2nd probe * will fail
*/
entry = debugfs_create_file("dma_map_benchmark", 0600, NULL, map,
&map_benchmark_fops); if (IS_ERR(entry)) return PTR_ERR(entry);
map->debugfs = entry;
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.