summaryrefslogtreecommitdiff
path: root/tools/dma
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dma')
-rw-r--r--tools/dma/.gitignore3
-rw-r--r--tools/dma/Makefile55
-rw-r--r--tools/dma/config1
-rw-r--r--tools/dma/dma_map_benchmark.c127
4 files changed, 186 insertions, 0 deletions
diff --git a/tools/dma/.gitignore b/tools/dma/.gitignore
new file mode 100644
index 000000000000..94b68cf4147b
--- /dev/null
+++ b/tools/dma/.gitignore
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+dma_map_benchmark
+include/linux/map_benchmark.h
diff --git a/tools/dma/Makefile b/tools/dma/Makefile
new file mode 100644
index 000000000000..e4abf37bf020
--- /dev/null
+++ b/tools/dma/Makefile
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+# This will work when dma is built in tools env. where srctree
+# isn't set and when invoked from selftests build, where srctree
+# is set to ".". building_out_of_srctree is undefined for in srctree
+# builds
+ifndef building_out_of_srctree
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := dma_map_benchmark
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/map_benchmark.h: ../../include/uapi/linux/map_benchmark.h
+ mkdir -p $(OUTPUT)include/linux 2>&1 || true
+ ln -sf $(CURDIR)/../../include/uapi/linux/map_benchmark.h $@
+
+prepare: $(OUTPUT)include/linux/map_benchmark.h
+
+FORCE:
+
+DMA_MAP_BENCHMARK = dma_map_benchmark
+$(DMA_MAP_BENCHMARK): prepare FORCE
+ $(CC) $(CFLAGS) $(DMA_MAP_BENCHMARK).c -o $(DMA_MAP_BENCHMARK)
+
+clean:
+ rm -f $(ALL_PROGRAMS)
+ rm -rf $(OUTPUT)include
+ find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.cmd' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+.PHONY: all install clean prepare FORCE
diff --git a/tools/dma/config b/tools/dma/config
new file mode 100644
index 000000000000..6102ee3c43cd
--- /dev/null
+++ b/tools/dma/config
@@ -0,0 +1 @@
+CONFIG_DMA_MAP_BENCHMARK=y
diff --git a/tools/dma/dma_map_benchmark.c b/tools/dma/dma_map_benchmark.c
new file mode 100644
index 000000000000..dd0ed528e6df
--- /dev/null
+++ b/tools/dma/dma_map_benchmark.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 HiSilicon Limited.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/map_benchmark.h>
+
+#define NSEC_PER_MSEC 1000000L
+
+static char *directions[] = {
+ "BIDIRECTIONAL",
+ "TO_DEVICE",
+ "FROM_DEVICE",
+};
+
+int main(int argc, char **argv)
+{
+ struct map_benchmark map;
+ int fd, opt;
+ /* default single thread, run 20 seconds on NUMA_NO_NODE */
+ int threads = 1, seconds = 20, node = -1;
+ /* default dma mask 32bit, bidirectional DMA */
+ int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL;
+ /* default granule 1 PAGESIZE */
+ int granule = 1;
+
+ int cmd = DMA_MAP_BENCHMARK;
+
+ while ((opt = getopt(argc, argv, "t:s:n:b:d:x:g:")) != -1) {
+ switch (opt) {
+ case 't':
+ threads = atoi(optarg);
+ break;
+ case 's':
+ seconds = atoi(optarg);
+ break;
+ case 'n':
+ node = atoi(optarg);
+ break;
+ case 'b':
+ bits = atoi(optarg);
+ break;
+ case 'd':
+ dir = atoi(optarg);
+ break;
+ case 'x':
+ xdelay = atoi(optarg);
+ break;
+ case 'g':
+ granule = atoi(optarg);
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) {
+ fprintf(stderr, "invalid number of threads, must be in 1-%d\n",
+ DMA_MAP_MAX_THREADS);
+ exit(1);
+ }
+
+ if (seconds <= 0 || seconds > DMA_MAP_MAX_SECONDS) {
+ fprintf(stderr, "invalid number of seconds, must be in 1-%d\n",
+ DMA_MAP_MAX_SECONDS);
+ exit(1);
+ }
+
+ if (xdelay < 0 || xdelay > DMA_MAP_MAX_TRANS_DELAY) {
+ fprintf(stderr, "invalid transmit delay, must be in 0-%ld\n",
+ DMA_MAP_MAX_TRANS_DELAY);
+ exit(1);
+ }
+
+ /* suppose the mininum DMA zone is 1MB in the world */
+ if (bits < 20 || bits > 64) {
+ fprintf(stderr, "invalid dma mask bit, must be in 20-64\n");
+ exit(1);
+ }
+
+ if (dir != DMA_MAP_BIDIRECTIONAL && dir != DMA_MAP_TO_DEVICE &&
+ dir != DMA_MAP_FROM_DEVICE) {
+ fprintf(stderr, "invalid dma direction\n");
+ exit(1);
+ }
+
+ if (granule < 1 || granule > 1024) {
+ fprintf(stderr, "invalid granule size\n");
+ exit(1);
+ }
+
+ fd = open("/sys/kernel/debug/dma_map_benchmark", O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ memset(&map, 0, sizeof(map));
+ map.seconds = seconds;
+ map.threads = threads;
+ map.node = node;
+ map.dma_bits = bits;
+ map.dma_dir = dir;
+ map.dma_trans_ns = xdelay;
+ map.granule = granule;
+
+ if (ioctl(fd, cmd, &map)) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s granule: %d\n",
+ threads, seconds, node, directions[dir], granule);
+ printf("average map latency(us):%.1f standard deviation:%.1f\n",
+ map.avg_map_100ns/10.0, map.map_stddev/10.0);
+ printf("average unmap latency(us):%.1f standard deviation:%.1f\n",
+ map.avg_unmap_100ns/10.0, map.unmap_stddev/10.0);
+
+ return 0;
+}