xref: /linux/kernel/dma/map_benchmark.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2020 HiSilicon Limited.
4  */
5 
6 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
7 
8 #include <linux/cleanup.h>
9 #include <linux/debugfs.h>
10 #include <linux/delay.h>
11 #include <linux/device.h>
12 #include <linux/dma-mapping.h>
13 #include <linux/kernel.h>
14 #include <linux/kthread.h>
15 #include <linux/math64.h>
16 #include <linux/module.h>
17 #include <linux/pci.h>
18 #include <linux/platform_device.h>
19 #include <linux/scatterlist.h>
20 #include <linux/slab.h>
21 #include <linux/timekeeping.h>
22 #include <uapi/linux/map_benchmark.h>
23 
24 struct map_benchmark_data {
25 	struct map_benchmark bparam;
26 	struct device *dev;
27 	struct dentry  *debugfs;
28 	enum dma_data_direction dir;
29 	atomic64_t sum_map_100ns;
30 	atomic64_t sum_unmap_100ns;
31 	atomic64_t sum_sq_map;
32 	atomic64_t sum_sq_unmap;
33 	atomic64_t loops;
34 };
35 
36 struct map_benchmark_ops {
37 	void *(*prepare)(struct map_benchmark_data *map);
38 	void (*unprepare)(void *mparam);
39 	void (*initialize_data)(void *mparam);
40 	int (*do_map)(void *mparam);
41 	void (*do_unmap)(void *mparam);
42 };
43 
44 struct dma_single_map_param {
45 	struct device *dev;
46 	dma_addr_t addr;
47 	void *xbuf;
48 	u32 npages;
49 	u32 dma_dir;
50 };
51 
52 static void *dma_single_map_benchmark_prepare(struct map_benchmark_data *map)
53 {
54 	struct dma_single_map_param *params __free(kfree) = kzalloc(sizeof(*params),
55 								    GFP_KERNEL);
56 	if (!params)
57 		return NULL;
58 
59 	params->npages = map->bparam.granule;
60 	params->dma_dir = map->bparam.dma_dir;
61 	params->dev = map->dev;
62 	params->xbuf = alloc_pages_exact(params->npages * PAGE_SIZE, GFP_KERNEL);
63 	if (!params->xbuf)
64 		return NULL;
65 
66 	return_ptr(params);
67 }
68 
69 static void dma_single_map_benchmark_unprepare(void *mparam)
70 {
71 	struct dma_single_map_param *params = mparam;
72 
73 	free_pages_exact(params->xbuf, params->npages * PAGE_SIZE);
74 	kfree(params);
75 }
76 
77 static void dma_single_map_benchmark_initialize_data(void *mparam)
78 {
79 	struct dma_single_map_param *params = mparam;
80 
81 	/*
82 	 * for a non-coherent device, if we don't stain them in the
83 	 * cache, this will give an underestimate of the real-world
84 	 * overhead of BIDIRECTIONAL or TO_DEVICE mappings;
85 	 * 66 means everything goes well! 66 is lucky.
86 	 */
87 	if (params->dma_dir != DMA_FROM_DEVICE)
88 		memset(params->xbuf, 0x66, params->npages * PAGE_SIZE);
89 }
90 
91 static int dma_single_map_benchmark_do_map(void *mparam)
92 {
93 	struct dma_single_map_param *params = mparam;
94 
95 	params->addr = dma_map_single(params->dev, params->xbuf,
96 				      params->npages * PAGE_SIZE, params->dma_dir);
97 	if (unlikely(dma_mapping_error(params->dev, params->addr))) {
98 		pr_err("dma_map_single failed on %s\n", dev_name(params->dev));
99 		return -ENOMEM;
100 	}
101 
102 	return 0;
103 }
104 
105 static void dma_single_map_benchmark_do_unmap(void *mparam)
106 {
107 	struct dma_single_map_param *params = mparam;
108 
109 	dma_unmap_single(params->dev, params->addr,
110 			 params->npages * PAGE_SIZE, params->dma_dir);
111 }
112 
113 static struct map_benchmark_ops dma_single_map_benchmark_ops = {
114 	.prepare = dma_single_map_benchmark_prepare,
115 	.unprepare = dma_single_map_benchmark_unprepare,
116 	.initialize_data = dma_single_map_benchmark_initialize_data,
117 	.do_map = dma_single_map_benchmark_do_map,
118 	.do_unmap = dma_single_map_benchmark_do_unmap,
119 };
120 
121 struct dma_sg_map_param {
122 	struct sg_table sgt;
123 	struct device *dev;
124 	u32 npages;
125 	u32 dma_dir;
126 	void *buf[] __counted_by(npages);
127 };
128 
129 static void *dma_sg_map_benchmark_prepare(struct map_benchmark_data *map)
130 {
131 	struct dma_sg_map_param *params;
132 	struct scatterlist *sg;
133 	u32 npages;
134 	int i;
135 
136 	/*
137 	 * Set the number of scatterlist entries based on the granule.
138 	 * In SG mode, 'granule' represents the number of scatterlist entries.
139 	 * Each scatterlist entry corresponds to a single page.
140 	 */
141 	npages = map->bparam.granule;
142 
143 	params = kzalloc_flex(*params, buf, npages);
144 	if (!params)
145 		return NULL;
146 
147 	params->npages = npages;
148 	params->dma_dir = map->bparam.dma_dir;
149 	params->dev = map->dev;
150 
151 	if (sg_alloc_table(&params->sgt, params->npages, GFP_KERNEL))
152 		goto free_params;
153 
154 	for_each_sgtable_sg(&params->sgt, sg, i) {
155 		params->buf[i] = (void *)__get_free_page(GFP_KERNEL);
156 		if (!params->buf[i])
157 			goto free_page;
158 
159 		sg_set_buf(sg, params->buf[i], PAGE_SIZE);
160 	}
161 
162 	return params;
163 
164 free_page:
165 	while (i-- > 0)
166 		free_page((unsigned long)params->buf[i]);
167 
168 	sg_free_table(&params->sgt);
169 free_params:
170 	kfree(params);
171 	return NULL;
172 }
173 
174 static void dma_sg_map_benchmark_unprepare(void *mparam)
175 {
176 	struct dma_sg_map_param *params = mparam;
177 	int i;
178 
179 	for (i = 0; i < params->npages; i++)
180 		free_page((unsigned long)params->buf[i]);
181 
182 	sg_free_table(&params->sgt);
183 
184 	kfree(params);
185 }
186 
187 static void dma_sg_map_benchmark_initialize_data(void *mparam)
188 {
189 	struct dma_sg_map_param *params = mparam;
190 	struct scatterlist *sg;
191 	int i = 0;
192 
193 	if (params->dma_dir == DMA_FROM_DEVICE)
194 		return;
195 
196 	for_each_sgtable_sg(&params->sgt, sg, i)
197 		memset(params->buf[i], 0x66, PAGE_SIZE);
198 }
199 
200 static int dma_sg_map_benchmark_do_map(void *mparam)
201 {
202 	struct dma_sg_map_param *params = mparam;
203 	int ret = 0;
204 
205 	int sg_mapped = dma_map_sg(params->dev, params->sgt.sgl,
206 				   params->npages, params->dma_dir);
207 	if (!sg_mapped) {
208 		pr_err("dma_map_sg failed on %s\n", dev_name(params->dev));
209 		ret = -ENOMEM;
210 	}
211 
212 	return ret;
213 }
214 
215 static void dma_sg_map_benchmark_do_unmap(void *mparam)
216 {
217 	struct dma_sg_map_param *params = mparam;
218 
219 	dma_unmap_sg(params->dev, params->sgt.sgl, params->npages,
220 		     params->dma_dir);
221 }
222 
223 static struct map_benchmark_ops dma_sg_map_benchmark_ops = {
224 	.prepare = dma_sg_map_benchmark_prepare,
225 	.unprepare = dma_sg_map_benchmark_unprepare,
226 	.initialize_data = dma_sg_map_benchmark_initialize_data,
227 	.do_map = dma_sg_map_benchmark_do_map,
228 	.do_unmap = dma_sg_map_benchmark_do_unmap,
229 };
230 
231 static struct map_benchmark_ops *dma_map_benchmark_ops[DMA_MAP_BENCH_MODE_MAX] = {
232 	[DMA_MAP_BENCH_SINGLE_MODE] = &dma_single_map_benchmark_ops,
233 	[DMA_MAP_BENCH_SG_MODE] = &dma_sg_map_benchmark_ops,
234 };
235 
236 static int map_benchmark_thread(void *data)
237 {
238 	struct map_benchmark_data *map = data;
239 	__u8 map_mode = map->bparam.map_mode;
240 	int ret = 0;
241 
242 	struct map_benchmark_ops *mb_ops = dma_map_benchmark_ops[map_mode];
243 	void *mparam = mb_ops->prepare(map);
244 
245 	if (!mparam)
246 		return -ENOMEM;
247 
248 	while (!kthread_should_stop())  {
249 		u64 map_100ns, unmap_100ns, map_sq, unmap_sq;
250 		ktime_t map_stime, map_etime, unmap_stime, unmap_etime;
251 		ktime_t map_delta, unmap_delta;
252 
253 		mb_ops->initialize_data(mparam);
254 		map_stime = ktime_get();
255 		ret = mb_ops->do_map(mparam);
256 		if (ret)
257 			goto out;
258 
259 		map_etime = ktime_get();
260 		map_delta = ktime_sub(map_etime, map_stime);
261 
262 		/* Pretend DMA is transmitting */
263 		ndelay(map->bparam.dma_trans_ns);
264 
265 		unmap_stime = ktime_get();
266 		mb_ops->do_unmap(mparam);
267 
268 		unmap_etime = ktime_get();
269 		unmap_delta = ktime_sub(unmap_etime, unmap_stime);
270 
271 		/* calculate sum and sum of squares */
272 
273 		map_100ns = div64_ul(map_delta,  100);
274 		unmap_100ns = div64_ul(unmap_delta, 100);
275 		map_sq = map_100ns * map_100ns;
276 		unmap_sq = unmap_100ns * unmap_100ns;
277 
278 		atomic64_add(map_100ns, &map->sum_map_100ns);
279 		atomic64_add(unmap_100ns, &map->sum_unmap_100ns);
280 		atomic64_add(map_sq, &map->sum_sq_map);
281 		atomic64_add(unmap_sq, &map->sum_sq_unmap);
282 		atomic64_inc(&map->loops);
283 
284 		/*
285 		 * We may test for a long time so periodically check whether
286 		 * we need to schedule to avoid starving the others. Otherwise
287 		 * we may hangup the kernel in a non-preemptible kernel when
288 		 * the test kthreads number >= CPU number, the test kthreads
289 		 * will run endless on every CPU since the thread resposible
290 		 * for notifying the kthread stop (in do_map_benchmark())
291 		 * could not be scheduled.
292 		 *
293 		 * Note this may degrade the test concurrency since the test
294 		 * threads may need to share the CPU time with other load
295 		 * in the system. So it's recommended to run this benchmark
296 		 * on an idle system.
297 		 */
298 		cond_resched();
299 	}
300 
301 out:
302 	mb_ops->unprepare(mparam);
303 	return ret;
304 }
305 
306 static int do_map_benchmark(struct map_benchmark_data *map)
307 {
308 	struct task_struct **tsk;
309 	int threads = map->bparam.threads;
310 	int node = map->bparam.node;
311 	u64 loops;
312 	int ret = 0;
313 	int i;
314 
315 	tsk = kmalloc_objs(*tsk, threads);
316 	if (!tsk)
317 		return -ENOMEM;
318 
319 	get_device(map->dev);
320 
321 	for (i = 0; i < threads; i++) {
322 		tsk[i] = kthread_create_on_node(map_benchmark_thread, map,
323 				map->bparam.node, "dma-map-benchmark/%d", i);
324 		if (IS_ERR(tsk[i])) {
325 			pr_err("create dma_map thread failed\n");
326 			ret = PTR_ERR(tsk[i]);
327 			while (--i >= 0)
328 				kthread_stop(tsk[i]);
329 			goto out;
330 		}
331 
332 		if (node != NUMA_NO_NODE)
333 			kthread_bind_mask(tsk[i], cpumask_of_node(node));
334 	}
335 
336 	/* clear the old value in the previous benchmark */
337 	atomic64_set(&map->sum_map_100ns, 0);
338 	atomic64_set(&map->sum_unmap_100ns, 0);
339 	atomic64_set(&map->sum_sq_map, 0);
340 	atomic64_set(&map->sum_sq_unmap, 0);
341 	atomic64_set(&map->loops, 0);
342 
343 	for (i = 0; i < threads; i++) {
344 		get_task_struct(tsk[i]);
345 		wake_up_process(tsk[i]);
346 	}
347 
348 	msleep_interruptible(map->bparam.seconds * 1000);
349 
350 	/* wait for the completion of all started benchmark threads */
351 	for (i = 0; i < threads; i++) {
352 		int kthread_ret = kthread_stop_put(tsk[i]);
353 
354 		if (kthread_ret)
355 			ret = kthread_ret;
356 	}
357 
358 	if (ret)
359 		goto out;
360 
361 	loops = atomic64_read(&map->loops);
362 	if (likely(loops > 0)) {
363 		u64 map_variance, unmap_variance;
364 		u64 sum_map = atomic64_read(&map->sum_map_100ns);
365 		u64 sum_unmap = atomic64_read(&map->sum_unmap_100ns);
366 		u64 sum_sq_map = atomic64_read(&map->sum_sq_map);
367 		u64 sum_sq_unmap = atomic64_read(&map->sum_sq_unmap);
368 
369 		/* average latency */
370 		map->bparam.avg_map_100ns = div64_u64(sum_map, loops);
371 		map->bparam.avg_unmap_100ns = div64_u64(sum_unmap, loops);
372 
373 		/* standard deviation of latency */
374 		map_variance = div64_u64(sum_sq_map, loops) -
375 				map->bparam.avg_map_100ns *
376 				map->bparam.avg_map_100ns;
377 		unmap_variance = div64_u64(sum_sq_unmap, loops) -
378 				map->bparam.avg_unmap_100ns *
379 				map->bparam.avg_unmap_100ns;
380 		map->bparam.map_stddev = int_sqrt64(map_variance);
381 		map->bparam.unmap_stddev = int_sqrt64(unmap_variance);
382 	}
383 
384 out:
385 	put_device(map->dev);
386 	kfree(tsk);
387 	return ret;
388 }
389 
390 static long map_benchmark_ioctl(struct file *file, unsigned int cmd,
391 		unsigned long arg)
392 {
393 	struct map_benchmark_data *map = file->private_data;
394 	void __user *argp = (void __user *)arg;
395 	u64 old_dma_mask;
396 	int ret;
397 
398 	if (copy_from_user(&map->bparam, argp, sizeof(map->bparam)))
399 		return -EFAULT;
400 
401 	switch (cmd) {
402 	case DMA_MAP_BENCHMARK:
403 		if (map->bparam.map_mode < 0 ||
404 		    map->bparam.map_mode >= DMA_MAP_BENCH_MODE_MAX) {
405 			pr_err("invalid map mode\n");
406 			return -EINVAL;
407 		}
408 
409 		if (map->bparam.threads == 0 ||
410 		    map->bparam.threads > DMA_MAP_MAX_THREADS) {
411 			pr_err("invalid thread number\n");
412 			return -EINVAL;
413 		}
414 
415 		if (map->bparam.seconds == 0 ||
416 		    map->bparam.seconds > DMA_MAP_MAX_SECONDS) {
417 			pr_err("invalid duration seconds\n");
418 			return -EINVAL;
419 		}
420 
421 		if (map->bparam.dma_trans_ns > DMA_MAP_MAX_TRANS_DELAY) {
422 			pr_err("invalid transmission delay\n");
423 			return -EINVAL;
424 		}
425 
426 		if (map->bparam.node != NUMA_NO_NODE &&
427 		    (map->bparam.node < 0 || map->bparam.node >= MAX_NUMNODES ||
428 		     !node_possible(map->bparam.node))) {
429 			pr_err("invalid numa node\n");
430 			return -EINVAL;
431 		}
432 
433 		if (map->bparam.granule < 1 || map->bparam.granule > 1024) {
434 			pr_err("invalid granule size\n");
435 			return -EINVAL;
436 		}
437 
438 		switch (map->bparam.dma_dir) {
439 		case DMA_MAP_BIDIRECTIONAL:
440 			map->dir = DMA_BIDIRECTIONAL;
441 			break;
442 		case DMA_MAP_FROM_DEVICE:
443 			map->dir = DMA_FROM_DEVICE;
444 			break;
445 		case DMA_MAP_TO_DEVICE:
446 			map->dir = DMA_TO_DEVICE;
447 			break;
448 		default:
449 			pr_err("invalid DMA direction\n");
450 			return -EINVAL;
451 		}
452 
453 		old_dma_mask = dma_get_mask(map->dev);
454 
455 		ret = dma_set_mask(map->dev,
456 				   DMA_BIT_MASK(map->bparam.dma_bits));
457 		if (ret) {
458 			pr_err("failed to set dma_mask on device %s\n",
459 				dev_name(map->dev));
460 			return -EINVAL;
461 		}
462 
463 		ret = do_map_benchmark(map);
464 
465 		/*
466 		 * restore the original dma_mask as many devices' dma_mask are
467 		 * set by architectures, acpi, busses. When we bind them back
468 		 * to their original drivers, those drivers shouldn't see
469 		 * dma_mask changed by benchmark
470 		 */
471 		dma_set_mask(map->dev, old_dma_mask);
472 
473 		if (ret)
474 			return ret;
475 		break;
476 	default:
477 		return -EINVAL;
478 	}
479 
480 	if (copy_to_user(argp, &map->bparam, sizeof(map->bparam)))
481 		return -EFAULT;
482 
483 	return ret;
484 }
485 
486 static const struct file_operations map_benchmark_fops = {
487 	.open			= simple_open,
488 	.unlocked_ioctl		= map_benchmark_ioctl,
489 };
490 
491 static void map_benchmark_remove_debugfs(void *data)
492 {
493 	struct map_benchmark_data *map = (struct map_benchmark_data *)data;
494 
495 	debugfs_remove(map->debugfs);
496 }
497 
498 static int __map_benchmark_probe(struct device *dev)
499 {
500 	struct dentry *entry;
501 	struct map_benchmark_data *map;
502 	int ret;
503 
504 	map = devm_kzalloc(dev, sizeof(*map), GFP_KERNEL);
505 	if (!map)
506 		return -ENOMEM;
507 	map->dev = dev;
508 
509 	ret = devm_add_action(dev, map_benchmark_remove_debugfs, map);
510 	if (ret) {
511 		pr_err("Can't add debugfs remove action\n");
512 		return ret;
513 	}
514 
515 	/*
516 	 * we only permit a device bound with this driver, 2nd probe
517 	 * will fail
518 	 */
519 	entry = debugfs_create_file("dma_map_benchmark", 0600, NULL, map,
520 			&map_benchmark_fops);
521 	if (IS_ERR(entry))
522 		return PTR_ERR(entry);
523 	map->debugfs = entry;
524 
525 	return 0;
526 }
527 
528 static int map_benchmark_platform_probe(struct platform_device *pdev)
529 {
530 	return __map_benchmark_probe(&pdev->dev);
531 }
532 
533 static struct platform_driver map_benchmark_platform_driver = {
534 	.driver		= {
535 		.name	= "dma_map_benchmark",
536 	},
537 	.probe = map_benchmark_platform_probe,
538 };
539 
540 static int
541 map_benchmark_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
542 {
543 	return __map_benchmark_probe(&pdev->dev);
544 }
545 
546 static struct pci_driver map_benchmark_pci_driver = {
547 	.name	= "dma_map_benchmark",
548 	.probe	= map_benchmark_pci_probe,
549 };
550 
551 static int __init map_benchmark_init(void)
552 {
553 	int ret;
554 
555 	ret = pci_register_driver(&map_benchmark_pci_driver);
556 	if (ret)
557 		return ret;
558 
559 	ret = platform_driver_register(&map_benchmark_platform_driver);
560 	if (ret) {
561 		pci_unregister_driver(&map_benchmark_pci_driver);
562 		return ret;
563 	}
564 
565 	return 0;
566 }
567 
568 static void __exit map_benchmark_cleanup(void)
569 {
570 	platform_driver_unregister(&map_benchmark_platform_driver);
571 	pci_unregister_driver(&map_benchmark_pci_driver);
572 }
573 
574 module_init(map_benchmark_init);
575 module_exit(map_benchmark_cleanup);
576 
577 MODULE_AUTHOR("Barry Song <song.bao.hua@hisilicon.com>");
578 MODULE_DESCRIPTION("dma_map benchmark driver");
579