1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2020 HiSilicon Limited.
4 */
5
6#include <fcntl.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include <sys/ioctl.h>
12#include <sys/mman.h>
13#include <linux/types.h>
14#include <linux/map_benchmark.h>
15
16#define NSEC_PER_MSEC	1000000L
17
18static char *directions[] = {
19	"BIDIRECTIONAL",
20	"TO_DEVICE",
21	"FROM_DEVICE",
22};
23
24int main(int argc, char **argv)
25{
26	struct map_benchmark map;
27	int fd, opt;
28	/* default single thread, run 20 seconds on NUMA_NO_NODE */
29	int threads = 1, seconds = 20, node = -1;
30	/* default dma mask 32bit, bidirectional DMA */
31	int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL;
32	/* default granule 1 PAGESIZE */
33	int granule = 1;
34
35	int cmd = DMA_MAP_BENCHMARK;
36	char *p;
37
38	while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:")) != -1) {
39		switch (opt) {
40		case 't':
41			threads = atoi(optarg);
42			break;
43		case 's':
44			seconds = atoi(optarg);
45			break;
46		case 'n':
47			node = atoi(optarg);
48			break;
49		case 'b':
50			bits = atoi(optarg);
51			break;
52		case 'd':
53			dir = atoi(optarg);
54			break;
55		case 'x':
56			xdelay = atoi(optarg);
57			break;
58		case 'g':
59			granule = atoi(optarg);
60			break;
61		default:
62			return -1;
63		}
64	}
65
66	if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) {
67		fprintf(stderr, "invalid number of threads, must be in 1-%d\n",
68			DMA_MAP_MAX_THREADS);
69		exit(1);
70	}
71
72	if (seconds <= 0 || seconds > DMA_MAP_MAX_SECONDS) {
73		fprintf(stderr, "invalid number of seconds, must be in 1-%d\n",
74			DMA_MAP_MAX_SECONDS);
75		exit(1);
76	}
77
78	if (xdelay < 0 || xdelay > DMA_MAP_MAX_TRANS_DELAY) {
79		fprintf(stderr, "invalid transmit delay, must be in 0-%ld\n",
80			DMA_MAP_MAX_TRANS_DELAY);
81		exit(1);
82	}
83
84	/* suppose the mininum DMA zone is 1MB in the world */
85	if (bits < 20 || bits > 64) {
86		fprintf(stderr, "invalid dma mask bit, must be in 20-64\n");
87		exit(1);
88	}
89
90	if (dir != DMA_MAP_BIDIRECTIONAL && dir != DMA_MAP_TO_DEVICE &&
91			dir != DMA_MAP_FROM_DEVICE) {
92		fprintf(stderr, "invalid dma direction\n");
93		exit(1);
94	}
95
96	if (granule < 1 || granule > 1024) {
97		fprintf(stderr, "invalid granule size\n");
98		exit(1);
99	}
100
101	fd = open("/sys/kernel/debug/dma_map_benchmark", O_RDWR);
102	if (fd == -1) {
103		perror("open");
104		exit(1);
105	}
106
107	memset(&map, 0, sizeof(map));
108	map.seconds = seconds;
109	map.threads = threads;
110	map.node = node;
111	map.dma_bits = bits;
112	map.dma_dir = dir;
113	map.dma_trans_ns = xdelay;
114	map.granule = granule;
115
116	if (ioctl(fd, cmd, &map)) {
117		perror("ioctl");
118		exit(1);
119	}
120
121	printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s granule: %d\n",
122			threads, seconds, node, dir[directions], granule);
123	printf("average map latency(us):%.1f standard deviation:%.1f\n",
124			map.avg_map_100ns/10.0, map.map_stddev/10.0);
125	printf("average unmap latency(us):%.1f standard deviation:%.1f\n",
126			map.avg_unmap_100ns/10.0, map.unmap_stddev/10.0);
127
128	return 0;
129}
130