All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] testsuite: App of gpio loopback/react benchmark
@ 2020-08-03  6:04 chensong
  2020-08-11  7:15 ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: chensong @ 2020-08-03  6:04 UTC (permalink / raw)
  To: xenomai, jan.kiszka, greg

This a tool to benchmark the latency of GPIO driver,
it's able to run 2 kinds of benchmark test:

1, loopback mode
1) apply 2 gpio pins by calling service in gpio RTDM driver
   like gpio-bcm2835 and gpio-core.c, one is as output,
   the other is as interrupt
2) call write_rt to send a pulse from output
3) call read_rt to get timestamps recorded in driver (inner loop)
4) also record timespace in user space(outer_loop)
   outer_loop is inner_loop plus overhead of event wakeup
5) ftrace enable/disable

2, react mode
1) apply 2 gpio pins by calling service in gpio RTDM driver
   like gpio-bcm2835 and gpio-core.c, one is as ourput,
   the other is as interrupt
2) call read_rt to wait for a pulse from latency box
3) call write_rt to send a signal back to latency box
   as a reaction
4) latency box calculates the diff and makes the histogram

e.g.:
1) react mode:
   gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
2) loopback mode:
   gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50

CC: Jan Kiszka <jan.kiszka@siemens.com>
CC: Greg Gallagher <greg@embeddedgreg.com>

Signed-off-by: chensong <chensong@tj.kylinos.cn>
---
 configure.ac                     |   1 +
 doc/asciidoc/man1/gpiobench.adoc |  70 +++++
 testsuite/Makefile.am            |   3 +-
 testsuite/gpiobench/Makefile.am  |  18 ++
 testsuite/gpiobench/gpiobench.c  | 654 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 745 insertions(+), 1 deletion(-)
 create mode 100644 doc/asciidoc/man1/gpiobench.adoc
 create mode 100644 testsuite/gpiobench/Makefile.am
 create mode 100644 testsuite/gpiobench/gpiobench.c

diff --git a/configure.ac b/configure.ac
index 29fefab..164c449 100644
--- a/configure.ac
+++ b/configure.ac
@@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
 	testsuite/latency/Makefile \
 	testsuite/switchtest/Makefile \
 	testsuite/gpiotest/Makefile \
+	testsuite/gpiobench/Makefile \
 	testsuite/spitest/Makefile \
 	testsuite/smokey/Makefile \
 	testsuite/smokey/arith/Makefile \
diff --git a/doc/asciidoc/man1/gpiobench.adoc b/doc/asciidoc/man1/gpiobench.adoc
new file mode 100644
index 0000000..bd32ea8
--- /dev/null
+++ b/doc/asciidoc/man1/gpiobench.adoc
@@ -0,0 +1,70 @@
+// ** The above line should force tbl to be a preprocessor **
+// Man page for gpiobench
+//
+// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
+//
+// You may distribute under the terms of the GNU General Public
+// License as specified in the file COPYING that comes with the
+// Xenomai distribution.
+//
+//
+GPIOBENCH(1)
+==========
+:doctype: manpage
+:revdate: 2020/08/03
+:man source: Xenomai
+:man version: {xenover}
+:man manual: Xenomai Manual
+
+NAME
+-----
+gpiobench - Xenomai gpio latency benchmark
+
+SYNOPSIS
+---------
+// The general command line
+*gpiobench* [ options ]
+
+DESCRIPTION
+------------
+*gpiobench* is part of the Xenomai test suite. It is a gpio latency
+benchmark program.  The system must run a suitable Xenomai enabled kernel with
+the respective module (xeno_timerbench).
+
+OPTIONS
+--------
+*gpiobench* accepts the following options:
+
+*-h <histogram-size>*::
+default = 100, increase if your last bucket is full
+
+*-l <num-of-loops>*::
+default=1000, number of loops to run the test
+
+*-q <quiet>*::
+print only a summary on exit
+
+*-m <test-mode>*::
+0 = loopback (default), 1 = react
+
+*-c <pin-controller>*::
+name of pin controller
+
+*-o <output-pin>*::
+number of gpio pin as output
+
+*-i <interrupt-pin>*::
+number of gpin pin as interrupt
+
+*-p <priority>*::
+default = 99, task priority
+
+*-b <bracktrace>*::
+default = 1000, send break trace command when latency > breaktrace
+
+
+
+AUTHOR
+-------
+*gpiobench* was written by song chen. This man page
+was written by song chen.
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index 76d108e..4932f6d 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -1,5 +1,5 @@
 
-SUBDIRS = latency smokey
+SUBDIRS = latency smokey gpiobench
 
 if XENO_COBALT
 SUBDIRS += 		\
@@ -13,6 +13,7 @@ endif
 DIST_SUBDIRS =		\
 	clocktest	\
 	gpiotest	\
+	gpiobench   \
 	latency		\
 	smokey		\
 	spitest		\
diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
new file mode 100644
index 0000000..cca3395
--- /dev/null
+++ b/testsuite/gpiobench/Makefile.am
@@ -0,0 +1,18 @@
+testdir = @XENO_TEST_DIR@
+
+CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
+
+test_PROGRAMS = gpiobench
+
+gpiobench_SOURCES = gpiobench.c
+
+gpiobench_CPPFLAGS = 		\
+	$(XENO_USER_CFLAGS)	\
+	-I$(top_srcdir)/include
+
+gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
+
+gpiobench_LDADD =			\
+	@XENO_CORE_LDADD@	\
+	@XENO_USER_LDADD@	\
+	-lpthread -lrt -lm
diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
new file mode 100644
index 0000000..7f19837
--- /dev/null
+++ b/testsuite/gpiobench/gpiobench.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <sched.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sys/timerfd.h>
+#include <xeno_config.h>
+#include <rtdm/testing.h>
+#include <rtdm/gpio.h>
+#include <boilerplate/trace.h>
+#include <xenomai/init.h>
+#include <sys/mman.h>
+#include <getopt.h>
+
+#define NS_PER_MS (1000000)
+#define NS_PER_S (1000000000)
+
+#define DEFAULT_PRIO 99
+#define VERSION_STRING "0.1"
+#define GPIO_HIGH 1
+#define GPIO_LOW  0
+#define MAX_HIST		100
+#define MAX_CYCLES 1000000
+#define DEFAULT_LIMIT 1000
+#define DEV_PATH    "/dev/rtdm/"
+#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
+#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
+#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
+#define ON  "1"
+#define OFF "0"
+
+enum {
+	MODE_LOOPBACK,
+	MODE_REACT,
+	MODE_ALL
+};
+
+/* Struct for statistics */
+struct test_stat {
+	long inner_min;
+	long inner_max;
+	double inner_avg;
+	long *inner_hist_array;
+	long inner_hist_overflow;
+	long outer_min;
+	long outer_max;
+	double outer_avg;
+	long *outer_hist_array;
+	long outer_hist_overflow;
+};
+
+/* Struct for information */
+struct test_info {
+	unsigned long max_cycles;
+	unsigned long total_cycles;
+	unsigned long max_histogram;
+	int mode;
+	int prio;
+	int quiet;
+	int tracelimit;
+	int fd_dev_intr;
+	int fd_dev_out;
+	char pin_controller[32];
+	pthread_t gpio_task;
+	int gpio_intr;
+	int gpio_out;
+	struct test_stat ts;
+};
+
+struct test_info ti;
+/* Print usage information */
+static void display_help(void)
+{
+	printf("gpiobench V %s\n", VERSION_STRING);
+	printf("Usage:\n"
+	       "gpiobench <options>\n\n"
+
+	       "-b       --breaktrace=USEC  send break trace command when latency > USEC\n"
+	       "                            default=1000\n"
+	       "-h       --histogram=US     dump a latency histogram to stdout after the run\n"
+	       "                            US is the max time to be tracked in microseconds,\n"
+	       "                            default=100\n"
+	       "-l       --loops            number of loops, default=1000\n"
+	       "-p       --prio             priority of highest prio thread, defaults=99\n"
+	       "-q       --quiet            print only a summary on exit\n"
+	       "-o       --output           gpio port number for output, no default value,\n"
+	       "                            must be specified\n"
+	       "-i       --intr             gpio port number as an interrupt, no default value,\n"
+	       "                            must be specified\n"
+	       "-c       --pinctrl          gpio pin controller's name, no default value,\n"
+	       "                            must be specified\n"
+	       "-m       --testmode         0 is loopback mode\n"
+	       "                            1 is react mode which works with a latency box,\n"
+	       "                            default=0\n\n"
+
+	       "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
+		);
+}
+
+static void process_options(int argc, char *argv[])
+{
+	int c = 0;
+	static const char optstring[] = "h:p:m:l:c:b:i:o:q";
+
+	struct option long_options[] = {
+		{ "bracetrace", required_argument, 0, 'b'},
+		{ "histogram", required_argument, 0, 'h'},
+		{ "loops", required_argument, 0, 'l'},
+		{ "prio", required_argument, 0, 'p'},
+		{ "quiet", no_argument, 0, 'q'},
+		{ "output", required_argument, 0, 'o'},
+		{ "intr", required_argument, 0, 'i'},
+		{ "pinctrl", required_argument, 0, 'c'},
+		{ "testmode", required_argument, 0, 'm'},
+		{ 0, 0, 0, 0},
+	};
+
+	while ((c = getopt_long(argc, argv, optstring, long_options,
+						NULL)) != -1) {
+		switch (c) {
+		case 'h':
+			ti.max_histogram = atoi(optarg);
+			break;
+
+		case 'p':
+			ti.prio = atoi(optarg);
+			break;
+
+		case 'l':
+			ti.max_cycles = atoi(optarg);
+			break;
+
+		case 'q':
+			ti.quiet = 1;
+			break;
+
+		case 'b':
+			ti.tracelimit = atoi(optarg);
+			break;
+
+		case 'i':
+			ti.gpio_intr = atoi(optarg);
+			break;
+
+		case 'o':
+			ti.gpio_out = atoi(optarg);
+			break;
+
+		case 'c':
+			strcpy(ti.pin_controller, optarg);
+			break;
+
+		case 'm':
+			ti.mode = atoi(optarg) >=
+				MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
+			break;
+
+		default:
+			display_help();
+			exit(2);
+		}
+	}
+
+	if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
+				|| (strlen(ti.pin_controller) == 0)) {
+		display_help();
+		exit(2);
+	}
+
+	ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
+	ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
+
+	ti.max_histogram = ti.max_histogram > MAX_HIST ?
+		MAX_HIST : ti.max_histogram;
+	ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
+	ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
+}
+
+static int thread_msleep(unsigned int ms)
+{
+	struct timespec ts = {
+		.tv_sec = (ms * NS_PER_MS) / NS_PER_S,
+		.tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
+	};
+
+	return -nanosleep(&ts, NULL);
+}
+
+static inline int64_t calc_us(struct timespec t)
+{
+	return (t.tv_sec * NS_PER_S + t.tv_nsec);
+}
+
+static int setevent(char *event, char *val)
+{
+	int fd;
+	int ret;
+
+	fd = open(event, O_WRONLY);
+	if (fd < 0) {
+		printf("unable to open %s\n", event);
+		return -1;
+	}
+
+	ret = write(fd, val, strlen(val));
+	if (ret < 0) {
+		printf("unable to write %s to %s\n", val, event);
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+static void tracing(char *enable)
+{
+	setevent(TRACING_EVENTS, enable);
+	setevent(TRACING_ON, enable);
+}
+
+#define write_check(__fd, __buf, __len)			\
+	do {						\
+		int __ret = write(__fd, __buf, __len);	\
+		(void)__ret;				\
+	} while (0)
+
+#define TRACEBUFSIZ 1024
+static __thread char tracebuf[TRACEBUFSIZ];
+
+static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
+static void tracemark(char *fmt, ...)
+{
+	va_list ap;
+	int len;
+	int tracemark_fd;
+
+	tracemark_fd = open(TRACE_MARKER, O_WRONLY);
+	if (tracemark_fd == -1) {
+		printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
+		return;
+	}
+
+	/* bail out if we're not tracing */
+	/* or if the kernel doesn't support trace_mark */
+	if (tracemark_fd < 0)
+		return;
+
+	va_start(ap, fmt);
+	len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
+	va_end(ap);
+	write_check(tracemark_fd, tracebuf, len);
+
+	close(tracemark_fd);
+}
+
+static int rw_gpio(int value, int index)
+{
+	int ret;
+	struct timespec timestamp;
+	struct rtdm_gpio_readout rdo;
+	uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
+
+	clock_gettime(CLOCK_MONOTONIC, &timestamp);
+	gpio_write = calc_us(timestamp);
+
+	ret = write(ti.fd_dev_out, &value, sizeof(value));
+	if (ret < 0) {
+		printf("write GPIO, failed\n");
+		return ret;
+	}
+
+	ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
+	if (ret < 0) {
+		printf("read GPIO, failed\n");
+		return ret;
+	}
+
+	clock_gettime(CLOCK_MONOTONIC, &timestamp);
+	gpio_read = calc_us(timestamp);
+
+	inner_diff = (rdo.timestamp - gpio_write) / 1000;
+	outer_diff = (gpio_read - gpio_write) / 1000;
+
+	if (inner_diff < ti.ts.inner_min)
+		ti.ts.inner_min = inner_diff;
+	if (inner_diff > ti.ts.inner_max)
+		ti.ts.inner_max = inner_diff;
+	ti.ts.inner_avg += (double) inner_diff;
+	if (inner_diff >= ti.max_histogram)
+		ti.ts.inner_hist_overflow++;
+	else
+		ti.ts.inner_hist_array[inner_diff]++;
+
+	if (outer_diff < ti.ts.outer_min)
+		ti.ts.outer_min = outer_diff;
+	if (inner_diff > ti.ts.outer_max)
+		ti.ts.outer_max = outer_diff;
+	ti.ts.outer_avg += (double) outer_diff;
+	if (outer_diff >= ti.max_histogram)
+		ti.ts.outer_hist_overflow++;
+	else
+		ti.ts.outer_hist_array[outer_diff]++;
+
+	if (ti.quiet == 0)
+		printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
+					index, inner_diff, outer_diff);
+
+	return outer_diff;
+}
+
+static void *run_gpiobench_loop(void *cookie)
+{
+	int i, ret;
+
+	printf("----rt task, gpio loop, test run----\n");
+
+	for (i = 0; i < ti.max_cycles; i++) {
+		ti.total_cycles = i;
+		/* send a high level pulse from gpio output pin and
+		 * receive an interrupt from the other gpio pin,
+		 * measuring the time elapsed between the two events
+		 */
+		ret = rw_gpio(GPIO_HIGH, i);
+		if (ret < 0) {
+			printf("RW GPIO, failed\n");
+			break;
+		} else if (ret > ti.tracelimit) {
+			tracemark("hit latency threshold (%d > %d), index: %d",
+						ret, ti.tracelimit, i);
+			break;
+		}
+
+		/*take a break, nanosleep here will not jeopardize the latency*/
+		thread_msleep(10);
+
+		ret = rw_gpio(GPIO_LOW, i);
+		/* send a low level pulse from gpio output pin and
+		 * receive an interrupt from the other gpio pin,
+		 * measuring the time elapsed between the two events
+		 */
+		if (ret < 0) {
+			printf("RW GPIO, failed\n");
+			break;
+		} else if (ti.tracelimit && ret > ti.tracelimit) {
+			tracemark("hit latency threshold (%d > %d), index: %d",
+						ret, ti.tracelimit, i);
+			break;
+		}
+
+		/*take a break, nanosleep here will not jeopardize the latency*/
+		thread_msleep(10);
+	}
+
+	ti.ts.inner_avg /= (ti.total_cycles * 2);
+	ti.ts.outer_avg /= (ti.total_cycles * 2);
+
+	return NULL;
+}
+
+static void *run_gpiobench_react(void *cookie)
+{
+	int value, ret, i;
+	struct rtdm_gpio_readout rdo;
+
+	printf("----rt task, gpio react, test run----\n");
+
+	for (i = 0; i < ti.max_cycles; i++) {
+		/* received a pulse from latency box from one of
+		 * the gpio pin pair
+		 */
+		ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
+		if (ret < 0) {
+			printf("RW GPIO read, failed\n");
+			break;
+		}
+
+		if (ti.quiet == 0)
+			printf("idx: %d, received signal from latency box\n",
+				    i);
+
+		/* send a signal back from the other gpio pin
+		 * to latency box as the acknowledge,
+		 * latency box will measure the time elapsed
+		 * between the two events
+		 */
+		value = GPIO_HIGH;
+		ret = write(ti.fd_dev_out, &value, sizeof(value));
+		if (ret < 0) {
+			printf("RW GPIO write, failed\n");
+			break;
+		}
+
+		if (ti.quiet == 0)
+			printf("idx: %d, sent reaction to latency box\n", i);
+	}
+
+	return NULL;
+}
+
+static void setup_sched_parameters(pthread_attr_t *attr, int prio)
+{
+	struct sched_param p;
+	int ret;
+
+	ret = pthread_attr_init(attr);
+	if (ret) {
+		printf("pthread_attr_init(), failed\n");
+		return;
+	}
+
+	ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
+	if (ret) {
+		printf("pthread_attr_setinheritsched(), failed\n");
+		return;
+	}
+
+	ret = pthread_attr_setschedpolicy(attr,
+				prio ? SCHED_FIFO : SCHED_OTHER);
+	if (ret) {
+		printf("pthread_attr_setschedpolicy(), failed\n");
+		return;
+	}
+
+	p.sched_priority = prio;
+	ret = pthread_attr_setschedparam(attr, &p);
+	if (ret) {
+		printf("pthread_attr_setschedparam(), failed\n");
+		return;
+	}
+}
+
+static void init_ti(void)
+{
+	memset(&ti, 0, sizeof(struct test_info));
+	ti.prio = DEFAULT_PRIO;
+	ti.max_cycles = MAX_CYCLES;
+	ti.total_cycles = MAX_CYCLES;
+	ti.max_histogram = MAX_HIST;
+	ti.tracelimit = DEFAULT_LIMIT;
+	ti.quiet = 0;
+	ti.gpio_out = -1;
+	ti.gpio_intr = -1;
+	ti.mode = MODE_LOOPBACK;
+
+	ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
+	ti.ts.inner_max = ti.ts.outer_max = 0;
+	ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
+}
+
+static void print_hist(void)
+{
+	int i;
+
+	printf("\n");
+	printf("# Inner Loop Histogram\n");
+	printf("# Inner Loop latency is the latency in kernel space\n"
+		   "# between gpio_set_value and irq handler\n");
+
+	for (i = 0; i < ti.max_histogram; i++) {
+		unsigned long curr_latency = ti.ts.inner_hist_array[i];
+
+		printf("%06d ", i);
+		printf("%06lu\n", curr_latency);
+	}
+
+	printf("# Total:");
+	printf(" %09lu", ti.total_cycles);
+	printf("\n");
+
+	printf("# Min Latencies:");
+	printf(" %05lu", ti.ts.inner_min);
+	printf("\n");
+	printf("# Avg Latencies:");
+	printf(" %05lf", ti.ts.inner_avg);
+	printf("\n");
+	printf("# Max Latencies:");
+	printf(" %05lu", ti.ts.inner_max);
+	printf("\n");
+
+	printf("\n");
+	printf("\n");
+
+	printf("# Outer Loop Histogram\n");
+	printf("# Outer Loop latency is the latency in user space\n"
+		   "# between write and read\n"
+		   "# Technically, outer loop latency is inner loop latercy\n"
+		   "# plus overhead of event wakeup\n");
+
+	for (i = 0; i < ti.max_histogram; i++) {
+		unsigned long curr_latency = ti.ts.outer_hist_array[i];
+
+		printf("%06d ", i);
+		printf("%06lu\n", curr_latency);
+	}
+
+	printf("# Total:");
+	printf(" %09lu", ti.total_cycles);
+	printf("\n");
+
+	printf("# Min Latencies:");
+	printf(" %05lu", ti.ts.outer_min);
+	printf("\n");
+	printf("# Avg Latencies:");
+	printf(" %05lf", ti.ts.outer_avg);
+	printf("\n");
+	printf("# Max Latencies:");
+	printf(" %05lu", ti.ts.outer_max);
+	printf("\n");
+}
+
+static void cleanup(void)
+{
+	int ret;
+
+	if (ti.tracelimit < DEFAULT_LIMIT)
+		tracing(OFF);
+
+	ret = close(ti.fd_dev_out);
+	if (ret < 0)
+		printf("can't close gpio_out device\n");
+
+	ret = close(ti.fd_dev_intr);
+	if (ret < 0)
+		printf("can't close gpio_intr device\n");
+
+	if (ti.mode == MODE_LOOPBACK)
+		print_hist();
+
+}
+
+static void cleanup_and_exit(int sig)
+{
+	printf("Signal %d received\n", sig);
+	cleanup();
+	exit(0);
+}
+
+int main(int argc, char **argv)
+{
+	struct sigaction sa __attribute__((unused));
+	int ret = 0;
+	pthread_attr_t tattr;
+	int trigger, value;
+	char dev_name[64];
+
+	init_ti();
+
+	process_options(argc, argv);
+
+	ret = mlockall(MCL_CURRENT|MCL_FUTURE);
+	if (ret) {
+		printf("mlockall failed\n");
+		goto out;
+	}
+
+	sprintf(dev_name, "%s%s/gpio%d",
+		    DEV_PATH, ti.pin_controller, ti.gpio_out);
+	ti.fd_dev_out = open(dev_name, O_RDWR);
+	if (ti.fd_dev_out < 0) {
+		printf("can't open %s\n", dev_name);
+		goto out;
+	}
+
+	if (ti.gpio_out) {
+		value = 0;
+		ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
+		if (ret) {
+			printf("ioctl gpio port output, failed\n");
+			goto out;
+		}
+	}
+
+	sprintf(dev_name, "%s%s/gpio%d",
+		    DEV_PATH, ti.pin_controller, ti.gpio_intr);
+	ti.fd_dev_intr = open(dev_name, O_RDWR);
+	if (ti.fd_dev_intr < 0) {
+		printf("can't open %s\n", dev_name);
+		goto out;
+	}
+
+	if (ti.gpio_intr) {
+		trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
+		value = 1;
+
+		ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
+		if (ret) {
+			printf("ioctl gpio port interrupt, failed\n");
+			goto out;
+		}
+
+		ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
+		if (ret) {
+			printf("ioctl gpio port ts, failed\n");
+			goto out;
+		}
+	}
+
+	if (ti.tracelimit < DEFAULT_LIMIT)
+		tracing(ON);
+
+	signal(SIGTERM, cleanup_and_exit);
+	signal(SIGINT, cleanup_and_exit);
+	setup_sched_parameters(&tattr, ti.prio);
+
+	if (ti.mode == MODE_LOOPBACK)
+		ret = pthread_create(&ti.gpio_task, &tattr,
+					run_gpiobench_loop, NULL);
+	else
+		ret = pthread_create(&ti.gpio_task, &tattr,
+					run_gpiobench_react, NULL);
+
+	if (ret) {
+		printf("pthread_create(gpiotask), failed\n");
+		goto out;
+	}
+
+	pthread_join(ti.gpio_task, NULL);
+	pthread_attr_destroy(&tattr);
+
+out:
+	cleanup();
+	return 0;
+}
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-03  6:04 [PATCH v3] testsuite: App of gpio loopback/react benchmark chensong
@ 2020-08-11  7:15 ` Jan Kiszka
  2020-08-11 15:36   ` Greg Gallagher
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-11  7:15 UTC (permalink / raw)
  To: chensong, xenomai, greg

On 03.08.20 08:04, chensong wrote:
> This a tool to benchmark the latency of GPIO driver,
> it's able to run 2 kinds of benchmark test:
> 
> 1, loopback mode
> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>    like gpio-bcm2835 and gpio-core.c, one is as output,
>    the other is as interrupt
> 2) call write_rt to send a pulse from output
> 3) call read_rt to get timestamps recorded in driver (inner loop)
> 4) also record timespace in user space(outer_loop)
>    outer_loop is inner_loop plus overhead of event wakeup
> 5) ftrace enable/disable
> 
> 2, react mode
> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>    like gpio-bcm2835 and gpio-core.c, one is as ourput,
>    the other is as interrupt
> 2) call read_rt to wait for a pulse from latency box
> 3) call write_rt to send a signal back to latency box
>    as a reaction
> 4) latency box calculates the diff and makes the histogram
> 
> e.g.:
> 1) react mode:
>    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
> 2) loopback mode:
>    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
> 
> CC: Jan Kiszka <jan.kiszka@siemens.com>
> CC: Greg Gallagher <greg@embeddedgreg.com>
> 
> Signed-off-by: chensong <chensong@tj.kylinos.cn>
> ---
>  configure.ac                     |   1 +
>  doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>  testsuite/Makefile.am            |   3 +-
>  testsuite/gpiobench/Makefile.am  |  18 ++
>  testsuite/gpiobench/gpiobench.c  | 654 +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 745 insertions(+), 1 deletion(-)
>  create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>  create mode 100644 testsuite/gpiobench/Makefile.am
>  create mode 100644 testsuite/gpiobench/gpiobench.c
> 
> diff --git a/configure.ac b/configure.ac
> index 29fefab..164c449 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>  	testsuite/latency/Makefile \
>  	testsuite/switchtest/Makefile \
>  	testsuite/gpiotest/Makefile \
> +	testsuite/gpiobench/Makefile \
>  	testsuite/spitest/Makefile \
>  	testsuite/smokey/Makefile \
>  	testsuite/smokey/arith/Makefile \
> diff --git a/doc/asciidoc/man1/gpiobench.adoc b/doc/asciidoc/man1/gpiobench.adoc
> new file mode 100644
> index 0000000..bd32ea8
> --- /dev/null
> +++ b/doc/asciidoc/man1/gpiobench.adoc
> @@ -0,0 +1,70 @@
> +// ** The above line should force tbl to be a preprocessor **
> +// Man page for gpiobench
> +//
> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
> +//
> +// You may distribute under the terms of the GNU General Public
> +// License as specified in the file COPYING that comes with the
> +// Xenomai distribution.
> +//
> +//
> +GPIOBENCH(1)
> +==========
> +:doctype: manpage
> +:revdate: 2020/08/03
> +:man source: Xenomai
> +:man version: {xenover}
> +:man manual: Xenomai Manual
> +
> +NAME
> +-----
> +gpiobench - Xenomai gpio latency benchmark
> +
> +SYNOPSIS
> +---------
> +// The general command line
> +*gpiobench* [ options ]
> +
> +DESCRIPTION
> +------------
> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
> +benchmark program.  The system must run a suitable Xenomai enabled kernel with
> +the respective module (xeno_timerbench).
> +
> +OPTIONS
> +--------
> +*gpiobench* accepts the following options:
> +
> +*-h <histogram-size>*::
> +default = 100, increase if your last bucket is full
> +
> +*-l <num-of-loops>*::
> +default=1000, number of loops to run the test
> +
> +*-q <quiet>*::
> +print only a summary on exit
> +
> +*-m <test-mode>*::
> +0 = loopback (default), 1 = react
> +
> +*-c <pin-controller>*::
> +name of pin controller
> +
> +*-o <output-pin>*::
> +number of gpio pin as output
> +
> +*-i <interrupt-pin>*::
> +number of gpin pin as interrupt
> +
> +*-p <priority>*::
> +default = 99, task priority
> +
> +*-b <bracktrace>*::
> +default = 1000, send break trace command when latency > breaktrace
> +
> +
> +
> +AUTHOR
> +-------
> +*gpiobench* was written by song chen. This man page
> +was written by song chen.
> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> index 76d108e..4932f6d 100644
> --- a/testsuite/Makefile.am
> +++ b/testsuite/Makefile.am
> @@ -1,5 +1,5 @@
>  
> -SUBDIRS = latency smokey
> +SUBDIRS = latency smokey gpiobench
>  
>  if XENO_COBALT
>  SUBDIRS += 		\
> @@ -13,6 +13,7 @@ endif
>  DIST_SUBDIRS =		\
>  	clocktest	\
>  	gpiotest	\
> +	gpiobench   \
>  	latency		\
>  	smokey		\
>  	spitest		\
> diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
> new file mode 100644
> index 0000000..cca3395
> --- /dev/null
> +++ b/testsuite/gpiobench/Makefile.am
> @@ -0,0 +1,18 @@
> +testdir = @XENO_TEST_DIR@
> +
> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
> +
> +test_PROGRAMS = gpiobench
> +
> +gpiobench_SOURCES = gpiobench.c
> +
> +gpiobench_CPPFLAGS = 		\
> +	$(XENO_USER_CFLAGS)	\
> +	-I$(top_srcdir)/include
> +
> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
> +
> +gpiobench_LDADD =			\
> +	@XENO_CORE_LDADD@	\
> +	@XENO_USER_LDADD@	\
> +	-lpthread -lrt -lm
> diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
> new file mode 100644
> index 0000000..7f19837
> --- /dev/null
> +++ b/testsuite/gpiobench/gpiobench.c
> @@ -0,0 +1,654 @@
> +/*
> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining
> + * a copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sublicense, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included
> + * in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + */
> +#include <stdlib.h>
> +#include <math.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <error.h>
> +#include <signal.h>
> +#include <sched.h>
> +#include <time.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +#include <pthread.h>
> +#include <semaphore.h>
> +#include <sys/timerfd.h>
> +#include <xeno_config.h>
> +#include <rtdm/testing.h>
> +#include <rtdm/gpio.h>
> +#include <boilerplate/trace.h>
> +#include <xenomai/init.h>
> +#include <sys/mman.h>
> +#include <getopt.h>
> +
> +#define NS_PER_MS (1000000)
> +#define NS_PER_S (1000000000)
> +
> +#define DEFAULT_PRIO 99
> +#define VERSION_STRING "0.1"
> +#define GPIO_HIGH 1
> +#define GPIO_LOW  0
> +#define MAX_HIST		100
> +#define MAX_CYCLES 1000000
> +#define DEFAULT_LIMIT 1000
> +#define DEV_PATH    "/dev/rtdm/"
> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
> +#define ON  "1"
> +#define OFF "0"
> +
> +enum {
> +	MODE_LOOPBACK,
> +	MODE_REACT,
> +	MODE_ALL
> +};
> +
> +/* Struct for statistics */
> +struct test_stat {
> +	long inner_min;
> +	long inner_max;
> +	double inner_avg;
> +	long *inner_hist_array;
> +	long inner_hist_overflow;
> +	long outer_min;
> +	long outer_max;
> +	double outer_avg;
> +	long *outer_hist_array;
> +	long outer_hist_overflow;
> +};
> +
> +/* Struct for information */
> +struct test_info {
> +	unsigned long max_cycles;
> +	unsigned long total_cycles;
> +	unsigned long max_histogram;
> +	int mode;
> +	int prio;
> +	int quiet;
> +	int tracelimit;
> +	int fd_dev_intr;
> +	int fd_dev_out;
> +	char pin_controller[32];
> +	pthread_t gpio_task;
> +	int gpio_intr;
> +	int gpio_out;
> +	struct test_stat ts;
> +};
> +
> +struct test_info ti;
> +/* Print usage information */
> +static void display_help(void)
> +{
> +	printf("gpiobench V %s\n", VERSION_STRING);
> +	printf("Usage:\n"
> +	       "gpiobench <options>\n\n"
> +
> +	       "-b       --breaktrace=USEC  send break trace command when latency > USEC\n"
> +	       "                            default=1000\n"
> +	       "-h       --histogram=US     dump a latency histogram to stdout after the run\n"
> +	       "                            US is the max time to be tracked in microseconds,\n"
> +	       "                            default=100\n"
> +	       "-l       --loops            number of loops, default=1000\n"
> +	       "-p       --prio             priority of highest prio thread, defaults=99\n"
> +	       "-q       --quiet            print only a summary on exit\n"
> +	       "-o       --output           gpio port number for output, no default value,\n"
> +	       "                            must be specified\n"
> +	       "-i       --intr             gpio port number as an interrupt, no default value,\n"
> +	       "                            must be specified\n"
> +	       "-c       --pinctrl          gpio pin controller's name, no default value,\n"
> +	       "                            must be specified\n"
> +	       "-m       --testmode         0 is loopback mode\n"
> +	       "                            1 is react mode which works with a latency box,\n"
> +	       "                            default=0\n\n"
> +
> +	       "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
> +		);
> +}
> +
> +static void process_options(int argc, char *argv[])
> +{
> +	int c = 0;
> +	static const char optstring[] = "h:p:m:l:c:b:i:o:q";
> +
> +	struct option long_options[] = {
> +		{ "bracetrace", required_argument, 0, 'b'},
> +		{ "histogram", required_argument, 0, 'h'},
> +		{ "loops", required_argument, 0, 'l'},
> +		{ "prio", required_argument, 0, 'p'},
> +		{ "quiet", no_argument, 0, 'q'},
> +		{ "output", required_argument, 0, 'o'},
> +		{ "intr", required_argument, 0, 'i'},
> +		{ "pinctrl", required_argument, 0, 'c'},
> +		{ "testmode", required_argument, 0, 'm'},
> +		{ 0, 0, 0, 0},
> +	};
> +
> +	while ((c = getopt_long(argc, argv, optstring, long_options,
> +						NULL)) != -1) {
> +		switch (c) {
> +		case 'h':
> +			ti.max_histogram = atoi(optarg);
> +			break;
> +
> +		case 'p':
> +			ti.prio = atoi(optarg);
> +			break;
> +
> +		case 'l':
> +			ti.max_cycles = atoi(optarg);
> +			break;
> +
> +		case 'q':
> +			ti.quiet = 1;
> +			break;
> +
> +		case 'b':
> +			ti.tracelimit = atoi(optarg);
> +			break;
> +
> +		case 'i':
> +			ti.gpio_intr = atoi(optarg);
> +			break;
> +
> +		case 'o':
> +			ti.gpio_out = atoi(optarg);
> +			break;
> +
> +		case 'c':
> +			strcpy(ti.pin_controller, optarg);
> +			break;
> +
> +		case 'm':
> +			ti.mode = atoi(optarg) >=
> +				MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
> +			break;
> +
> +		default:
> +			display_help();
> +			exit(2);
> +		}
> +	}
> +
> +	if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
> +				|| (strlen(ti.pin_controller) == 0)) {
> +		display_help();
> +		exit(2);
> +	}
> +
> +	ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
> +	ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
> +
> +	ti.max_histogram = ti.max_histogram > MAX_HIST ?
> +		MAX_HIST : ti.max_histogram;
> +	ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
> +	ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
> +}
> +
> +static int thread_msleep(unsigned int ms)
> +{
> +	struct timespec ts = {
> +		.tv_sec = (ms * NS_PER_MS) / NS_PER_S,
> +		.tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
> +	};
> +
> +	return -nanosleep(&ts, NULL);
> +}
> +
> +static inline int64_t calc_us(struct timespec t)
> +{
> +	return (t.tv_sec * NS_PER_S + t.tv_nsec);
> +}
> +
> +static int setevent(char *event, char *val)
> +{
> +	int fd;
> +	int ret;
> +
> +	fd = open(event, O_WRONLY);
> +	if (fd < 0) {
> +		printf("unable to open %s\n", event);
> +		return -1;
> +	}
> +
> +	ret = write(fd, val, strlen(val));
> +	if (ret < 0) {
> +		printf("unable to write %s to %s\n", val, event);
> +		close(fd);
> +		return -1;
> +	}
> +
> +	close(fd);
> +	return 0;
> +}
> +
> +static void tracing(char *enable)
> +{
> +	setevent(TRACING_EVENTS, enable);
> +	setevent(TRACING_ON, enable);
> +}
> +
> +#define write_check(__fd, __buf, __len)			\
> +	do {						\
> +		int __ret = write(__fd, __buf, __len);	\
> +		(void)__ret;				\
> +	} while (0)
> +
> +#define TRACEBUFSIZ 1024
> +static __thread char tracebuf[TRACEBUFSIZ];
> +
> +static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
> +static void tracemark(char *fmt, ...)
> +{
> +	va_list ap;
> +	int len;
> +	int tracemark_fd;
> +
> +	tracemark_fd = open(TRACE_MARKER, O_WRONLY);
> +	if (tracemark_fd == -1) {
> +		printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
> +		return;
> +	}
> +
> +	/* bail out if we're not tracing */
> +	/* or if the kernel doesn't support trace_mark */
> +	if (tracemark_fd < 0)
> +		return;
> +
> +	va_start(ap, fmt);
> +	len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
> +	va_end(ap);
> +	write_check(tracemark_fd, tracebuf, len);
> +
> +	close(tracemark_fd);
> +}
> +
> +static int rw_gpio(int value, int index)
> +{
> +	int ret;
> +	struct timespec timestamp;
> +	struct rtdm_gpio_readout rdo;
> +	uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
> +
> +	clock_gettime(CLOCK_MONOTONIC, &timestamp);
> +	gpio_write = calc_us(timestamp);
> +
> +	ret = write(ti.fd_dev_out, &value, sizeof(value));
> +	if (ret < 0) {
> +		printf("write GPIO, failed\n");
> +		return ret;
> +	}
> +
> +	ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
> +	if (ret < 0) {
> +		printf("read GPIO, failed\n");
> +		return ret;
> +	}
> +
> +	clock_gettime(CLOCK_MONOTONIC, &timestamp);
> +	gpio_read = calc_us(timestamp);
> +
> +	inner_diff = (rdo.timestamp - gpio_write) / 1000;
> +	outer_diff = (gpio_read - gpio_write) / 1000;
> +
> +	if (inner_diff < ti.ts.inner_min)
> +		ti.ts.inner_min = inner_diff;
> +	if (inner_diff > ti.ts.inner_max)
> +		ti.ts.inner_max = inner_diff;
> +	ti.ts.inner_avg += (double) inner_diff;
> +	if (inner_diff >= ti.max_histogram)
> +		ti.ts.inner_hist_overflow++;
> +	else
> +		ti.ts.inner_hist_array[inner_diff]++;
> +
> +	if (outer_diff < ti.ts.outer_min)
> +		ti.ts.outer_min = outer_diff;
> +	if (inner_diff > ti.ts.outer_max)
> +		ti.ts.outer_max = outer_diff;
> +	ti.ts.outer_avg += (double) outer_diff;
> +	if (outer_diff >= ti.max_histogram)
> +		ti.ts.outer_hist_overflow++;
> +	else
> +		ti.ts.outer_hist_array[outer_diff]++;
> +
> +	if (ti.quiet == 0)
> +		printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
> +					index, inner_diff, outer_diff);
> +
> +	return outer_diff;
> +}
> +
> +static void *run_gpiobench_loop(void *cookie)
> +{
> +	int i, ret;
> +
> +	printf("----rt task, gpio loop, test run----\n");
> +
> +	for (i = 0; i < ti.max_cycles; i++) {
> +		ti.total_cycles = i;
> +		/* send a high level pulse from gpio output pin and
> +		 * receive an interrupt from the other gpio pin,
> +		 * measuring the time elapsed between the two events
> +		 */
> +		ret = rw_gpio(GPIO_HIGH, i);
> +		if (ret < 0) {
> +			printf("RW GPIO, failed\n");
> +			break;
> +		} else if (ret > ti.tracelimit) {
> +			tracemark("hit latency threshold (%d > %d), index: %d",
> +						ret, ti.tracelimit, i);
> +			break;
> +		}
> +
> +		/*take a break, nanosleep here will not jeopardize the latency*/
> +		thread_msleep(10);
> +
> +		ret = rw_gpio(GPIO_LOW, i);
> +		/* send a low level pulse from gpio output pin and
> +		 * receive an interrupt from the other gpio pin,
> +		 * measuring the time elapsed between the two events
> +		 */
> +		if (ret < 0) {
> +			printf("RW GPIO, failed\n");
> +			break;
> +		} else if (ti.tracelimit && ret > ti.tracelimit) {
> +			tracemark("hit latency threshold (%d > %d), index: %d",
> +						ret, ti.tracelimit, i);
> +			break;
> +		}
> +
> +		/*take a break, nanosleep here will not jeopardize the latency*/
> +		thread_msleep(10);
> +	}
> +
> +	ti.ts.inner_avg /= (ti.total_cycles * 2);
> +	ti.ts.outer_avg /= (ti.total_cycles * 2);
> +
> +	return NULL;
> +}
> +
> +static void *run_gpiobench_react(void *cookie)
> +{
> +	int value, ret, i;
> +	struct rtdm_gpio_readout rdo;
> +
> +	printf("----rt task, gpio react, test run----\n");
> +
> +	for (i = 0; i < ti.max_cycles; i++) {
> +		/* received a pulse from latency box from one of
> +		 * the gpio pin pair
> +		 */
> +		ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
> +		if (ret < 0) {
> +			printf("RW GPIO read, failed\n");
> +			break;
> +		}
> +
> +		if (ti.quiet == 0)
> +			printf("idx: %d, received signal from latency box\n",
> +				    i);
> +
> +		/* send a signal back from the other gpio pin
> +		 * to latency box as the acknowledge,
> +		 * latency box will measure the time elapsed
> +		 * between the two events
> +		 */
> +		value = GPIO_HIGH;
> +		ret = write(ti.fd_dev_out, &value, sizeof(value));
> +		if (ret < 0) {
> +			printf("RW GPIO write, failed\n");
> +			break;
> +		}
> +
> +		if (ti.quiet == 0)
> +			printf("idx: %d, sent reaction to latency box\n", i);
> +	}
> +
> +	return NULL;
> +}
> +
> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
> +{
> +	struct sched_param p;
> +	int ret;
> +
> +	ret = pthread_attr_init(attr);
> +	if (ret) {
> +		printf("pthread_attr_init(), failed\n");
> +		return;
> +	}
> +
> +	ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
> +	if (ret) {
> +		printf("pthread_attr_setinheritsched(), failed\n");
> +		return;
> +	}
> +
> +	ret = pthread_attr_setschedpolicy(attr,
> +				prio ? SCHED_FIFO : SCHED_OTHER);
> +	if (ret) {
> +		printf("pthread_attr_setschedpolicy(), failed\n");
> +		return;
> +	}
> +
> +	p.sched_priority = prio;
> +	ret = pthread_attr_setschedparam(attr, &p);
> +	if (ret) {
> +		printf("pthread_attr_setschedparam(), failed\n");
> +		return;
> +	}
> +}
> +
> +static void init_ti(void)
> +{
> +	memset(&ti, 0, sizeof(struct test_info));
> +	ti.prio = DEFAULT_PRIO;
> +	ti.max_cycles = MAX_CYCLES;
> +	ti.total_cycles = MAX_CYCLES;
> +	ti.max_histogram = MAX_HIST;
> +	ti.tracelimit = DEFAULT_LIMIT;
> +	ti.quiet = 0;
> +	ti.gpio_out = -1;
> +	ti.gpio_intr = -1;
> +	ti.mode = MODE_LOOPBACK;
> +
> +	ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
> +	ti.ts.inner_max = ti.ts.outer_max = 0;
> +	ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
> +}
> +
> +static void print_hist(void)
> +{
> +	int i;
> +
> +	printf("\n");
> +	printf("# Inner Loop Histogram\n");
> +	printf("# Inner Loop latency is the latency in kernel space\n"
> +		   "# between gpio_set_value and irq handler\n");
> +
> +	for (i = 0; i < ti.max_histogram; i++) {
> +		unsigned long curr_latency = ti.ts.inner_hist_array[i];
> +
> +		printf("%06d ", i);
> +		printf("%06lu\n", curr_latency);
> +	}
> +
> +	printf("# Total:");
> +	printf(" %09lu", ti.total_cycles);
> +	printf("\n");
> +
> +	printf("# Min Latencies:");
> +	printf(" %05lu", ti.ts.inner_min);
> +	printf("\n");
> +	printf("# Avg Latencies:");
> +	printf(" %05lf", ti.ts.inner_avg);
> +	printf("\n");
> +	printf("# Max Latencies:");
> +	printf(" %05lu", ti.ts.inner_max);
> +	printf("\n");
> +
> +	printf("\n");
> +	printf("\n");
> +
> +	printf("# Outer Loop Histogram\n");
> +	printf("# Outer Loop latency is the latency in user space\n"
> +		   "# between write and read\n"
> +		   "# Technically, outer loop latency is inner loop latercy\n"
> +		   "# plus overhead of event wakeup\n");
> +
> +	for (i = 0; i < ti.max_histogram; i++) {
> +		unsigned long curr_latency = ti.ts.outer_hist_array[i];
> +
> +		printf("%06d ", i);
> +		printf("%06lu\n", curr_latency);
> +	}
> +
> +	printf("# Total:");
> +	printf(" %09lu", ti.total_cycles);
> +	printf("\n");
> +
> +	printf("# Min Latencies:");
> +	printf(" %05lu", ti.ts.outer_min);
> +	printf("\n");
> +	printf("# Avg Latencies:");
> +	printf(" %05lf", ti.ts.outer_avg);
> +	printf("\n");
> +	printf("# Max Latencies:");
> +	printf(" %05lu", ti.ts.outer_max);
> +	printf("\n");
> +}
> +
> +static void cleanup(void)
> +{
> +	int ret;
> +
> +	if (ti.tracelimit < DEFAULT_LIMIT)
> +		tracing(OFF);
> +
> +	ret = close(ti.fd_dev_out);
> +	if (ret < 0)
> +		printf("can't close gpio_out device\n");
> +
> +	ret = close(ti.fd_dev_intr);
> +	if (ret < 0)
> +		printf("can't close gpio_intr device\n");
> +
> +	if (ti.mode == MODE_LOOPBACK)
> +		print_hist();
> +
> +}
> +
> +static void cleanup_and_exit(int sig)
> +{
> +	printf("Signal %d received\n", sig);
> +	cleanup();
> +	exit(0);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	struct sigaction sa __attribute__((unused));
> +	int ret = 0;
> +	pthread_attr_t tattr;
> +	int trigger, value;
> +	char dev_name[64];
> +
> +	init_ti();
> +
> +	process_options(argc, argv);
> +
> +	ret = mlockall(MCL_CURRENT|MCL_FUTURE);
> +	if (ret) {
> +		printf("mlockall failed\n");
> +		goto out;
> +	}
> +
> +	sprintf(dev_name, "%s%s/gpio%d",
> +		    DEV_PATH, ti.pin_controller, ti.gpio_out);
> +	ti.fd_dev_out = open(dev_name, O_RDWR);
> +	if (ti.fd_dev_out < 0) {
> +		printf("can't open %s\n", dev_name);
> +		goto out;
> +	}
> +
> +	if (ti.gpio_out) {
> +		value = 0;
> +		ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
> +		if (ret) {
> +			printf("ioctl gpio port output, failed\n");
> +			goto out;
> +		}
> +	}
> +
> +	sprintf(dev_name, "%s%s/gpio%d",
> +		    DEV_PATH, ti.pin_controller, ti.gpio_intr);
> +	ti.fd_dev_intr = open(dev_name, O_RDWR);
> +	if (ti.fd_dev_intr < 0) {
> +		printf("can't open %s\n", dev_name);
> +		goto out;
> +	}
> +
> +	if (ti.gpio_intr) {
> +		trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
> +		value = 1;
> +
> +		ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
> +		if (ret) {
> +			printf("ioctl gpio port interrupt, failed\n");
> +			goto out;
> +		}
> +
> +		ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
> +		if (ret) {
> +			printf("ioctl gpio port ts, failed\n");
> +			goto out;
> +		}
> +	}
> +
> +	if (ti.tracelimit < DEFAULT_LIMIT)
> +		tracing(ON);
> +
> +	signal(SIGTERM, cleanup_and_exit);
> +	signal(SIGINT, cleanup_and_exit);
> +	setup_sched_parameters(&tattr, ti.prio);
> +
> +	if (ti.mode == MODE_LOOPBACK)
> +		ret = pthread_create(&ti.gpio_task, &tattr,
> +					run_gpiobench_loop, NULL);
> +	else
> +		ret = pthread_create(&ti.gpio_task, &tattr,
> +					run_gpiobench_react, NULL);
> +
> +	if (ret) {
> +		printf("pthread_create(gpiotask), failed\n");
> +		goto out;
> +	}
> +
> +	pthread_join(ti.gpio_task, NULL);
> +	pthread_attr_destroy(&tattr);
> +
> +out:
> +	cleanup();
> +	return 0;
> +}
> 

Looks good to me. Greg, any comments from your side?

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-11  7:15 ` Jan Kiszka
@ 2020-08-11 15:36   ` Greg Gallagher
  2020-08-12  3:36     ` Greg Gallagher
  0 siblings, 1 reply; 14+ messages in thread
From: Greg Gallagher @ 2020-08-11 15:36 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org

On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> wrote:
>
> On 03.08.20 08:04, chensong wrote:
> > This a tool to benchmark the latency of GPIO driver,
> > it's able to run 2 kinds of benchmark test:
> >
> > 1, loopback mode
> > 1) apply 2 gpio pins by calling service in gpio RTDM driver
> >    like gpio-bcm2835 and gpio-core.c, one is as output,
> >    the other is as interrupt
> > 2) call write_rt to send a pulse from output
> > 3) call read_rt to get timestamps recorded in driver (inner loop)
> > 4) also record timespace in user space(outer_loop)
> >    outer_loop is inner_loop plus overhead of event wakeup
> > 5) ftrace enable/disable
> >
> > 2, react mode
> > 1) apply 2 gpio pins by calling service in gpio RTDM driver
> >    like gpio-bcm2835 and gpio-core.c, one is as ourput,
> >    the other is as interrupt
> > 2) call read_rt to wait for a pulse from latency box
> > 3) call write_rt to send a signal back to latency box
> >    as a reaction
> > 4) latency box calculates the diff and makes the histogram
> >
> > e.g.:
> > 1) react mode:
> >    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
> > 2) loopback mode:
> >    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
> >
> > CC: Jan Kiszka <jan.kiszka@siemens.com>
> > CC: Greg Gallagher <greg@embeddedgreg.com>
> >
> > Signed-off-by: chensong <chensong@tj.kylinos.cn>
> > ---
> >  configure.ac                     |   1 +
> >  doc/asciidoc/man1/gpiobench.adoc |  70 +++++
> >  testsuite/Makefile.am            |   3 +-
> >  testsuite/gpiobench/Makefile.am  |  18 ++
> >  testsuite/gpiobench/gpiobench.c  | 654 +++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 745 insertions(+), 1 deletion(-)
> >  create mode 100644 doc/asciidoc/man1/gpiobench.adoc
> >  create mode 100644 testsuite/gpiobench/Makefile.am
> >  create mode 100644 testsuite/gpiobench/gpiobench.c
> >
> > diff --git a/configure.ac b/configure.ac
> > index 29fefab..164c449 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
> >       testsuite/latency/Makefile \
> >       testsuite/switchtest/Makefile \
> >       testsuite/gpiotest/Makefile \
> > +     testsuite/gpiobench/Makefile \
> >       testsuite/spitest/Makefile \
> >       testsuite/smokey/Makefile \
> >       testsuite/smokey/arith/Makefile \
> > diff --git a/doc/asciidoc/man1/gpiobench.adoc b/doc/asciidoc/man1/gpiobench.adoc
> > new file mode 100644
> > index 0000000..bd32ea8
> > --- /dev/null
> > +++ b/doc/asciidoc/man1/gpiobench.adoc
> > @@ -0,0 +1,70 @@
> > +// ** The above line should force tbl to be a preprocessor **
> > +// Man page for gpiobench
> > +//
> > +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
> > +//
> > +// You may distribute under the terms of the GNU General Public
> > +// License as specified in the file COPYING that comes with the
> > +// Xenomai distribution.
> > +//
> > +//
> > +GPIOBENCH(1)
> > +==========
> > +:doctype: manpage
> > +:revdate: 2020/08/03
> > +:man source: Xenomai
> > +:man version: {xenover}
> > +:man manual: Xenomai Manual
> > +
> > +NAME
> > +-----
> > +gpiobench - Xenomai gpio latency benchmark
> > +
> > +SYNOPSIS
> > +---------
> > +// The general command line
> > +*gpiobench* [ options ]
> > +
> > +DESCRIPTION
> > +------------
> > +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
> > +benchmark program.  The system must run a suitable Xenomai enabled kernel with
> > +the respective module (xeno_timerbench).
> > +
> > +OPTIONS
> > +--------
> > +*gpiobench* accepts the following options:
> > +
> > +*-h <histogram-size>*::
> > +default = 100, increase if your last bucket is full
> > +
> > +*-l <num-of-loops>*::
> > +default=1000, number of loops to run the test
> > +
> > +*-q <quiet>*::
> > +print only a summary on exit
> > +
> > +*-m <test-mode>*::
> > +0 = loopback (default), 1 = react
> > +
> > +*-c <pin-controller>*::
> > +name of pin controller

Consider changing this to from pin controller to something a little
more generic.  On the bcm2835 the gpios are
> > +
> > +*-o <output-pin>*::
> > +number of gpio pin as output
> > +
> > +*-i <interrupt-pin>*::
> > +number of gpin pin as interrupt
> > +
> > +*-p <priority>*::
> > +default = 99, task priority
> > +
> > +*-b <bracktrace>*::
> > +default = 1000, send break trace command when latency > breaktrace
> > +
> > +
> > +
> > +AUTHOR
> > +-------
> > +*gpiobench* was written by song chen. This man page
> > +was written by song chen.
> > diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> > index 76d108e..4932f6d 100644
> > --- a/testsuite/Makefile.am
> > +++ b/testsuite/Makefile.am
> > @@ -1,5 +1,5 @@
> >
> > -SUBDIRS = latency smokey
> > +SUBDIRS = latency smokey gpiobench
> >
> >  if XENO_COBALT
> >  SUBDIRS +=           \
> > @@ -13,6 +13,7 @@ endif
> >  DIST_SUBDIRS =               \
> >       clocktest       \
> >       gpiotest        \
> > +     gpiobench   \
> >       latency         \
> >       smokey          \
> >       spitest         \
> > diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
> > new file mode 100644
> > index 0000000..cca3395
> > --- /dev/null
> > +++ b/testsuite/gpiobench/Makefile.am
> > @@ -0,0 +1,18 @@
> > +testdir = @XENO_TEST_DIR@
> > +
> > +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
> > +
> > +test_PROGRAMS = gpiobench
> > +
> > +gpiobench_SOURCES = gpiobench.c
> > +
> > +gpiobench_CPPFLAGS =                 \
> > +     $(XENO_USER_CFLAGS)     \
> > +     -I$(top_srcdir)/include
> > +
> > +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
> > +
> > +gpiobench_LDADD =                    \
> > +     @XENO_CORE_LDADD@       \
> > +     @XENO_USER_LDADD@       \
> > +     -lpthread -lrt -lm
> > diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
> > new file mode 100644
> > index 0000000..7f19837
> > --- /dev/null
> > +++ b/testsuite/gpiobench/gpiobench.c
> > @@ -0,0 +1,654 @@
> > +/*
> > + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining
> > + * a copy of this software and associated documentation files (the
> > + * "Software"), to deal in the Software without restriction, including
> > + * without limitation the rights to use, copy, modify, merge, publish,
> > + * distribute, sublicense, and/or sell copies of the Software, and to
> > + * permit persons to whom the Software is furnished to do so, subject to
> > + * the following conditions:
> > + *
> > + * The above copyright notice and this permission notice shall be included
> > + * in all copies or substantial portions of the Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> > + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > + */
> > +#include <stdlib.h>
> > +#include <math.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <errno.h>
> > +#include <error.h>
> > +#include <signal.h>
> > +#include <sched.h>
> > +#include <time.h>
> > +#include <sys/time.h>
> > +#include <unistd.h>
> > +#include <pthread.h>
> > +#include <semaphore.h>
> > +#include <sys/timerfd.h>
> > +#include <xeno_config.h>
> > +#include <rtdm/testing.h>
> > +#include <rtdm/gpio.h>
> > +#include <boilerplate/trace.h>
> > +#include <xenomai/init.h>
> > +#include <sys/mman.h>
> > +#include <getopt.h>
> > +
> > +#define NS_PER_MS (1000000)
> > +#define NS_PER_S (1000000000)
> > +
> > +#define DEFAULT_PRIO 99
> > +#define VERSION_STRING "0.1"
> > +#define GPIO_HIGH 1
> > +#define GPIO_LOW  0
> > +#define MAX_HIST             100
> > +#define MAX_CYCLES 1000000
> > +#define DEFAULT_LIMIT 1000
> > +#define DEV_PATH    "/dev/rtdm/"
> > +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
> > +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
> > +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
> > +#define ON  "1"
> > +#define OFF "0"
> > +
> > +enum {
> > +     MODE_LOOPBACK,
> > +     MODE_REACT,
> > +     MODE_ALL
> > +};
> > +
> > +/* Struct for statistics */
> > +struct test_stat {
> > +     long inner_min;
> > +     long inner_max;
> > +     double inner_avg;
> > +     long *inner_hist_array;
> > +     long inner_hist_overflow;
> > +     long outer_min;
> > +     long outer_max;
> > +     double outer_avg;
> > +     long *outer_hist_array;
> > +     long outer_hist_overflow;
> > +};
> > +
> > +/* Struct for information */
> > +struct test_info {
> > +     unsigned long max_cycles;
> > +     unsigned long total_cycles;
> > +     unsigned long max_histogram;
> > +     int mode;
> > +     int prio;
> > +     int quiet;
> > +     int tracelimit;
> > +     int fd_dev_intr;
> > +     int fd_dev_out;
> > +     char pin_controller[32];
> > +     pthread_t gpio_task;
> > +     int gpio_intr;
> > +     int gpio_out;
> > +     struct test_stat ts;
> > +};
> > +
> > +struct test_info ti;
> > +/* Print usage information */
> > +static void display_help(void)
> > +{
> > +     printf("gpiobench V %s\n", VERSION_STRING);
> > +     printf("Usage:\n"
> > +            "gpiobench <options>\n\n"
> > +
> > +            "-b       --breaktrace=USEC  send break trace command when latency > USEC\n"
> > +            "                            default=1000\n"
> > +            "-h       --histogram=US     dump a latency histogram to stdout after the run\n"
> > +            "                            US is the max time to be tracked in microseconds,\n"
> > +            "                            default=100\n"
> > +            "-l       --loops            number of loops, default=1000\n"
> > +            "-p       --prio             priority of highest prio thread, defaults=99\n"
> > +            "-q       --quiet            print only a summary on exit\n"
> > +            "-o       --output           gpio port number for output, no default value,\n"
> > +            "                            must be specified\n"
> > +            "-i       --intr             gpio port number as an interrupt, no default value,\n"
> > +            "                            must be specified\n"
> > +            "-c       --pinctrl          gpio pin controller's name, no default value,\n"
> > +            "                            must be specified\n"
> > +            "-m       --testmode         0 is loopback mode\n"
> > +            "                            1 is react mode which works with a latency box,\n"
> > +            "                            default=0\n\n"
> > +
> > +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
> > +             );
> > +}
> > +
> > +static void process_options(int argc, char *argv[])
> > +{
> > +     int c = 0;
> > +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
> > +
> > +     struct option long_options[] = {
> > +             { "bracetrace", required_argument, 0, 'b'},
> > +             { "histogram", required_argument, 0, 'h'},
> > +             { "loops", required_argument, 0, 'l'},
> > +             { "prio", required_argument, 0, 'p'},
> > +             { "quiet", no_argument, 0, 'q'},
> > +             { "output", required_argument, 0, 'o'},
> > +             { "intr", required_argument, 0, 'i'},
> > +             { "pinctrl", required_argument, 0, 'c'},
> > +             { "testmode", required_argument, 0, 'm'},
> > +             { 0, 0, 0, 0},
> > +     };
> > +
> > +     while ((c = getopt_long(argc, argv, optstring, long_options,
> > +                                             NULL)) != -1) {
> > +             switch (c) {
> > +             case 'h':
> > +                     ti.max_histogram = atoi(optarg);
> > +                     break;
> > +
> > +             case 'p':
> > +                     ti.prio = atoi(optarg);
> > +                     break;
> > +
> > +             case 'l':
> > +                     ti.max_cycles = atoi(optarg);
> > +                     break;
> > +
> > +             case 'q':
> > +                     ti.quiet = 1;
> > +                     break;
> > +
> > +             case 'b':
> > +                     ti.tracelimit = atoi(optarg);
> > +                     break;
> > +
> > +             case 'i':
> > +                     ti.gpio_intr = atoi(optarg);
> > +                     break;
> > +
> > +             case 'o':
> > +                     ti.gpio_out = atoi(optarg);
> > +                     break;
> > +
> > +             case 'c':
> > +                     strcpy(ti.pin_controller, optarg);
> > +                     break;
> > +
> > +             case 'm':
> > +                     ti.mode = atoi(optarg) >=
> > +                             MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
> > +                     break;
> > +
> > +             default:
> > +                     display_help();
> > +                     exit(2);
> > +             }
> > +     }
> > +
> > +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
> > +                             || (strlen(ti.pin_controller) == 0)) {
> > +             display_help();
> > +             exit(2);
> > +     }
> > +
> > +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
> > +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
> > +
> > +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
> > +             MAX_HIST : ti.max_histogram;
> > +     ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
> > +     ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
> > +}
> > +
> > +static int thread_msleep(unsigned int ms)
> > +{
> > +     struct timespec ts = {
> > +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
> > +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
> > +     };
> > +
> > +     return -nanosleep(&ts, NULL);
> > +}
> > +
> > +static inline int64_t calc_us(struct timespec t)
> > +{
> > +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
> > +}
> > +
> > +static int setevent(char *event, char *val)
> > +{
> > +     int fd;
> > +     int ret;
> > +
> > +     fd = open(event, O_WRONLY);
> > +     if (fd < 0) {
> > +             printf("unable to open %s\n", event);
> > +             return -1;
> > +     }
> > +
> > +     ret = write(fd, val, strlen(val));
> > +     if (ret < 0) {
> > +             printf("unable to write %s to %s\n", val, event);
> > +             close(fd);
> > +             return -1;
> > +     }
> > +
> > +     close(fd);
> > +     return 0;
> > +}
> > +
> > +static void tracing(char *enable)
> > +{
> > +     setevent(TRACING_EVENTS, enable);
> > +     setevent(TRACING_ON, enable);
> > +}
> > +
> > +#define write_check(__fd, __buf, __len)                      \
> > +     do {                                            \
> > +             int __ret = write(__fd, __buf, __len);  \
> > +             (void)__ret;                            \
> > +     } while (0)
> > +
> > +#define TRACEBUFSIZ 1024
> > +static __thread char tracebuf[TRACEBUFSIZ];
> > +
> > +static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
> > +static void tracemark(char *fmt, ...)
> > +{
> > +     va_list ap;
> > +     int len;
> > +     int tracemark_fd;
> > +
> > +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
> > +     if (tracemark_fd == -1) {
> > +             printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
> > +             return;
> > +     }
> > +
> > +     /* bail out if we're not tracing */
> > +     /* or if the kernel doesn't support trace_mark */
> > +     if (tracemark_fd < 0)
> > +             return;
> > +
> > +     va_start(ap, fmt);
> > +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
> > +     va_end(ap);
> > +     write_check(tracemark_fd, tracebuf, len);
> > +
> > +     close(tracemark_fd);
> > +}
> > +
> > +static int rw_gpio(int value, int index)
> > +{
> > +     int ret;
> > +     struct timespec timestamp;
> > +     struct rtdm_gpio_readout rdo;
> > +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
> > +
> > +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> > +     gpio_write = calc_us(timestamp);
> > +
> > +     ret = write(ti.fd_dev_out, &value, sizeof(value));
> > +     if (ret < 0) {
> > +             printf("write GPIO, failed\n");
> > +             return ret;
> > +     }
> > +
> > +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
> > +     if (ret < 0) {
> > +             printf("read GPIO, failed\n");
> > +             return ret;
> > +     }
> > +
> > +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> > +     gpio_read = calc_us(timestamp);
> > +
> > +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
> > +     outer_diff = (gpio_read - gpio_write) / 1000;
> > +
> > +     if (inner_diff < ti.ts.inner_min)
> > +             ti.ts.inner_min = inner_diff;
> > +     if (inner_diff > ti.ts.inner_max)
> > +             ti.ts.inner_max = inner_diff;
> > +     ti.ts.inner_avg += (double) inner_diff;
> > +     if (inner_diff >= ti.max_histogram)
> > +             ti.ts.inner_hist_overflow++;
> > +     else
> > +             ti.ts.inner_hist_array[inner_diff]++;
> > +
> > +     if (outer_diff < ti.ts.outer_min)
> > +             ti.ts.outer_min = outer_diff;
> > +     if (inner_diff > ti.ts.outer_max)
> > +             ti.ts.outer_max = outer_diff;
> > +     ti.ts.outer_avg += (double) outer_diff;
> > +     if (outer_diff >= ti.max_histogram)
> > +             ti.ts.outer_hist_overflow++;
> > +     else
> > +             ti.ts.outer_hist_array[outer_diff]++;
> > +
> > +     if (ti.quiet == 0)
> > +             printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
> > +                                     index, inner_diff, outer_diff);
> > +
> > +     return outer_diff;
> > +}
> > +
> > +static void *run_gpiobench_loop(void *cookie)
> > +{
> > +     int i, ret;
> > +
> > +     printf("----rt task, gpio loop, test run----\n");
> > +
> > +     for (i = 0; i < ti.max_cycles; i++) {
> > +             ti.total_cycles = i;
> > +             /* send a high level pulse from gpio output pin and
> > +              * receive an interrupt from the other gpio pin,
> > +              * measuring the time elapsed between the two events
> > +              */
> > +             ret = rw_gpio(GPIO_HIGH, i);
> > +             if (ret < 0) {
> > +                     printf("RW GPIO, failed\n");
> > +                     break;
> > +             } else if (ret > ti.tracelimit) {
> > +                     tracemark("hit latency threshold (%d > %d), index: %d",
> > +                                             ret, ti.tracelimit, i);
> > +                     break;
> > +             }
> > +
> > +             /*take a break, nanosleep here will not jeopardize the latency*/
> > +             thread_msleep(10);
> > +
> > +             ret = rw_gpio(GPIO_LOW, i);
> > +             /* send a low level pulse from gpio output pin and
> > +              * receive an interrupt from the other gpio pin,
> > +              * measuring the time elapsed between the two events
> > +              */
> > +             if (ret < 0) {
> > +                     printf("RW GPIO, failed\n");
> > +                     break;
> > +             } else if (ti.tracelimit && ret > ti.tracelimit) {
> > +                     tracemark("hit latency threshold (%d > %d), index: %d",
> > +                                             ret, ti.tracelimit, i);
> > +                     break;
> > +             }
> > +
> > +             /*take a break, nanosleep here will not jeopardize the latency*/
> > +             thread_msleep(10);
> > +     }
> > +
> > +     ti.ts.inner_avg /= (ti.total_cycles * 2);
> > +     ti.ts.outer_avg /= (ti.total_cycles * 2);
> > +
> > +     return NULL;
> > +}
> > +
> > +static void *run_gpiobench_react(void *cookie)
> > +{
> > +     int value, ret, i;
> > +     struct rtdm_gpio_readout rdo;
> > +
> > +     printf("----rt task, gpio react, test run----\n");
> > +
> > +     for (i = 0; i < ti.max_cycles; i++) {
> > +             /* received a pulse from latency box from one of
> > +              * the gpio pin pair
> > +              */
> > +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
> > +             if (ret < 0) {
> > +                     printf("RW GPIO read, failed\n");
> > +                     break;
> > +             }
> > +
> > +             if (ti.quiet == 0)
> > +                     printf("idx: %d, received signal from latency box\n",
> > +                                 i);
> > +
> > +             /* send a signal back from the other gpio pin
> > +              * to latency box as the acknowledge,
> > +              * latency box will measure the time elapsed
> > +              * between the two events
> > +              */
> > +             value = GPIO_HIGH;
> > +             ret = write(ti.fd_dev_out, &value, sizeof(value));
> > +             if (ret < 0) {
> > +                     printf("RW GPIO write, failed\n");
> > +                     break;
> > +             }
> > +
> > +             if (ti.quiet == 0)
> > +                     printf("idx: %d, sent reaction to latency box\n", i);
> > +     }
> > +
> > +     return NULL;
> > +}
> > +
> > +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
> > +{
> > +     struct sched_param p;
> > +     int ret;
> > +
> > +     ret = pthread_attr_init(attr);
> > +     if (ret) {
> > +             printf("pthread_attr_init(), failed\n");
> > +             return;
> > +     }
> > +
> > +     ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
> > +     if (ret) {
> > +             printf("pthread_attr_setinheritsched(), failed\n");
> > +             return;
> > +     }
> > +
> > +     ret = pthread_attr_setschedpolicy(attr,
> > +                             prio ? SCHED_FIFO : SCHED_OTHER);
> > +     if (ret) {
> > +             printf("pthread_attr_setschedpolicy(), failed\n");
> > +             return;
> > +     }
> > +
> > +     p.sched_priority = prio;
> > +     ret = pthread_attr_setschedparam(attr, &p);
> > +     if (ret) {
> > +             printf("pthread_attr_setschedparam(), failed\n");
> > +             return;
> > +     }
> > +}
> > +
> > +static void init_ti(void)
> > +{
> > +     memset(&ti, 0, sizeof(struct test_info));
> > +     ti.prio = DEFAULT_PRIO;
> > +     ti.max_cycles = MAX_CYCLES;
> > +     ti.total_cycles = MAX_CYCLES;
> > +     ti.max_histogram = MAX_HIST;
> > +     ti.tracelimit = DEFAULT_LIMIT;
> > +     ti.quiet = 0;
> > +     ti.gpio_out = -1;
> > +     ti.gpio_intr = -1;
> > +     ti.mode = MODE_LOOPBACK;
> > +
> > +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
> > +     ti.ts.inner_max = ti.ts.outer_max = 0;
> > +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
> > +}
> > +
> > +static void print_hist(void)
> > +{
> > +     int i;
> > +
> > +     printf("\n");
> > +     printf("# Inner Loop Histogram\n");
> > +     printf("# Inner Loop latency is the latency in kernel space\n"
> > +                "# between gpio_set_value and irq handler\n");
> > +
> > +     for (i = 0; i < ti.max_histogram; i++) {
> > +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
> > +
> > +             printf("%06d ", i);
> > +             printf("%06lu\n", curr_latency);
> > +     }
> > +
> > +     printf("# Total:");
> > +     printf(" %09lu", ti.total_cycles);
> > +     printf("\n");
> > +
> > +     printf("# Min Latencies:");
> > +     printf(" %05lu", ti.ts.inner_min);
> > +     printf("\n");
> > +     printf("# Avg Latencies:");
> > +     printf(" %05lf", ti.ts.inner_avg);
> > +     printf("\n");
> > +     printf("# Max Latencies:");
> > +     printf(" %05lu", ti.ts.inner_max);
> > +     printf("\n");
> > +
> > +     printf("\n");
> > +     printf("\n");
> > +
> > +     printf("# Outer Loop Histogram\n");
> > +     printf("# Outer Loop latency is the latency in user space\n"
> > +                "# between write and read\n"
> > +                "# Technically, outer loop latency is inner loop latercy\n"
> > +                "# plus overhead of event wakeup\n");
> > +
> > +     for (i = 0; i < ti.max_histogram; i++) {
> > +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
> > +
> > +             printf("%06d ", i);
> > +             printf("%06lu\n", curr_latency);
> > +     }
> > +
> > +     printf("# Total:");
> > +     printf(" %09lu", ti.total_cycles);
> > +     printf("\n");
> > +
> > +     printf("# Min Latencies:");
> > +     printf(" %05lu", ti.ts.outer_min);
> > +     printf("\n");
> > +     printf("# Avg Latencies:");
> > +     printf(" %05lf", ti.ts.outer_avg);
> > +     printf("\n");
> > +     printf("# Max Latencies:");
> > +     printf(" %05lu", ti.ts.outer_max);
> > +     printf("\n");
> > +}
> > +
> > +static void cleanup(void)
> > +{
> > +     int ret;
> > +
> > +     if (ti.tracelimit < DEFAULT_LIMIT)
> > +             tracing(OFF);
> > +
> > +     ret = close(ti.fd_dev_out);
> > +     if (ret < 0)
> > +             printf("can't close gpio_out device\n");
> > +
> > +     ret = close(ti.fd_dev_intr);
> > +     if (ret < 0)
> > +             printf("can't close gpio_intr device\n");
> > +
> > +     if (ti.mode == MODE_LOOPBACK)
> > +             print_hist();
> > +
> > +}
> > +
> > +static void cleanup_and_exit(int sig)
> > +{
> > +     printf("Signal %d received\n", sig);
> > +     cleanup();
> > +     exit(0);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     struct sigaction sa __attribute__((unused));
> > +     int ret = 0;
> > +     pthread_attr_t tattr;
> > +     int trigger, value;
> > +     char dev_name[64];
> > +
> > +     init_ti();
> > +
> > +     process_options(argc, argv);
> > +
> > +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
> > +     if (ret) {
> > +             printf("mlockall failed\n");
> > +             goto out;
> > +     }
> > +
> > +     sprintf(dev_name, "%s%s/gpio%d",
> > +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
> > +     ti.fd_dev_out = open(dev_name, O_RDWR);
> > +     if (ti.fd_dev_out < 0) {
> > +             printf("can't open %s\n", dev_name);
> > +             goto out;
> > +     }
> > +
> > +     if (ti.gpio_out) {
> > +             value = 0;
> > +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
> > +             if (ret) {
> > +                     printf("ioctl gpio port output, failed\n");
> > +                     goto out;
> > +             }
> > +     }
> > +
> > +     sprintf(dev_name, "%s%s/gpio%d",
> > +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
> > +     ti.fd_dev_intr = open(dev_name, O_RDWR);
> > +     if (ti.fd_dev_intr < 0) {
> > +             printf("can't open %s\n", dev_name);
> > +             goto out;
> > +     }
> > +
> > +     if (ti.gpio_intr) {
> > +             trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
> > +             value = 1;
> > +
> > +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
> > +             if (ret) {
> > +                     printf("ioctl gpio port interrupt, failed\n");
> > +                     goto out;
> > +             }
> > +
> > +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
> > +             if (ret) {
> > +                     printf("ioctl gpio port ts, failed\n");
> > +                     goto out;
> > +             }
> > +     }
> > +
> > +     if (ti.tracelimit < DEFAULT_LIMIT)
> > +             tracing(ON);
> > +
> > +     signal(SIGTERM, cleanup_and_exit);
> > +     signal(SIGINT, cleanup_and_exit);
> > +     setup_sched_parameters(&tattr, ti.prio);
> > +
> > +     if (ti.mode == MODE_LOOPBACK)
> > +             ret = pthread_create(&ti.gpio_task, &tattr,
> > +                                     run_gpiobench_loop, NULL);
> > +     else
> > +             ret = pthread_create(&ti.gpio_task, &tattr,
> > +                                     run_gpiobench_react, NULL);
> > +
> > +     if (ret) {
> > +             printf("pthread_create(gpiotask), failed\n");
> > +             goto out;
> > +     }
> > +
> > +     pthread_join(ti.gpio_task, NULL);
> > +     pthread_attr_destroy(&tattr);
> > +
> > +out:
> > +     cleanup();
> > +     return 0;
> > +}
> >
>
> Looks good to me. Greg, any comments from your side?
>
> Jan
>
> --
> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
> Corporate Competence Center Embedded Linux

Hi,
  I'll run this on the zynq platform for testing tonight.  It looks
good, my only concern is the 'ti.pin_controller' name may be a little
bit misleading, but I want to confirm that assumption tonight.

Thanks

Greg


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-11 15:36   ` Greg Gallagher
@ 2020-08-12  3:36     ` Greg Gallagher
  2020-08-12 14:55       ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: Greg Gallagher @ 2020-08-12  3:36 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org

On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher <greg@embeddedgreg.com> wrote:
>
> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> wrote:
> >
> > On 03.08.20 08:04, chensong wrote:
> > > This a tool to benchmark the latency of GPIO driver,
> > > it's able to run 2 kinds of benchmark test:
> > >
> > > 1, loopback mode
> > > 1) apply 2 gpio pins by calling service in gpio RTDM driver
> > >    like gpio-bcm2835 and gpio-core.c, one is as output,
> > >    the other is as interrupt
> > > 2) call write_rt to send a pulse from output
> > > 3) call read_rt to get timestamps recorded in driver (inner loop)
> > > 4) also record timespace in user space(outer_loop)
> > >    outer_loop is inner_loop plus overhead of event wakeup
> > > 5) ftrace enable/disable
> > >
> > > 2, react mode
> > > 1) apply 2 gpio pins by calling service in gpio RTDM driver
> > >    like gpio-bcm2835 and gpio-core.c, one is as ourput,
> > >    the other is as interrupt
> > > 2) call read_rt to wait for a pulse from latency box
> > > 3) call write_rt to send a signal back to latency box
> > >    as a reaction
> > > 4) latency box calculates the diff and makes the histogram
> > >
> > > e.g.:
> > > 1) react mode:
> > >    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
> > > 2) loopback mode:
> > >    gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
> > >
> > > CC: Jan Kiszka <jan.kiszka@siemens.com>
> > > CC: Greg Gallagher <greg@embeddedgreg.com>
> > >
> > > Signed-off-by: chensong <chensong@tj.kylinos.cn>
> > > ---
> > >  configure.ac                     |   1 +
> > >  doc/asciidoc/man1/gpiobench.adoc |  70 +++++
> > >  testsuite/Makefile.am            |   3 +-
> > >  testsuite/gpiobench/Makefile.am  |  18 ++
> > >  testsuite/gpiobench/gpiobench.c  | 654 +++++++++++++++++++++++++++++++++++++++
> > >  5 files changed, 745 insertions(+), 1 deletion(-)
> > >  create mode 100644 doc/asciidoc/man1/gpiobench.adoc
> > >  create mode 100644 testsuite/gpiobench/Makefile.am
> > >  create mode 100644 testsuite/gpiobench/gpiobench.c
> > >
> > > diff --git a/configure.ac b/configure.ac
> > > index 29fefab..164c449 100644
> > > --- a/configure.ac
> > > +++ b/configure.ac
> > > @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
> > >       testsuite/latency/Makefile \
> > >       testsuite/switchtest/Makefile \
> > >       testsuite/gpiotest/Makefile \
> > > +     testsuite/gpiobench/Makefile \
> > >       testsuite/spitest/Makefile \
> > >       testsuite/smokey/Makefile \
> > >       testsuite/smokey/arith/Makefile \
> > > diff --git a/doc/asciidoc/man1/gpiobench.adoc b/doc/asciidoc/man1/gpiobench.adoc
> > > new file mode 100644
> > > index 0000000..bd32ea8
> > > --- /dev/null
> > > +++ b/doc/asciidoc/man1/gpiobench.adoc
> > > @@ -0,0 +1,70 @@
> > > +// ** The above line should force tbl to be a preprocessor **
> > > +// Man page for gpiobench
> > > +//
> > > +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
> > > +//
> > > +// You may distribute under the terms of the GNU General Public
> > > +// License as specified in the file COPYING that comes with the
> > > +// Xenomai distribution.
> > > +//
> > > +//
> > > +GPIOBENCH(1)
> > > +==========
> > > +:doctype: manpage
> > > +:revdate: 2020/08/03
> > > +:man source: Xenomai
> > > +:man version: {xenover}
> > > +:man manual: Xenomai Manual
> > > +
> > > +NAME
> > > +-----
> > > +gpiobench - Xenomai gpio latency benchmark
> > > +
> > > +SYNOPSIS
> > > +---------
> > > +// The general command line
> > > +*gpiobench* [ options ]
> > > +
> > > +DESCRIPTION
> > > +------------
> > > +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
> > > +benchmark program.  The system must run a suitable Xenomai enabled kernel with
> > > +the respective module (xeno_timerbench).
> > > +
> > > +OPTIONS
> > > +--------
> > > +*gpiobench* accepts the following options:
> > > +
> > > +*-h <histogram-size>*::
> > > +default = 100, increase if your last bucket is full
> > > +
> > > +*-l <num-of-loops>*::
> > > +default=1000, number of loops to run the test
> > > +
> > > +*-q <quiet>*::
> > > +print only a summary on exit
> > > +
> > > +*-m <test-mode>*::
> > > +0 = loopback (default), 1 = react
> > > +
> > > +*-c <pin-controller>*::
> > > +name of pin controller
>
> Consider changing this to from pin controller to something a little
> more generic.  On the bcm2835 the gpios are
> > > +
> > > +*-o <output-pin>*::
> > > +number of gpio pin as output
> > > +
> > > +*-i <interrupt-pin>*::
> > > +number of gpin pin as interrupt
> > > +
> > > +*-p <priority>*::
> > > +default = 99, task priority
> > > +
> > > +*-b <bracktrace>*::
> > > +default = 1000, send break trace command when latency > breaktrace
> > > +
> > > +
> > > +
> > > +AUTHOR
> > > +-------
> > > +*gpiobench* was written by song chen. This man page
> > > +was written by song chen.
> > > diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> > > index 76d108e..4932f6d 100644
> > > --- a/testsuite/Makefile.am
> > > +++ b/testsuite/Makefile.am
> > > @@ -1,5 +1,5 @@
> > >
> > > -SUBDIRS = latency smokey
> > > +SUBDIRS = latency smokey gpiobench
> > >
> > >  if XENO_COBALT
> > >  SUBDIRS +=           \
> > > @@ -13,6 +13,7 @@ endif
> > >  DIST_SUBDIRS =               \
> > >       clocktest       \
> > >       gpiotest        \
> > > +     gpiobench   \
> > >       latency         \
> > >       smokey          \
> > >       spitest         \
> > > diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
> > > new file mode 100644
> > > index 0000000..cca3395
> > > --- /dev/null
> > > +++ b/testsuite/gpiobench/Makefile.am
> > > @@ -0,0 +1,18 @@
> > > +testdir = @XENO_TEST_DIR@
> > > +
> > > +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
> > > +
> > > +test_PROGRAMS = gpiobench
> > > +
> > > +gpiobench_SOURCES = gpiobench.c
> > > +
> > > +gpiobench_CPPFLAGS =                 \
> > > +     $(XENO_USER_CFLAGS)     \
> > > +     -I$(top_srcdir)/include
> > > +
> > > +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
> > > +
> > > +gpiobench_LDADD =                    \
> > > +     @XENO_CORE_LDADD@       \
> > > +     @XENO_USER_LDADD@       \
> > > +     -lpthread -lrt -lm
> > > diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
> > > new file mode 100644
> > > index 0000000..7f19837
> > > --- /dev/null
> > > +++ b/testsuite/gpiobench/gpiobench.c
> > > @@ -0,0 +1,654 @@
> > > +/*
> > > + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person obtaining
> > > + * a copy of this software and associated documentation files (the
> > > + * "Software"), to deal in the Software without restriction, including
> > > + * without limitation the rights to use, copy, modify, merge, publish,
> > > + * distribute, sublicense, and/or sell copies of the Software, and to
> > > + * permit persons to whom the Software is furnished to do so, subject to
> > > + * the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice shall be included
> > > + * in all copies or substantial portions of the Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> > > + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> > > + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> > > + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> > > + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> > > + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> > > + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> > > + */
> > > +#include <stdlib.h>
> > > +#include <math.h>
> > > +#include <stdio.h>
> > > +#include <string.h>
> > > +#include <errno.h>
> > > +#include <error.h>
> > > +#include <signal.h>
> > > +#include <sched.h>
> > > +#include <time.h>
> > > +#include <sys/time.h>
> > > +#include <unistd.h>
> > > +#include <pthread.h>
> > > +#include <semaphore.h>
> > > +#include <sys/timerfd.h>
> > > +#include <xeno_config.h>
> > > +#include <rtdm/testing.h>
> > > +#include <rtdm/gpio.h>
> > > +#include <boilerplate/trace.h>
> > > +#include <xenomai/init.h>
> > > +#include <sys/mman.h>
> > > +#include <getopt.h>
> > > +
> > > +#define NS_PER_MS (1000000)
> > > +#define NS_PER_S (1000000000)
> > > +
> > > +#define DEFAULT_PRIO 99
> > > +#define VERSION_STRING "0.1"
> > > +#define GPIO_HIGH 1
> > > +#define GPIO_LOW  0
> > > +#define MAX_HIST             100
> > > +#define MAX_CYCLES 1000000
> > > +#define DEFAULT_LIMIT 1000
> > > +#define DEV_PATH    "/dev/rtdm/"
> > > +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
> > > +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
> > > +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
> > > +#define ON  "1"
> > > +#define OFF "0"
> > > +
> > > +enum {
> > > +     MODE_LOOPBACK,
> > > +     MODE_REACT,
> > > +     MODE_ALL
> > > +};
> > > +
> > > +/* Struct for statistics */
> > > +struct test_stat {
> > > +     long inner_min;
> > > +     long inner_max;
> > > +     double inner_avg;
> > > +     long *inner_hist_array;
> > > +     long inner_hist_overflow;
> > > +     long outer_min;
> > > +     long outer_max;
> > > +     double outer_avg;
> > > +     long *outer_hist_array;
> > > +     long outer_hist_overflow;
> > > +};
> > > +
> > > +/* Struct for information */
> > > +struct test_info {
> > > +     unsigned long max_cycles;
> > > +     unsigned long total_cycles;
> > > +     unsigned long max_histogram;
> > > +     int mode;
> > > +     int prio;
> > > +     int quiet;
> > > +     int tracelimit;
> > > +     int fd_dev_intr;
> > > +     int fd_dev_out;
> > > +     char pin_controller[32];
> > > +     pthread_t gpio_task;
> > > +     int gpio_intr;
> > > +     int gpio_out;
> > > +     struct test_stat ts;
> > > +};
> > > +
> > > +struct test_info ti;
> > > +/* Print usage information */
> > > +static void display_help(void)
> > > +{
> > > +     printf("gpiobench V %s\n", VERSION_STRING);
> > > +     printf("Usage:\n"
> > > +            "gpiobench <options>\n\n"
> > > +
> > > +            "-b       --breaktrace=USEC  send break trace command when latency > USEC\n"
> > > +            "                            default=1000\n"
> > > +            "-h       --histogram=US     dump a latency histogram to stdout after the run\n"
> > > +            "                            US is the max time to be tracked in microseconds,\n"
> > > +            "                            default=100\n"
> > > +            "-l       --loops            number of loops, default=1000\n"
> > > +            "-p       --prio             priority of highest prio thread, defaults=99\n"
> > > +            "-q       --quiet            print only a summary on exit\n"
> > > +            "-o       --output           gpio port number for output, no default value,\n"
> > > +            "                            must be specified\n"
> > > +            "-i       --intr             gpio port number as an interrupt, no default value,\n"
> > > +            "                            must be specified\n"
> > > +            "-c       --pinctrl          gpio pin controller's name, no default value,\n"
> > > +            "                            must be specified\n"
> > > +            "-m       --testmode         0 is loopback mode\n"
> > > +            "                            1 is react mode which works with a latency box,\n"
> > > +            "                            default=0\n\n"
> > > +
> > > +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
> > > +             );
> > > +}
> > > +
> > > +static void process_options(int argc, char *argv[])
> > > +{
> > > +     int c = 0;
> > > +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
> > > +
> > > +     struct option long_options[] = {
> > > +             { "bracetrace", required_argument, 0, 'b'},
> > > +             { "histogram", required_argument, 0, 'h'},
> > > +             { "loops", required_argument, 0, 'l'},
> > > +             { "prio", required_argument, 0, 'p'},
> > > +             { "quiet", no_argument, 0, 'q'},
> > > +             { "output", required_argument, 0, 'o'},
> > > +             { "intr", required_argument, 0, 'i'},
> > > +             { "pinctrl", required_argument, 0, 'c'},
> > > +             { "testmode", required_argument, 0, 'm'},
> > > +             { 0, 0, 0, 0},
> > > +     };
> > > +
> > > +     while ((c = getopt_long(argc, argv, optstring, long_options,
> > > +                                             NULL)) != -1) {
> > > +             switch (c) {
> > > +             case 'h':
> > > +                     ti.max_histogram = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'p':
> > > +                     ti.prio = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'l':
> > > +                     ti.max_cycles = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'q':
> > > +                     ti.quiet = 1;
> > > +                     break;
> > > +
> > > +             case 'b':
> > > +                     ti.tracelimit = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'i':
> > > +                     ti.gpio_intr = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'o':
> > > +                     ti.gpio_out = atoi(optarg);
> > > +                     break;
> > > +
> > > +             case 'c':
> > > +                     strcpy(ti.pin_controller, optarg);
> > > +                     break;
> > > +
> > > +             case 'm':
> > > +                     ti.mode = atoi(optarg) >=
> > > +                             MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
> > > +                     break;
> > > +
> > > +             default:
> > > +                     display_help();
> > > +                     exit(2);
> > > +             }
> > > +     }
> > > +
> > > +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
> > > +                             || (strlen(ti.pin_controller) == 0)) {
> > > +             display_help();
> > > +             exit(2);
> > > +     }
> > > +
> > > +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
> > > +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
> > > +
> > > +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
> > > +             MAX_HIST : ti.max_histogram;
> > > +     ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
> > > +     ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
> > > +}
> > > +
> > > +static int thread_msleep(unsigned int ms)
> > > +{
> > > +     struct timespec ts = {
> > > +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
> > > +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
> > > +     };
> > > +
> > > +     return -nanosleep(&ts, NULL);
> > > +}
> > > +
> > > +static inline int64_t calc_us(struct timespec t)
> > > +{
> > > +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
> > > +}
> > > +
> > > +static int setevent(char *event, char *val)
> > > +{
> > > +     int fd;
> > > +     int ret;
> > > +
> > > +     fd = open(event, O_WRONLY);
> > > +     if (fd < 0) {
> > > +             printf("unable to open %s\n", event);
> > > +             return -1;
> > > +     }
> > > +
> > > +     ret = write(fd, val, strlen(val));
> > > +     if (ret < 0) {
> > > +             printf("unable to write %s to %s\n", val, event);
> > > +             close(fd);
> > > +             return -1;
> > > +     }
> > > +
> > > +     close(fd);
> > > +     return 0;
> > > +}
> > > +
> > > +static void tracing(char *enable)
> > > +{
> > > +     setevent(TRACING_EVENTS, enable);
> > > +     setevent(TRACING_ON, enable);
> > > +}
> > > +
> > > +#define write_check(__fd, __buf, __len)                      \
> > > +     do {                                            \
> > > +             int __ret = write(__fd, __buf, __len);  \
> > > +             (void)__ret;                            \
> > > +     } while (0)
> > > +
> > > +#define TRACEBUFSIZ 1024
> > > +static __thread char tracebuf[TRACEBUFSIZ];
> > > +
> > > +static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
> > > +static void tracemark(char *fmt, ...)
> > > +{
> > > +     va_list ap;
> > > +     int len;
> > > +     int tracemark_fd;
> > > +
> > > +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
> > > +     if (tracemark_fd == -1) {
> > > +             printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
> > > +             return;
> > > +     }
> > > +
> > > +     /* bail out if we're not tracing */
> > > +     /* or if the kernel doesn't support trace_mark */
> > > +     if (tracemark_fd < 0)
> > > +             return;
> > > +
> > > +     va_start(ap, fmt);
> > > +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
> > > +     va_end(ap);
> > > +     write_check(tracemark_fd, tracebuf, len);
> > > +
> > > +     close(tracemark_fd);
> > > +}
> > > +
> > > +static int rw_gpio(int value, int index)
> > > +{
> > > +     int ret;
> > > +     struct timespec timestamp;
> > > +     struct rtdm_gpio_readout rdo;
> > > +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
> > > +
> > > +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> > > +     gpio_write = calc_us(timestamp);
> > > +
> > > +     ret = write(ti.fd_dev_out, &value, sizeof(value));
> > > +     if (ret < 0) {
> > > +             printf("write GPIO, failed\n");
> > > +             return ret;
> > > +     }
> > > +
> > > +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
> > > +     if (ret < 0) {
> > > +             printf("read GPIO, failed\n");
> > > +             return ret;
> > > +     }
> > > +
> > > +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> > > +     gpio_read = calc_us(timestamp);
> > > +
> > > +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
> > > +     outer_diff = (gpio_read - gpio_write) / 1000;
> > > +
> > > +     if (inner_diff < ti.ts.inner_min)
> > > +             ti.ts.inner_min = inner_diff;
> > > +     if (inner_diff > ti.ts.inner_max)
> > > +             ti.ts.inner_max = inner_diff;
> > > +     ti.ts.inner_avg += (double) inner_diff;
> > > +     if (inner_diff >= ti.max_histogram)
> > > +             ti.ts.inner_hist_overflow++;
> > > +     else
> > > +             ti.ts.inner_hist_array[inner_diff]++;
> > > +
> > > +     if (outer_diff < ti.ts.outer_min)
> > > +             ti.ts.outer_min = outer_diff;
> > > +     if (inner_diff > ti.ts.outer_max)
> > > +             ti.ts.outer_max = outer_diff;
> > > +     ti.ts.outer_avg += (double) outer_diff;
> > > +     if (outer_diff >= ti.max_histogram)
> > > +             ti.ts.outer_hist_overflow++;
> > > +     else
> > > +             ti.ts.outer_hist_array[outer_diff]++;
> > > +
> > > +     if (ti.quiet == 0)
> > > +             printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
> > > +                                     index, inner_diff, outer_diff);
> > > +
> > > +     return outer_diff;
> > > +}
> > > +
> > > +static void *run_gpiobench_loop(void *cookie)
> > > +{
> > > +     int i, ret;
> > > +
> > > +     printf("----rt task, gpio loop, test run----\n");
> > > +
> > > +     for (i = 0; i < ti.max_cycles; i++) {
> > > +             ti.total_cycles = i;
> > > +             /* send a high level pulse from gpio output pin and
> > > +              * receive an interrupt from the other gpio pin,
> > > +              * measuring the time elapsed between the two events
> > > +              */
> > > +             ret = rw_gpio(GPIO_HIGH, i);
> > > +             if (ret < 0) {
> > > +                     printf("RW GPIO, failed\n");
> > > +                     break;
> > > +             } else if (ret > ti.tracelimit) {
> > > +                     tracemark("hit latency threshold (%d > %d), index: %d",
> > > +                                             ret, ti.tracelimit, i);
> > > +                     break;
> > > +             }
> > > +
> > > +             /*take a break, nanosleep here will not jeopardize the latency*/
> > > +             thread_msleep(10);
> > > +
> > > +             ret = rw_gpio(GPIO_LOW, i);
> > > +             /* send a low level pulse from gpio output pin and
> > > +              * receive an interrupt from the other gpio pin,
> > > +              * measuring the time elapsed between the two events
> > > +              */
> > > +             if (ret < 0) {
> > > +                     printf("RW GPIO, failed\n");
> > > +                     break;
> > > +             } else if (ti.tracelimit && ret > ti.tracelimit) {
> > > +                     tracemark("hit latency threshold (%d > %d), index: %d",
> > > +                                             ret, ti.tracelimit, i);
> > > +                     break;
> > > +             }
> > > +
> > > +             /*take a break, nanosleep here will not jeopardize the latency*/
> > > +             thread_msleep(10);
> > > +     }
> > > +
> > > +     ti.ts.inner_avg /= (ti.total_cycles * 2);
> > > +     ti.ts.outer_avg /= (ti.total_cycles * 2);
> > > +
> > > +     return NULL;
> > > +}
> > > +
> > > +static void *run_gpiobench_react(void *cookie)
> > > +{
> > > +     int value, ret, i;
> > > +     struct rtdm_gpio_readout rdo;
> > > +
> > > +     printf("----rt task, gpio react, test run----\n");
> > > +
> > > +     for (i = 0; i < ti.max_cycles; i++) {
> > > +             /* received a pulse from latency box from one of
> > > +              * the gpio pin pair
> > > +              */
> > > +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
> > > +             if (ret < 0) {
> > > +                     printf("RW GPIO read, failed\n");
> > > +                     break;
> > > +             }
> > > +
> > > +             if (ti.quiet == 0)
> > > +                     printf("idx: %d, received signal from latency box\n",
> > > +                                 i);
> > > +
> > > +             /* send a signal back from the other gpio pin
> > > +              * to latency box as the acknowledge,
> > > +              * latency box will measure the time elapsed
> > > +              * between the two events
> > > +              */
> > > +             value = GPIO_HIGH;
> > > +             ret = write(ti.fd_dev_out, &value, sizeof(value));
> > > +             if (ret < 0) {
> > > +                     printf("RW GPIO write, failed\n");
> > > +                     break;
> > > +             }
> > > +
> > > +             if (ti.quiet == 0)
> > > +                     printf("idx: %d, sent reaction to latency box\n", i);
> > > +     }
> > > +
> > > +     return NULL;
> > > +}
> > > +
> > > +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
> > > +{
> > > +     struct sched_param p;
> > > +     int ret;
> > > +
> > > +     ret = pthread_attr_init(attr);
> > > +     if (ret) {
> > > +             printf("pthread_attr_init(), failed\n");
> > > +             return;
> > > +     }
> > > +
> > > +     ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
> > > +     if (ret) {
> > > +             printf("pthread_attr_setinheritsched(), failed\n");
> > > +             return;
> > > +     }
> > > +
> > > +     ret = pthread_attr_setschedpolicy(attr,
> > > +                             prio ? SCHED_FIFO : SCHED_OTHER);
> > > +     if (ret) {
> > > +             printf("pthread_attr_setschedpolicy(), failed\n");
> > > +             return;
> > > +     }
> > > +
> > > +     p.sched_priority = prio;
> > > +     ret = pthread_attr_setschedparam(attr, &p);
> > > +     if (ret) {
> > > +             printf("pthread_attr_setschedparam(), failed\n");
> > > +             return;
> > > +     }
> > > +}
> > > +
> > > +static void init_ti(void)
> > > +{
> > > +     memset(&ti, 0, sizeof(struct test_info));
> > > +     ti.prio = DEFAULT_PRIO;
> > > +     ti.max_cycles = MAX_CYCLES;
> > > +     ti.total_cycles = MAX_CYCLES;
> > > +     ti.max_histogram = MAX_HIST;
> > > +     ti.tracelimit = DEFAULT_LIMIT;
> > > +     ti.quiet = 0;
> > > +     ti.gpio_out = -1;
> > > +     ti.gpio_intr = -1;
> > > +     ti.mode = MODE_LOOPBACK;
> > > +
> > > +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
> > > +     ti.ts.inner_max = ti.ts.outer_max = 0;
> > > +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
> > > +}
> > > +
> > > +static void print_hist(void)
> > > +{
> > > +     int i;
> > > +
> > > +     printf("\n");
> > > +     printf("# Inner Loop Histogram\n");
> > > +     printf("# Inner Loop latency is the latency in kernel space\n"
> > > +                "# between gpio_set_value and irq handler\n");
> > > +
> > > +     for (i = 0; i < ti.max_histogram; i++) {
> > > +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
> > > +
> > > +             printf("%06d ", i);
> > > +             printf("%06lu\n", curr_latency);
> > > +     }
> > > +
> > > +     printf("# Total:");
> > > +     printf(" %09lu", ti.total_cycles);
> > > +     printf("\n");
> > > +
> > > +     printf("# Min Latencies:");
> > > +     printf(" %05lu", ti.ts.inner_min);
> > > +     printf("\n");
> > > +     printf("# Avg Latencies:");
> > > +     printf(" %05lf", ti.ts.inner_avg);
> > > +     printf("\n");
> > > +     printf("# Max Latencies:");
> > > +     printf(" %05lu", ti.ts.inner_max);
> > > +     printf("\n");
> > > +
> > > +     printf("\n");
> > > +     printf("\n");
> > > +
> > > +     printf("# Outer Loop Histogram\n");
> > > +     printf("# Outer Loop latency is the latency in user space\n"
> > > +                "# between write and read\n"
> > > +                "# Technically, outer loop latency is inner loop latercy\n"
> > > +                "# plus overhead of event wakeup\n");
> > > +
> > > +     for (i = 0; i < ti.max_histogram; i++) {
> > > +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
> > > +
> > > +             printf("%06d ", i);
> > > +             printf("%06lu\n", curr_latency);
> > > +     }
> > > +
> > > +     printf("# Total:");
> > > +     printf(" %09lu", ti.total_cycles);
> > > +     printf("\n");
> > > +
> > > +     printf("# Min Latencies:");
> > > +     printf(" %05lu", ti.ts.outer_min);
> > > +     printf("\n");
> > > +     printf("# Avg Latencies:");
> > > +     printf(" %05lf", ti.ts.outer_avg);
> > > +     printf("\n");
> > > +     printf("# Max Latencies:");
> > > +     printf(" %05lu", ti.ts.outer_max);
> > > +     printf("\n");
> > > +}
> > > +
> > > +static void cleanup(void)
> > > +{
> > > +     int ret;
> > > +
> > > +     if (ti.tracelimit < DEFAULT_LIMIT)
> > > +             tracing(OFF);
> > > +
> > > +     ret = close(ti.fd_dev_out);
> > > +     if (ret < 0)
> > > +             printf("can't close gpio_out device\n");
> > > +
> > > +     ret = close(ti.fd_dev_intr);
> > > +     if (ret < 0)
> > > +             printf("can't close gpio_intr device\n");
> > > +
> > > +     if (ti.mode == MODE_LOOPBACK)
> > > +             print_hist();
> > > +
> > > +}
> > > +
> > > +static void cleanup_and_exit(int sig)
> > > +{
> > > +     printf("Signal %d received\n", sig);
> > > +     cleanup();
> > > +     exit(0);
> > > +}
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +     struct sigaction sa __attribute__((unused));
> > > +     int ret = 0;
> > > +     pthread_attr_t tattr;
> > > +     int trigger, value;
> > > +     char dev_name[64];
> > > +
> > > +     init_ti();
> > > +
> > > +     process_options(argc, argv);
> > > +
> > > +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
> > > +     if (ret) {
> > > +             printf("mlockall failed\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     sprintf(dev_name, "%s%s/gpio%d",
> > > +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
> > > +     ti.fd_dev_out = open(dev_name, O_RDWR);
> > > +     if (ti.fd_dev_out < 0) {
> > > +             printf("can't open %s\n", dev_name);
> > > +             goto out;
> > > +     }
> > > +
> > > +     if (ti.gpio_out) {
> > > +             value = 0;
> > > +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
> > > +             if (ret) {
> > > +                     printf("ioctl gpio port output, failed\n");
> > > +                     goto out;
> > > +             }
> > > +     }
> > > +
> > > +     sprintf(dev_name, "%s%s/gpio%d",
> > > +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
> > > +     ti.fd_dev_intr = open(dev_name, O_RDWR);
> > > +     if (ti.fd_dev_intr < 0) {
> > > +             printf("can't open %s\n", dev_name);
> > > +             goto out;
> > > +     }
> > > +
> > > +     if (ti.gpio_intr) {
> > > +             trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
> > > +             value = 1;
> > > +
> > > +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
> > > +             if (ret) {
> > > +                     printf("ioctl gpio port interrupt, failed\n");
> > > +                     goto out;
> > > +             }
> > > +
> > > +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
> > > +             if (ret) {
> > > +                     printf("ioctl gpio port ts, failed\n");
> > > +                     goto out;
> > > +             }
> > > +     }
> > > +
> > > +     if (ti.tracelimit < DEFAULT_LIMIT)
> > > +             tracing(ON);
> > > +
> > > +     signal(SIGTERM, cleanup_and_exit);
> > > +     signal(SIGINT, cleanup_and_exit);
> > > +     setup_sched_parameters(&tattr, ti.prio);
> > > +
> > > +     if (ti.mode == MODE_LOOPBACK)
> > > +             ret = pthread_create(&ti.gpio_task, &tattr,
> > > +                                     run_gpiobench_loop, NULL);
> > > +     else
> > > +             ret = pthread_create(&ti.gpio_task, &tattr,
> > > +                                     run_gpiobench_react, NULL);
> > > +
> > > +     if (ret) {
> > > +             printf("pthread_create(gpiotask), failed\n");
> > > +             goto out;
> > > +     }
> > > +
> > > +     pthread_join(ti.gpio_task, NULL);
> > > +     pthread_attr_destroy(&tattr);
> > > +
> > > +out:
> > > +     cleanup();
> > > +     return 0;
> > > +}
> > >
> >
> > Looks good to me. Greg, any comments from your side?
> >
> > Jan
> >
> > --
> > Siemens AG, Corporate Technology, CT RDA IOT SES-DE
> > Corporate Competence Center Embedded Linux
>
> Hi,
>   I'll run this on the zynq platform for testing tonight.  It looks
> good, my only concern is the 'ti.pin_controller' name may be a little
> bit misleading, but I want to confirm that assumption tonight.
>
> Thanks
>
> Greg

Tested well on zynq.  After looking at gpio-core again, the name
pinctrl makes sense.  Looks good to me :)


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12  3:36     ` Greg Gallagher
@ 2020-08-12 14:55       ` Jan Kiszka
  2020-08-12 15:28         ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-12 14:55 UTC (permalink / raw)
  To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org

On 12.08.20 05:36, Greg Gallagher wrote:
> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher <greg@embeddedgreg.com> wrote:
>>
>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> wrote:
>>>
>>> On 03.08.20 08:04, chensong wrote:
>>>> This a tool to benchmark the latency of GPIO driver,
>>>> it's able to run 2 kinds of benchmark test:
>>>>
>>>> 1, loopback mode
>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>     like gpio-bcm2835 and gpio-core.c, one is as output,
>>>>     the other is as interrupt
>>>> 2) call write_rt to send a pulse from output
>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
>>>> 4) also record timespace in user space(outer_loop)
>>>>     outer_loop is inner_loop plus overhead of event wakeup
>>>> 5) ftrace enable/disable
>>>>
>>>> 2, react mode
>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>     like gpio-bcm2835 and gpio-core.c, one is as ourput,
>>>>     the other is as interrupt
>>>> 2) call read_rt to wait for a pulse from latency box
>>>> 3) call write_rt to send a signal back to latency box
>>>>     as a reaction
>>>> 4) latency box calculates the diff and makes the histogram
>>>>
>>>> e.g.:
>>>> 1) react mode:
>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
>>>> 2) loopback mode:
>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
>>>>
>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
>>>>
>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
>>>> ---
>>>>   configure.ac                     |   1 +
>>>>   doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>>>>   testsuite/Makefile.am            |   3 +-
>>>>   testsuite/gpiobench/Makefile.am  |  18 ++
>>>>   testsuite/gpiobench/gpiobench.c  | 654 +++++++++++++++++++++++++++++++++++++++
>>>>   5 files changed, 745 insertions(+), 1 deletion(-)
>>>>   create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>>>>   create mode 100644 testsuite/gpiobench/Makefile.am
>>>>   create mode 100644 testsuite/gpiobench/gpiobench.c
>>>>
>>>> diff --git a/configure.ac b/configure.ac
>>>> index 29fefab..164c449 100644
>>>> --- a/configure.ac
>>>> +++ b/configure.ac
>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>>>>        testsuite/latency/Makefile \
>>>>        testsuite/switchtest/Makefile \
>>>>        testsuite/gpiotest/Makefile \
>>>> +     testsuite/gpiobench/Makefile \
>>>>        testsuite/spitest/Makefile \
>>>>        testsuite/smokey/Makefile \
>>>>        testsuite/smokey/arith/Makefile \
>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc b/doc/asciidoc/man1/gpiobench.adoc
>>>> new file mode 100644
>>>> index 0000000..bd32ea8
>>>> --- /dev/null
>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
>>>> @@ -0,0 +1,70 @@
>>>> +// ** The above line should force tbl to be a preprocessor **
>>>> +// Man page for gpiobench
>>>> +//
>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
>>>> +//
>>>> +// You may distribute under the terms of the GNU General Public
>>>> +// License as specified in the file COPYING that comes with the
>>>> +// Xenomai distribution.
>>>> +//
>>>> +//
>>>> +GPIOBENCH(1)
>>>> +==========
>>>> +:doctype: manpage
>>>> +:revdate: 2020/08/03
>>>> +:man source: Xenomai
>>>> +:man version: {xenover}
>>>> +:man manual: Xenomai Manual
>>>> +
>>>> +NAME
>>>> +-----
>>>> +gpiobench - Xenomai gpio latency benchmark
>>>> +
>>>> +SYNOPSIS
>>>> +---------
>>>> +// The general command line
>>>> +*gpiobench* [ options ]
>>>> +
>>>> +DESCRIPTION
>>>> +------------
>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
>>>> +benchmark program.  The system must run a suitable Xenomai enabled kernel with
>>>> +the respective module (xeno_timerbench).
>>>> +
>>>> +OPTIONS
>>>> +--------
>>>> +*gpiobench* accepts the following options:
>>>> +
>>>> +*-h <histogram-size>*::
>>>> +default = 100, increase if your last bucket is full
>>>> +
>>>> +*-l <num-of-loops>*::
>>>> +default=1000, number of loops to run the test
>>>> +
>>>> +*-q <quiet>*::
>>>> +print only a summary on exit
>>>> +
>>>> +*-m <test-mode>*::
>>>> +0 = loopback (default), 1 = react
>>>> +
>>>> +*-c <pin-controller>*::
>>>> +name of pin controller
>>
>> Consider changing this to from pin controller to something a little
>> more generic.  On the bcm2835 the gpios are
>>>> +
>>>> +*-o <output-pin>*::
>>>> +number of gpio pin as output
>>>> +
>>>> +*-i <interrupt-pin>*::
>>>> +number of gpin pin as interrupt
>>>> +
>>>> +*-p <priority>*::
>>>> +default = 99, task priority
>>>> +
>>>> +*-b <bracktrace>*::
>>>> +default = 1000, send break trace command when latency > breaktrace
>>>> +
>>>> +
>>>> +
>>>> +AUTHOR
>>>> +-------
>>>> +*gpiobench* was written by song chen. This man page
>>>> +was written by song chen.
>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
>>>> index 76d108e..4932f6d 100644
>>>> --- a/testsuite/Makefile.am
>>>> +++ b/testsuite/Makefile.am
>>>> @@ -1,5 +1,5 @@
>>>>
>>>> -SUBDIRS = latency smokey
>>>> +SUBDIRS = latency smokey gpiobench
>>>>
>>>>   if XENO_COBALT
>>>>   SUBDIRS +=           \
>>>> @@ -13,6 +13,7 @@ endif
>>>>   DIST_SUBDIRS =               \
>>>>        clocktest       \
>>>>        gpiotest        \
>>>> +     gpiobench   \
>>>>        latency         \
>>>>        smokey          \
>>>>        spitest         \
>>>> diff --git a/testsuite/gpiobench/Makefile.am b/testsuite/gpiobench/Makefile.am
>>>> new file mode 100644
>>>> index 0000000..cca3395
>>>> --- /dev/null
>>>> +++ b/testsuite/gpiobench/Makefile.am
>>>> @@ -0,0 +1,18 @@
>>>> +testdir = @XENO_TEST_DIR@
>>>> +
>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
>>>> +
>>>> +test_PROGRAMS = gpiobench
>>>> +
>>>> +gpiobench_SOURCES = gpiobench.c
>>>> +
>>>> +gpiobench_CPPFLAGS =                 \
>>>> +     $(XENO_USER_CFLAGS)     \
>>>> +     -I$(top_srcdir)/include
>>>> +
>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
>>>> +
>>>> +gpiobench_LDADD =                    \
>>>> +     @XENO_CORE_LDADD@       \
>>>> +     @XENO_USER_LDADD@       \
>>>> +     -lpthread -lrt -lm
>>>> diff --git a/testsuite/gpiobench/gpiobench.c b/testsuite/gpiobench/gpiobench.c
>>>> new file mode 100644
>>>> index 0000000..7f19837
>>>> --- /dev/null
>>>> +++ b/testsuite/gpiobench/gpiobench.c
>>>> @@ -0,0 +1,654 @@
>>>> +/*
>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
>>>> + *
>>>> + * Permission is hereby granted, free of charge, to any person obtaining
>>>> + * a copy of this software and associated documentation files (the
>>>> + * "Software"), to deal in the Software without restriction, including
>>>> + * without limitation the rights to use, copy, modify, merge, publish,
>>>> + * distribute, sublicense, and/or sell copies of the Software, and to
>>>> + * permit persons to whom the Software is furnished to do so, subject to
>>>> + * the following conditions:
>>>> + *
>>>> + * The above copyright notice and this permission notice shall be included
>>>> + * in all copies or substantial portions of the Software.
>>>> + *
>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>>>> + */
>>>> +#include <stdlib.h>
>>>> +#include <math.h>
>>>> +#include <stdio.h>
>>>> +#include <string.h>
>>>> +#include <errno.h>
>>>> +#include <error.h>
>>>> +#include <signal.h>
>>>> +#include <sched.h>
>>>> +#include <time.h>
>>>> +#include <sys/time.h>
>>>> +#include <unistd.h>
>>>> +#include <pthread.h>
>>>> +#include <semaphore.h>
>>>> +#include <sys/timerfd.h>
>>>> +#include <xeno_config.h>
>>>> +#include <rtdm/testing.h>
>>>> +#include <rtdm/gpio.h>
>>>> +#include <boilerplate/trace.h>
>>>> +#include <xenomai/init.h>
>>>> +#include <sys/mman.h>
>>>> +#include <getopt.h>
>>>> +
>>>> +#define NS_PER_MS (1000000)
>>>> +#define NS_PER_S (1000000000)
>>>> +
>>>> +#define DEFAULT_PRIO 99
>>>> +#define VERSION_STRING "0.1"
>>>> +#define GPIO_HIGH 1
>>>> +#define GPIO_LOW  0
>>>> +#define MAX_HIST             100
>>>> +#define MAX_CYCLES 1000000
>>>> +#define DEFAULT_LIMIT 1000
>>>> +#define DEV_PATH    "/dev/rtdm/"
>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
>>>> +#define ON  "1"
>>>> +#define OFF "0"
>>>> +
>>>> +enum {
>>>> +     MODE_LOOPBACK,
>>>> +     MODE_REACT,
>>>> +     MODE_ALL
>>>> +};
>>>> +
>>>> +/* Struct for statistics */
>>>> +struct test_stat {
>>>> +     long inner_min;
>>>> +     long inner_max;
>>>> +     double inner_avg;
>>>> +     long *inner_hist_array;
>>>> +     long inner_hist_overflow;
>>>> +     long outer_min;
>>>> +     long outer_max;
>>>> +     double outer_avg;
>>>> +     long *outer_hist_array;
>>>> +     long outer_hist_overflow;
>>>> +};
>>>> +
>>>> +/* Struct for information */
>>>> +struct test_info {
>>>> +     unsigned long max_cycles;
>>>> +     unsigned long total_cycles;
>>>> +     unsigned long max_histogram;
>>>> +     int mode;
>>>> +     int prio;
>>>> +     int quiet;
>>>> +     int tracelimit;
>>>> +     int fd_dev_intr;
>>>> +     int fd_dev_out;
>>>> +     char pin_controller[32];
>>>> +     pthread_t gpio_task;
>>>> +     int gpio_intr;
>>>> +     int gpio_out;
>>>> +     struct test_stat ts;
>>>> +};
>>>> +
>>>> +struct test_info ti;
>>>> +/* Print usage information */
>>>> +static void display_help(void)
>>>> +{
>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
>>>> +     printf("Usage:\n"
>>>> +            "gpiobench <options>\n\n"
>>>> +
>>>> +            "-b       --breaktrace=USEC  send break trace command when latency > USEC\n"
>>>> +            "                            default=1000\n"
>>>> +            "-h       --histogram=US     dump a latency histogram to stdout after the run\n"
>>>> +            "                            US is the max time to be tracked in microseconds,\n"
>>>> +            "                            default=100\n"
>>>> +            "-l       --loops            number of loops, default=1000\n"
>>>> +            "-p       --prio             priority of highest prio thread, defaults=99\n"
>>>> +            "-q       --quiet            print only a summary on exit\n"
>>>> +            "-o       --output           gpio port number for output, no default value,\n"
>>>> +            "                            must be specified\n"
>>>> +            "-i       --intr             gpio port number as an interrupt, no default value,\n"
>>>> +            "                            must be specified\n"
>>>> +            "-c       --pinctrl          gpio pin controller's name, no default value,\n"
>>>> +            "                            must be specified\n"
>>>> +            "-m       --testmode         0 is loopback mode\n"
>>>> +            "                            1 is react mode which works with a latency box,\n"
>>>> +            "                            default=0\n\n"
>>>> +
>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
>>>> +             );
>>>> +}
>>>> +
>>>> +static void process_options(int argc, char *argv[])
>>>> +{
>>>> +     int c = 0;
>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
>>>> +
>>>> +     struct option long_options[] = {
>>>> +             { "bracetrace", required_argument, 0, 'b'},
>>>> +             { "histogram", required_argument, 0, 'h'},
>>>> +             { "loops", required_argument, 0, 'l'},
>>>> +             { "prio", required_argument, 0, 'p'},
>>>> +             { "quiet", no_argument, 0, 'q'},
>>>> +             { "output", required_argument, 0, 'o'},
>>>> +             { "intr", required_argument, 0, 'i'},
>>>> +             { "pinctrl", required_argument, 0, 'c'},
>>>> +             { "testmode", required_argument, 0, 'm'},
>>>> +             { 0, 0, 0, 0},
>>>> +     };
>>>> +
>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
>>>> +                                             NULL)) != -1) {
>>>> +             switch (c) {
>>>> +             case 'h':
>>>> +                     ti.max_histogram = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'p':
>>>> +                     ti.prio = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'l':
>>>> +                     ti.max_cycles = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'q':
>>>> +                     ti.quiet = 1;
>>>> +                     break;
>>>> +
>>>> +             case 'b':
>>>> +                     ti.tracelimit = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'i':
>>>> +                     ti.gpio_intr = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'o':
>>>> +                     ti.gpio_out = atoi(optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'c':
>>>> +                     strcpy(ti.pin_controller, optarg);
>>>> +                     break;
>>>> +
>>>> +             case 'm':
>>>> +                     ti.mode = atoi(optarg) >=
>>>> +                             MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
>>>> +                     break;
>>>> +
>>>> +             default:
>>>> +                     display_help();
>>>> +                     exit(2);
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
>>>> +                             || (strlen(ti.pin_controller) == 0)) {
>>>> +             display_help();
>>>> +             exit(2);
>>>> +     }
>>>> +
>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : ti.max_cycles;
>>>> +
>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
>>>> +             MAX_HIST : ti.max_histogram;
>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
>>>> +}
>>>> +
>>>> +static int thread_msleep(unsigned int ms)
>>>> +{
>>>> +     struct timespec ts = {
>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
>>>> +     };
>>>> +
>>>> +     return -nanosleep(&ts, NULL);
>>>> +}
>>>> +
>>>> +static inline int64_t calc_us(struct timespec t)
>>>> +{
>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
>>>> +}
>>>> +
>>>> +static int setevent(char *event, char *val)
>>>> +{
>>>> +     int fd;
>>>> +     int ret;
>>>> +
>>>> +     fd = open(event, O_WRONLY);
>>>> +     if (fd < 0) {
>>>> +             printf("unable to open %s\n", event);
>>>> +             return -1;
>>>> +     }
>>>> +
>>>> +     ret = write(fd, val, strlen(val));
>>>> +     if (ret < 0) {
>>>> +             printf("unable to write %s to %s\n", val, event);
>>>> +             close(fd);
>>>> +             return -1;
>>>> +     }
>>>> +
>>>> +     close(fd);
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static void tracing(char *enable)
>>>> +{
>>>> +     setevent(TRACING_EVENTS, enable);
>>>> +     setevent(TRACING_ON, enable);
>>>> +}
>>>> +
>>>> +#define write_check(__fd, __buf, __len)                      \
>>>> +     do {                                            \
>>>> +             int __ret = write(__fd, __buf, __len);  \
>>>> +             (void)__ret;                            \
>>>> +     } while (0)
>>>> +
>>>> +#define TRACEBUFSIZ 1024
>>>> +static __thread char tracebuf[TRACEBUFSIZ];
>>>> +
>>>> +static void tracemark(char *fmt, ...) __attribute__((format(printf, 1, 2)));
>>>> +static void tracemark(char *fmt, ...)
>>>> +{
>>>> +     va_list ap;
>>>> +     int len;
>>>> +     int tracemark_fd;
>>>> +
>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
>>>> +     if (tracemark_fd == -1) {
>>>> +             printf("unable to open trace_marker file: %s\n", TRACE_MARKER);
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     /* bail out if we're not tracing */
>>>> +     /* or if the kernel doesn't support trace_mark */
>>>> +     if (tracemark_fd < 0)
>>>> +             return;
>>>> +
>>>> +     va_start(ap, fmt);
>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
>>>> +     va_end(ap);
>>>> +     write_check(tracemark_fd, tracebuf, len);
>>>> +
>>>> +     close(tracemark_fd);
>>>> +}
>>>> +
>>>> +static int rw_gpio(int value, int index)
>>>> +{
>>>> +     int ret;
>>>> +     struct timespec timestamp;
>>>> +     struct rtdm_gpio_readout rdo;
>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
>>>> +
>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>> +     gpio_write = calc_us(timestamp);
>>>> +
>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>> +     if (ret < 0) {
>>>> +             printf("write GPIO, failed\n");
>>>> +             return ret;
>>>> +     }
>>>> +
>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct rtdm_gpio_readout));
>>>> +     if (ret < 0) {
>>>> +             printf("read GPIO, failed\n");
>>>> +             return ret;
>>>> +     }
>>>> +
>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>> +     gpio_read = calc_us(timestamp);
>>>> +
>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
>>>> +
>>>> +     if (inner_diff < ti.ts.inner_min)
>>>> +             ti.ts.inner_min = inner_diff;
>>>> +     if (inner_diff > ti.ts.inner_max)
>>>> +             ti.ts.inner_max = inner_diff;
>>>> +     ti.ts.inner_avg += (double) inner_diff;
>>>> +     if (inner_diff >= ti.max_histogram)
>>>> +             ti.ts.inner_hist_overflow++;
>>>> +     else
>>>> +             ti.ts.inner_hist_array[inner_diff]++;
>>>> +
>>>> +     if (outer_diff < ti.ts.outer_min)
>>>> +             ti.ts.outer_min = outer_diff;
>>>> +     if (inner_diff > ti.ts.outer_max)
>>>> +             ti.ts.outer_max = outer_diff;
>>>> +     ti.ts.outer_avg += (double) outer_diff;
>>>> +     if (outer_diff >= ti.max_histogram)
>>>> +             ti.ts.outer_hist_overflow++;
>>>> +     else
>>>> +             ti.ts.outer_hist_array[outer_diff]++;
>>>> +
>>>> +     if (ti.quiet == 0)
>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff: %8ld\n",
>>>> +                                     index, inner_diff, outer_diff);
>>>> +
>>>> +     return outer_diff;
>>>> +}
>>>> +
>>>> +static void *run_gpiobench_loop(void *cookie)
>>>> +{
>>>> +     int i, ret;
>>>> +
>>>> +     printf("----rt task, gpio loop, test run----\n");
>>>> +
>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>> +             ti.total_cycles = i;
>>>> +             /* send a high level pulse from gpio output pin and
>>>> +              * receive an interrupt from the other gpio pin,
>>>> +              * measuring the time elapsed between the two events
>>>> +              */
>>>> +             ret = rw_gpio(GPIO_HIGH, i);
>>>> +             if (ret < 0) {
>>>> +                     printf("RW GPIO, failed\n");
>>>> +                     break;
>>>> +             } else if (ret > ti.tracelimit) {
>>>> +                     tracemark("hit latency threshold (%d > %d), index: %d",
>>>> +                                             ret, ti.tracelimit, i);
>>>> +                     break;
>>>> +             }
>>>> +
>>>> +             /*take a break, nanosleep here will not jeopardize the latency*/
>>>> +             thread_msleep(10);
>>>> +
>>>> +             ret = rw_gpio(GPIO_LOW, i);
>>>> +             /* send a low level pulse from gpio output pin and
>>>> +              * receive an interrupt from the other gpio pin,
>>>> +              * measuring the time elapsed between the two events
>>>> +              */
>>>> +             if (ret < 0) {
>>>> +                     printf("RW GPIO, failed\n");
>>>> +                     break;
>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
>>>> +                     tracemark("hit latency threshold (%d > %d), index: %d",
>>>> +                                             ret, ti.tracelimit, i);
>>>> +                     break;
>>>> +             }
>>>> +
>>>> +             /*take a break, nanosleep here will not jeopardize the latency*/
>>>> +             thread_msleep(10);
>>>> +     }
>>>> +
>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
>>>> +
>>>> +     return NULL;
>>>> +}
>>>> +
>>>> +static void *run_gpiobench_react(void *cookie)
>>>> +{
>>>> +     int value, ret, i;
>>>> +     struct rtdm_gpio_readout rdo;
>>>> +
>>>> +     printf("----rt task, gpio react, test run----\n");
>>>> +
>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>> +             /* received a pulse from latency box from one of
>>>> +              * the gpio pin pair
>>>> +              */
>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
>>>> +             if (ret < 0) {
>>>> +                     printf("RW GPIO read, failed\n");
>>>> +                     break;
>>>> +             }
>>>> +
>>>> +             if (ti.quiet == 0)
>>>> +                     printf("idx: %d, received signal from latency box\n",
>>>> +                                 i);
>>>> +
>>>> +             /* send a signal back from the other gpio pin
>>>> +              * to latency box as the acknowledge,
>>>> +              * latency box will measure the time elapsed
>>>> +              * between the two events
>>>> +              */
>>>> +             value = GPIO_HIGH;
>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>> +             if (ret < 0) {
>>>> +                     printf("RW GPIO write, failed\n");
>>>> +                     break;
>>>> +             }
>>>> +
>>>> +             if (ti.quiet == 0)
>>>> +                     printf("idx: %d, sent reaction to latency box\n", i);
>>>> +     }
>>>> +
>>>> +     return NULL;
>>>> +}
>>>> +
>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
>>>> +{
>>>> +     struct sched_param p;
>>>> +     int ret;
>>>> +
>>>> +     ret = pthread_attr_init(attr);
>>>> +     if (ret) {
>>>> +             printf("pthread_attr_init(), failed\n");
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     ret = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
>>>> +     if (ret) {
>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     ret = pthread_attr_setschedpolicy(attr,
>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
>>>> +     if (ret) {
>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     p.sched_priority = prio;
>>>> +     ret = pthread_attr_setschedparam(attr, &p);
>>>> +     if (ret) {
>>>> +             printf("pthread_attr_setschedparam(), failed\n");
>>>> +             return;
>>>> +     }
>>>> +}
>>>> +
>>>> +static void init_ti(void)
>>>> +{
>>>> +     memset(&ti, 0, sizeof(struct test_info));
>>>> +     ti.prio = DEFAULT_PRIO;
>>>> +     ti.max_cycles = MAX_CYCLES;
>>>> +     ti.total_cycles = MAX_CYCLES;
>>>> +     ti.max_histogram = MAX_HIST;
>>>> +     ti.tracelimit = DEFAULT_LIMIT;
>>>> +     ti.quiet = 0;
>>>> +     ti.gpio_out = -1;
>>>> +     ti.gpio_intr = -1;
>>>> +     ti.mode = MODE_LOOPBACK;
>>>> +
>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
>>>> +}
>>>> +
>>>> +static void print_hist(void)
>>>> +{
>>>> +     int i;
>>>> +
>>>> +     printf("\n");
>>>> +     printf("# Inner Loop Histogram\n");
>>>> +     printf("# Inner Loop latency is the latency in kernel space\n"
>>>> +                "# between gpio_set_value and irq handler\n");
>>>> +
>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
>>>> +
>>>> +             printf("%06d ", i);
>>>> +             printf("%06lu\n", curr_latency);
>>>> +     }
>>>> +
>>>> +     printf("# Total:");
>>>> +     printf(" %09lu", ti.total_cycles);
>>>> +     printf("\n");
>>>> +
>>>> +     printf("# Min Latencies:");
>>>> +     printf(" %05lu", ti.ts.inner_min);
>>>> +     printf("\n");
>>>> +     printf("# Avg Latencies:");
>>>> +     printf(" %05lf", ti.ts.inner_avg);
>>>> +     printf("\n");
>>>> +     printf("# Max Latencies:");
>>>> +     printf(" %05lu", ti.ts.inner_max);
>>>> +     printf("\n");
>>>> +
>>>> +     printf("\n");
>>>> +     printf("\n");
>>>> +
>>>> +     printf("# Outer Loop Histogram\n");
>>>> +     printf("# Outer Loop latency is the latency in user space\n"
>>>> +                "# between write and read\n"
>>>> +                "# Technically, outer loop latency is inner loop latercy\n"
>>>> +                "# plus overhead of event wakeup\n");
>>>> +
>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
>>>> +
>>>> +             printf("%06d ", i);
>>>> +             printf("%06lu\n", curr_latency);
>>>> +     }
>>>> +
>>>> +     printf("# Total:");
>>>> +     printf(" %09lu", ti.total_cycles);
>>>> +     printf("\n");
>>>> +
>>>> +     printf("# Min Latencies:");
>>>> +     printf(" %05lu", ti.ts.outer_min);
>>>> +     printf("\n");
>>>> +     printf("# Avg Latencies:");
>>>> +     printf(" %05lf", ti.ts.outer_avg);
>>>> +     printf("\n");
>>>> +     printf("# Max Latencies:");
>>>> +     printf(" %05lu", ti.ts.outer_max);
>>>> +     printf("\n");
>>>> +}
>>>> +
>>>> +static void cleanup(void)
>>>> +{
>>>> +     int ret;
>>>> +
>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>> +             tracing(OFF);
>>>> +
>>>> +     ret = close(ti.fd_dev_out);
>>>> +     if (ret < 0)
>>>> +             printf("can't close gpio_out device\n");
>>>> +
>>>> +     ret = close(ti.fd_dev_intr);
>>>> +     if (ret < 0)
>>>> +             printf("can't close gpio_intr device\n");
>>>> +
>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>> +             print_hist();
>>>> +
>>>> +}
>>>> +
>>>> +static void cleanup_and_exit(int sig)
>>>> +{
>>>> +     printf("Signal %d received\n", sig);
>>>> +     cleanup();
>>>> +     exit(0);
>>>> +}
>>>> +
>>>> +int main(int argc, char **argv)
>>>> +{
>>>> +     struct sigaction sa __attribute__((unused));
>>>> +     int ret = 0;
>>>> +     pthread_attr_t tattr;
>>>> +     int trigger, value;
>>>> +     char dev_name[64];
>>>> +
>>>> +     init_ti();
>>>> +
>>>> +     process_options(argc, argv);
>>>> +
>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
>>>> +     if (ret) {
>>>> +             printf("mlockall failed\n");
>>>> +             goto out;
>>>> +     }
>>>> +
>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
>>>> +     if (ti.fd_dev_out < 0) {
>>>> +             printf("can't open %s\n", dev_name);
>>>> +             goto out;
>>>> +     }
>>>> +
>>>> +     if (ti.gpio_out) {
>>>> +             value = 0;
>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
>>>> +             if (ret) {
>>>> +                     printf("ioctl gpio port output, failed\n");
>>>> +                     goto out;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
>>>> +     if (ti.fd_dev_intr < 0) {
>>>> +             printf("can't open %s\n", dev_name);
>>>> +             goto out;
>>>> +     }
>>>> +
>>>> +     if (ti.gpio_intr) {
>>>> +             trigger = GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
>>>> +             value = 1;
>>>> +
>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
>>>> +             if (ret) {
>>>> +                     printf("ioctl gpio port interrupt, failed\n");
>>>> +                     goto out;
>>>> +             }
>>>> +
>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
>>>> +             if (ret) {
>>>> +                     printf("ioctl gpio port ts, failed\n");
>>>> +                     goto out;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>> +             tracing(ON);
>>>> +
>>>> +     signal(SIGTERM, cleanup_and_exit);
>>>> +     signal(SIGINT, cleanup_and_exit);
>>>> +     setup_sched_parameters(&tattr, ti.prio);
>>>> +
>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>> +                                     run_gpiobench_loop, NULL);
>>>> +     else
>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>> +                                     run_gpiobench_react, NULL);
>>>> +
>>>> +     if (ret) {
>>>> +             printf("pthread_create(gpiotask), failed\n");
>>>> +             goto out;
>>>> +     }
>>>> +
>>>> +     pthread_join(ti.gpio_task, NULL);
>>>> +     pthread_attr_destroy(&tattr);
>>>> +
>>>> +out:
>>>> +     cleanup();
>>>> +     return 0;
>>>> +}
>>>>
>>>
>>> Looks good to me. Greg, any comments from your side?
>>>
>>> Jan
>>>
>>> --
>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>> Corporate Competence Center Embedded Linux
>>
>> Hi,
>>    I'll run this on the zynq platform for testing tonight.  It looks
>> good, my only concern is the 'ti.pin_controller' name may be a little
>> bit misleading, but I want to confirm that assumption tonight.
>>
>> Thanks
>>
>> Greg
> 
> Tested well on zynq.  After looking at gpio-core again, the name
> pinctrl makes sense.  Looks good to me :)
> 

Perfect - applied to next.

Thanks,
Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12 14:55       ` Jan Kiszka
@ 2020-08-12 15:28         ` Jan Kiszka
  2020-08-12 15:43           ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-12 15:28 UTC (permalink / raw)
  To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org

On 12.08.20 16:55, Jan Kiszka wrote:
> On 12.08.20 05:36, Greg Gallagher wrote:
>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher 
>> <greg@embeddedgreg.com> wrote:
>>>
>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> 
>>> wrote:
>>>>
>>>> On 03.08.20 08:04, chensong wrote:
>>>>> This a tool to benchmark the latency of GPIO driver,
>>>>> it's able to run 2 kinds of benchmark test:
>>>>>
>>>>> 1, loopback mode
>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>     like gpio-bcm2835 and gpio-core.c, one is as output,
>>>>>     the other is as interrupt
>>>>> 2) call write_rt to send a pulse from output
>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
>>>>> 4) also record timespace in user space(outer_loop)
>>>>>     outer_loop is inner_loop plus overhead of event wakeup
>>>>> 5) ftrace enable/disable
>>>>>
>>>>> 2, react mode
>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>     like gpio-bcm2835 and gpio-core.c, one is as ourput,
>>>>>     the other is as interrupt
>>>>> 2) call read_rt to wait for a pulse from latency box
>>>>> 3) call write_rt to send a signal back to latency box
>>>>>     as a reaction
>>>>> 4) latency box calculates the diff and makes the histogram
>>>>>
>>>>> e.g.:
>>>>> 1) react mode:
>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
>>>>> 2) loopback mode:
>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 -b 50
>>>>>
>>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
>>>>>
>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
>>>>> ---
>>>>>   configure.ac                     |   1 +
>>>>>   doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>>>>>   testsuite/Makefile.am            |   3 +-
>>>>>   testsuite/gpiobench/Makefile.am  |  18 ++
>>>>>   testsuite/gpiobench/gpiobench.c  | 654 
>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>   5 files changed, 745 insertions(+), 1 deletion(-)
>>>>>   create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>>>>>   create mode 100644 testsuite/gpiobench/Makefile.am
>>>>>   create mode 100644 testsuite/gpiobench/gpiobench.c
>>>>>
>>>>> diff --git a/configure.ac b/configure.ac
>>>>> index 29fefab..164c449 100644
>>>>> --- a/configure.ac
>>>>> +++ b/configure.ac
>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>>>>>        testsuite/latency/Makefile \
>>>>>        testsuite/switchtest/Makefile \
>>>>>        testsuite/gpiotest/Makefile \
>>>>> +     testsuite/gpiobench/Makefile \
>>>>>        testsuite/spitest/Makefile \
>>>>>        testsuite/smokey/Makefile \
>>>>>        testsuite/smokey/arith/Makefile \
>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc 
>>>>> b/doc/asciidoc/man1/gpiobench.adoc
>>>>> new file mode 100644
>>>>> index 0000000..bd32ea8
>>>>> --- /dev/null
>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
>>>>> @@ -0,0 +1,70 @@
>>>>> +// ** The above line should force tbl to be a preprocessor **
>>>>> +// Man page for gpiobench
>>>>> +//
>>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
>>>>> +//
>>>>> +// You may distribute under the terms of the GNU General Public
>>>>> +// License as specified in the file COPYING that comes with the
>>>>> +// Xenomai distribution.
>>>>> +//
>>>>> +//
>>>>> +GPIOBENCH(1)
>>>>> +==========
>>>>> +:doctype: manpage
>>>>> +:revdate: 2020/08/03
>>>>> +:man source: Xenomai
>>>>> +:man version: {xenover}
>>>>> +:man manual: Xenomai Manual
>>>>> +
>>>>> +NAME
>>>>> +-----
>>>>> +gpiobench - Xenomai gpio latency benchmark
>>>>> +
>>>>> +SYNOPSIS
>>>>> +---------
>>>>> +// The general command line
>>>>> +*gpiobench* [ options ]
>>>>> +
>>>>> +DESCRIPTION
>>>>> +------------
>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
>>>>> +benchmark program.  The system must run a suitable Xenomai enabled 
>>>>> kernel with
>>>>> +the respective module (xeno_timerbench).
>>>>> +
>>>>> +OPTIONS
>>>>> +--------
>>>>> +*gpiobench* accepts the following options:
>>>>> +
>>>>> +*-h <histogram-size>*::
>>>>> +default = 100, increase if your last bucket is full
>>>>> +
>>>>> +*-l <num-of-loops>*::
>>>>> +default=1000, number of loops to run the test
>>>>> +
>>>>> +*-q <quiet>*::
>>>>> +print only a summary on exit
>>>>> +
>>>>> +*-m <test-mode>*::
>>>>> +0 = loopback (default), 1 = react
>>>>> +
>>>>> +*-c <pin-controller>*::
>>>>> +name of pin controller
>>>
>>> Consider changing this to from pin controller to something a little
>>> more generic.  On the bcm2835 the gpios are
>>>>> +
>>>>> +*-o <output-pin>*::
>>>>> +number of gpio pin as output
>>>>> +
>>>>> +*-i <interrupt-pin>*::
>>>>> +number of gpin pin as interrupt
>>>>> +
>>>>> +*-p <priority>*::
>>>>> +default = 99, task priority
>>>>> +
>>>>> +*-b <bracktrace>*::
>>>>> +default = 1000, send break trace command when latency > breaktrace
>>>>> +
>>>>> +
>>>>> +
>>>>> +AUTHOR
>>>>> +-------
>>>>> +*gpiobench* was written by song chen. This man page
>>>>> +was written by song chen.
>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
>>>>> index 76d108e..4932f6d 100644
>>>>> --- a/testsuite/Makefile.am
>>>>> +++ b/testsuite/Makefile.am
>>>>> @@ -1,5 +1,5 @@
>>>>>
>>>>> -SUBDIRS = latency smokey
>>>>> +SUBDIRS = latency smokey gpiobench
>>>>>
>>>>>   if XENO_COBALT
>>>>>   SUBDIRS +=           \
>>>>> @@ -13,6 +13,7 @@ endif
>>>>>   DIST_SUBDIRS =               \
>>>>>        clocktest       \
>>>>>        gpiotest        \
>>>>> +     gpiobench   \
>>>>>        latency         \
>>>>>        smokey          \
>>>>>        spitest         \
>>>>> diff --git a/testsuite/gpiobench/Makefile.am 
>>>>> b/testsuite/gpiobench/Makefile.am
>>>>> new file mode 100644
>>>>> index 0000000..cca3395
>>>>> --- /dev/null
>>>>> +++ b/testsuite/gpiobench/Makefile.am
>>>>> @@ -0,0 +1,18 @@
>>>>> +testdir = @XENO_TEST_DIR@
>>>>> +
>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
>>>>> +
>>>>> +test_PROGRAMS = gpiobench
>>>>> +
>>>>> +gpiobench_SOURCES = gpiobench.c
>>>>> +
>>>>> +gpiobench_CPPFLAGS =                 \
>>>>> +     $(XENO_USER_CFLAGS)     \
>>>>> +     -I$(top_srcdir)/include
>>>>> +
>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
>>>>> +
>>>>> +gpiobench_LDADD =                    \
>>>>> +     @XENO_CORE_LDADD@       \
>>>>> +     @XENO_USER_LDADD@       \
>>>>> +     -lpthread -lrt -lm
>>>>> diff --git a/testsuite/gpiobench/gpiobench.c 
>>>>> b/testsuite/gpiobench/gpiobench.c
>>>>> new file mode 100644
>>>>> index 0000000..7f19837
>>>>> --- /dev/null
>>>>> +++ b/testsuite/gpiobench/gpiobench.c
>>>>> @@ -0,0 +1,654 @@
>>>>> +/*
>>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
>>>>> + *
>>>>> + * Permission is hereby granted, free of charge, to any person 
>>>>> obtaining
>>>>> + * a copy of this software and associated documentation files (the
>>>>> + * "Software"), to deal in the Software without restriction, 
>>>>> including
>>>>> + * without limitation the rights to use, copy, modify, merge, 
>>>>> publish,
>>>>> + * distribute, sublicense, and/or sell copies of the Software, and to
>>>>> + * permit persons to whom the Software is furnished to do so, 
>>>>> subject to
>>>>> + * the following conditions:
>>>>> + *
>>>>> + * The above copyright notice and this permission notice shall be 
>>>>> included
>>>>> + * in all copies or substantial portions of the Software.
>>>>> + *
>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
>>>>> NONINFRINGEMENT.
>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
>>>>> FOR ANY
>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
>>>>> CONTRACT,
>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>>>>> + */
>>>>> +#include <stdlib.h>
>>>>> +#include <math.h>
>>>>> +#include <stdio.h>
>>>>> +#include <string.h>
>>>>> +#include <errno.h>
>>>>> +#include <error.h>
>>>>> +#include <signal.h>
>>>>> +#include <sched.h>
>>>>> +#include <time.h>
>>>>> +#include <sys/time.h>
>>>>> +#include <unistd.h>
>>>>> +#include <pthread.h>
>>>>> +#include <semaphore.h>
>>>>> +#include <sys/timerfd.h>
>>>>> +#include <xeno_config.h>
>>>>> +#include <rtdm/testing.h>
>>>>> +#include <rtdm/gpio.h>
>>>>> +#include <boilerplate/trace.h>
>>>>> +#include <xenomai/init.h>
>>>>> +#include <sys/mman.h>
>>>>> +#include <getopt.h>
>>>>> +
>>>>> +#define NS_PER_MS (1000000)
>>>>> +#define NS_PER_S (1000000000)
>>>>> +
>>>>> +#define DEFAULT_PRIO 99
>>>>> +#define VERSION_STRING "0.1"
>>>>> +#define GPIO_HIGH 1
>>>>> +#define GPIO_LOW  0
>>>>> +#define MAX_HIST             100
>>>>> +#define MAX_CYCLES 1000000
>>>>> +#define DEFAULT_LIMIT 1000
>>>>> +#define DEV_PATH    "/dev/rtdm/"
>>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
>>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
>>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
>>>>> +#define ON  "1"
>>>>> +#define OFF "0"
>>>>> +
>>>>> +enum {
>>>>> +     MODE_LOOPBACK,
>>>>> +     MODE_REACT,
>>>>> +     MODE_ALL
>>>>> +};
>>>>> +
>>>>> +/* Struct for statistics */
>>>>> +struct test_stat {
>>>>> +     long inner_min;
>>>>> +     long inner_max;
>>>>> +     double inner_avg;
>>>>> +     long *inner_hist_array;
>>>>> +     long inner_hist_overflow;
>>>>> +     long outer_min;
>>>>> +     long outer_max;
>>>>> +     double outer_avg;
>>>>> +     long *outer_hist_array;
>>>>> +     long outer_hist_overflow;
>>>>> +};
>>>>> +
>>>>> +/* Struct for information */
>>>>> +struct test_info {
>>>>> +     unsigned long max_cycles;
>>>>> +     unsigned long total_cycles;
>>>>> +     unsigned long max_histogram;
>>>>> +     int mode;
>>>>> +     int prio;
>>>>> +     int quiet;
>>>>> +     int tracelimit;
>>>>> +     int fd_dev_intr;
>>>>> +     int fd_dev_out;
>>>>> +     char pin_controller[32];
>>>>> +     pthread_t gpio_task;
>>>>> +     int gpio_intr;
>>>>> +     int gpio_out;
>>>>> +     struct test_stat ts;
>>>>> +};
>>>>> +
>>>>> +struct test_info ti;
>>>>> +/* Print usage information */
>>>>> +static void display_help(void)
>>>>> +{
>>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
>>>>> +     printf("Usage:\n"
>>>>> +            "gpiobench <options>\n\n"
>>>>> +
>>>>> +            "-b       --breaktrace=USEC  send break trace command 
>>>>> when latency > USEC\n"
>>>>> +            "                            default=1000\n"
>>>>> +            "-h       --histogram=US     dump a latency histogram 
>>>>> to stdout after the run\n"
>>>>> +            "                            US is the max time to be 
>>>>> tracked in microseconds,\n"
>>>>> +            "                            default=100\n"
>>>>> +            "-l       --loops            number of loops, 
>>>>> default=1000\n"
>>>>> +            "-p       --prio             priority of highest prio 
>>>>> thread, defaults=99\n"
>>>>> +            "-q       --quiet            print only a summary on 
>>>>> exit\n"
>>>>> +            "-o       --output           gpio port number for 
>>>>> output, no default value,\n"
>>>>> +            "                            must be specified\n"
>>>>> +            "-i       --intr             gpio port number as an 
>>>>> interrupt, no default value,\n"
>>>>> +            "                            must be specified\n"
>>>>> +            "-c       --pinctrl          gpio pin controller's 
>>>>> name, no default value,\n"
>>>>> +            "                            must be specified\n"
>>>>> +            "-m       --testmode         0 is loopback mode\n"
>>>>> +            "                            1 is react mode which 
>>>>> works with a latency box,\n"
>>>>> +            "                            default=0\n\n"
>>>>> +
>>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
>>>>> +             );
>>>>> +}
>>>>> +
>>>>> +static void process_options(int argc, char *argv[])
>>>>> +{
>>>>> +     int c = 0;
>>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
>>>>> +
>>>>> +     struct option long_options[] = {
>>>>> +             { "bracetrace", required_argument, 0, 'b'},
>>>>> +             { "histogram", required_argument, 0, 'h'},
>>>>> +             { "loops", required_argument, 0, 'l'},
>>>>> +             { "prio", required_argument, 0, 'p'},
>>>>> +             { "quiet", no_argument, 0, 'q'},
>>>>> +             { "output", required_argument, 0, 'o'},
>>>>> +             { "intr", required_argument, 0, 'i'},
>>>>> +             { "pinctrl", required_argument, 0, 'c'},
>>>>> +             { "testmode", required_argument, 0, 'm'},
>>>>> +             { 0, 0, 0, 0},
>>>>> +     };
>>>>> +
>>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
>>>>> +                                             NULL)) != -1) {
>>>>> +             switch (c) {
>>>>> +             case 'h':
>>>>> +                     ti.max_histogram = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'p':
>>>>> +                     ti.prio = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'l':
>>>>> +                     ti.max_cycles = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'q':
>>>>> +                     ti.quiet = 1;
>>>>> +                     break;
>>>>> +
>>>>> +             case 'b':
>>>>> +                     ti.tracelimit = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'i':
>>>>> +                     ti.gpio_intr = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'o':
>>>>> +                     ti.gpio_out = atoi(optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'c':
>>>>> +                     strcpy(ti.pin_controller, optarg);
>>>>> +                     break;
>>>>> +
>>>>> +             case 'm':
>>>>> +                     ti.mode = atoi(optarg) >=
>>>>> +                             MODE_REACT ? MODE_REACT : MODE_LOOPBACK;
>>>>> +                     break;
>>>>> +
>>>>> +             default:
>>>>> +                     display_help();
>>>>> +                     exit(2);
>>>>> +             }
>>>>> +     }
>>>>> +
>>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
>>>>> +                             || (strlen(ti.pin_controller) == 0)) {
>>>>> +             display_help();
>>>>> +             exit(2);
>>>>> +     }
>>>>> +
>>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
>>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : 
>>>>> ti.max_cycles;
>>>>> +
>>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
>>>>> +             MAX_HIST : ti.max_histogram;
>>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram, sizeof(long));
>>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram, sizeof(long));
>>>>> +}
>>>>> +
>>>>> +static int thread_msleep(unsigned int ms)
>>>>> +{
>>>>> +     struct timespec ts = {
>>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
>>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
>>>>> +     };
>>>>> +
>>>>> +     return -nanosleep(&ts, NULL);
>>>>> +}
>>>>> +
>>>>> +static inline int64_t calc_us(struct timespec t)
>>>>> +{
>>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
>>>>> +}
>>>>> +
>>>>> +static int setevent(char *event, char *val)
>>>>> +{
>>>>> +     int fd;
>>>>> +     int ret;
>>>>> +
>>>>> +     fd = open(event, O_WRONLY);
>>>>> +     if (fd < 0) {
>>>>> +             printf("unable to open %s\n", event);
>>>>> +             return -1;
>>>>> +     }
>>>>> +
>>>>> +     ret = write(fd, val, strlen(val));
>>>>> +     if (ret < 0) {
>>>>> +             printf("unable to write %s to %s\n", val, event);
>>>>> +             close(fd);
>>>>> +             return -1;
>>>>> +     }
>>>>> +
>>>>> +     close(fd);
>>>>> +     return 0;
>>>>> +}
>>>>> +
>>>>> +static void tracing(char *enable)
>>>>> +{
>>>>> +     setevent(TRACING_EVENTS, enable);
>>>>> +     setevent(TRACING_ON, enable);
>>>>> +}
>>>>> +
>>>>> +#define write_check(__fd, __buf, __len)                      \
>>>>> +     do {                                            \
>>>>> +             int __ret = write(__fd, __buf, __len);  \
>>>>> +             (void)__ret;                            \
>>>>> +     } while (0)
>>>>> +
>>>>> +#define TRACEBUFSIZ 1024
>>>>> +static __thread char tracebuf[TRACEBUFSIZ];
>>>>> +
>>>>> +static void tracemark(char *fmt, ...) 
>>>>> __attribute__((format(printf, 1, 2)));
>>>>> +static void tracemark(char *fmt, ...)
>>>>> +{
>>>>> +     va_list ap;
>>>>> +     int len;
>>>>> +     int tracemark_fd;
>>>>> +
>>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
>>>>> +     if (tracemark_fd == -1) {
>>>>> +             printf("unable to open trace_marker file: %s\n", 
>>>>> TRACE_MARKER);
>>>>> +             return;
>>>>> +     }
>>>>> +
>>>>> +     /* bail out if we're not tracing */
>>>>> +     /* or if the kernel doesn't support trace_mark */
>>>>> +     if (tracemark_fd < 0)
>>>>> +             return;
>>>>> +
>>>>> +     va_start(ap, fmt);
>>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
>>>>> +     va_end(ap);
>>>>> +     write_check(tracemark_fd, tracebuf, len);
>>>>> +
>>>>> +     close(tracemark_fd);
>>>>> +}
>>>>> +
>>>>> +static int rw_gpio(int value, int index)
>>>>> +{
>>>>> +     int ret;
>>>>> +     struct timespec timestamp;
>>>>> +     struct rtdm_gpio_readout rdo;
>>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
>>>>> +
>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>> +     gpio_write = calc_us(timestamp);
>>>>> +
>>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>> +     if (ret < 0) {
>>>>> +             printf("write GPIO, failed\n");
>>>>> +             return ret;
>>>>> +     }
>>>>> +
>>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct 
>>>>> rtdm_gpio_readout));
>>>>> +     if (ret < 0) {
>>>>> +             printf("read GPIO, failed\n");
>>>>> +             return ret;
>>>>> +     }
>>>>> +
>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>> +     gpio_read = calc_us(timestamp);
>>>>> +
>>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
>>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
>>>>> +
>>>>> +     if (inner_diff < ti.ts.inner_min)
>>>>> +             ti.ts.inner_min = inner_diff;
>>>>> +     if (inner_diff > ti.ts.inner_max)
>>>>> +             ti.ts.inner_max = inner_diff;
>>>>> +     ti.ts.inner_avg += (double) inner_diff;
>>>>> +     if (inner_diff >= ti.max_histogram)
>>>>> +             ti.ts.inner_hist_overflow++;
>>>>> +     else
>>>>> +             ti.ts.inner_hist_array[inner_diff]++;
>>>>> +
>>>>> +     if (outer_diff < ti.ts.outer_min)
>>>>> +             ti.ts.outer_min = outer_diff;
>>>>> +     if (inner_diff > ti.ts.outer_max)
>>>>> +             ti.ts.outer_max = outer_diff;
>>>>> +     ti.ts.outer_avg += (double) outer_diff;
>>>>> +     if (outer_diff >= ti.max_histogram)
>>>>> +             ti.ts.outer_hist_overflow++;
>>>>> +     else
>>>>> +             ti.ts.outer_hist_array[outer_diff]++;
>>>>> +
>>>>> +     if (ti.quiet == 0)
>>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff: 
>>>>> %8ld\n",
>>>>> +                                     index, inner_diff, outer_diff);
>>>>> +
>>>>> +     return outer_diff;
>>>>> +}
>>>>> +
>>>>> +static void *run_gpiobench_loop(void *cookie)
>>>>> +{
>>>>> +     int i, ret;
>>>>> +
>>>>> +     printf("----rt task, gpio loop, test run----\n");
>>>>> +
>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>> +             ti.total_cycles = i;
>>>>> +             /* send a high level pulse from gpio output pin and
>>>>> +              * receive an interrupt from the other gpio pin,
>>>>> +              * measuring the time elapsed between the two events
>>>>> +              */
>>>>> +             ret = rw_gpio(GPIO_HIGH, i);
>>>>> +             if (ret < 0) {
>>>>> +                     printf("RW GPIO, failed\n");
>>>>> +                     break;
>>>>> +             } else if (ret > ti.tracelimit) {
>>>>> +                     tracemark("hit latency threshold (%d > %d), 
>>>>> index: %d",
>>>>> +                                             ret, ti.tracelimit, i);
>>>>> +                     break;
>>>>> +             }
>>>>> +
>>>>> +             /*take a break, nanosleep here will not jeopardize 
>>>>> the latency*/
>>>>> +             thread_msleep(10);
>>>>> +
>>>>> +             ret = rw_gpio(GPIO_LOW, i);
>>>>> +             /* send a low level pulse from gpio output pin and
>>>>> +              * receive an interrupt from the other gpio pin,
>>>>> +              * measuring the time elapsed between the two events
>>>>> +              */
>>>>> +             if (ret < 0) {
>>>>> +                     printf("RW GPIO, failed\n");
>>>>> +                     break;
>>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
>>>>> +                     tracemark("hit latency threshold (%d > %d), 
>>>>> index: %d",
>>>>> +                                             ret, ti.tracelimit, i);
>>>>> +                     break;
>>>>> +             }
>>>>> +
>>>>> +             /*take a break, nanosleep here will not jeopardize 
>>>>> the latency*/
>>>>> +             thread_msleep(10);
>>>>> +     }
>>>>> +
>>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
>>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
>>>>> +
>>>>> +     return NULL;
>>>>> +}
>>>>> +
>>>>> +static void *run_gpiobench_react(void *cookie)
>>>>> +{
>>>>> +     int value, ret, i;
>>>>> +     struct rtdm_gpio_readout rdo;
>>>>> +
>>>>> +     printf("----rt task, gpio react, test run----\n");
>>>>> +
>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>> +             /* received a pulse from latency box from one of
>>>>> +              * the gpio pin pair
>>>>> +              */
>>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
>>>>> +             if (ret < 0) {
>>>>> +                     printf("RW GPIO read, failed\n");
>>>>> +                     break;
>>>>> +             }
>>>>> +
>>>>> +             if (ti.quiet == 0)
>>>>> +                     printf("idx: %d, received signal from latency 
>>>>> box\n",
>>>>> +                                 i);
>>>>> +
>>>>> +             /* send a signal back from the other gpio pin
>>>>> +              * to latency box as the acknowledge,
>>>>> +              * latency box will measure the time elapsed
>>>>> +              * between the two events
>>>>> +              */
>>>>> +             value = GPIO_HIGH;
>>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>> +             if (ret < 0) {
>>>>> +                     printf("RW GPIO write, failed\n");
>>>>> +                     break;
>>>>> +             }
>>>>> +
>>>>> +             if (ti.quiet == 0)
>>>>> +                     printf("idx: %d, sent reaction to latency 
>>>>> box\n", i);
>>>>> +     }
>>>>> +
>>>>> +     return NULL;
>>>>> +}
>>>>> +
>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
>>>>> +{
>>>>> +     struct sched_param p;
>>>>> +     int ret;
>>>>> +
>>>>> +     ret = pthread_attr_init(attr);
>>>>> +     if (ret) {
>>>>> +             printf("pthread_attr_init(), failed\n");
>>>>> +             return;
>>>>> +     }
>>>>> +
>>>>> +     ret = pthread_attr_setinheritsched(attr, 
>>>>> PTHREAD_EXPLICIT_SCHED);
>>>>> +     if (ret) {
>>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
>>>>> +             return;
>>>>> +     }
>>>>> +
>>>>> +     ret = pthread_attr_setschedpolicy(attr,
>>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
>>>>> +     if (ret) {
>>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
>>>>> +             return;
>>>>> +     }
>>>>> +
>>>>> +     p.sched_priority = prio;
>>>>> +     ret = pthread_attr_setschedparam(attr, &p);
>>>>> +     if (ret) {
>>>>> +             printf("pthread_attr_setschedparam(), failed\n");
>>>>> +             return;
>>>>> +     }
>>>>> +}
>>>>> +
>>>>> +static void init_ti(void)
>>>>> +{
>>>>> +     memset(&ti, 0, sizeof(struct test_info));
>>>>> +     ti.prio = DEFAULT_PRIO;
>>>>> +     ti.max_cycles = MAX_CYCLES;
>>>>> +     ti.total_cycles = MAX_CYCLES;
>>>>> +     ti.max_histogram = MAX_HIST;
>>>>> +     ti.tracelimit = DEFAULT_LIMIT;
>>>>> +     ti.quiet = 0;
>>>>> +     ti.gpio_out = -1;
>>>>> +     ti.gpio_intr = -1;
>>>>> +     ti.mode = MODE_LOOPBACK;
>>>>> +
>>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
>>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
>>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
>>>>> +}
>>>>> +
>>>>> +static void print_hist(void)
>>>>> +{
>>>>> +     int i;
>>>>> +
>>>>> +     printf("\n");
>>>>> +     printf("# Inner Loop Histogram\n");
>>>>> +     printf("# Inner Loop latency is the latency in kernel space\n"
>>>>> +                "# between gpio_set_value and irq handler\n");
>>>>> +
>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
>>>>> +
>>>>> +             printf("%06d ", i);
>>>>> +             printf("%06lu\n", curr_latency);
>>>>> +     }
>>>>> +
>>>>> +     printf("# Total:");
>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>> +     printf("\n");
>>>>> +
>>>>> +     printf("# Min Latencies:");
>>>>> +     printf(" %05lu", ti.ts.inner_min);
>>>>> +     printf("\n");
>>>>> +     printf("# Avg Latencies:");
>>>>> +     printf(" %05lf", ti.ts.inner_avg);
>>>>> +     printf("\n");
>>>>> +     printf("# Max Latencies:");
>>>>> +     printf(" %05lu", ti.ts.inner_max);
>>>>> +     printf("\n");
>>>>> +
>>>>> +     printf("\n");
>>>>> +     printf("\n");
>>>>> +
>>>>> +     printf("# Outer Loop Histogram\n");
>>>>> +     printf("# Outer Loop latency is the latency in user space\n"
>>>>> +                "# between write and read\n"
>>>>> +                "# Technically, outer loop latency is inner loop 
>>>>> latercy\n"
>>>>> +                "# plus overhead of event wakeup\n");
>>>>> +
>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
>>>>> +
>>>>> +             printf("%06d ", i);
>>>>> +             printf("%06lu\n", curr_latency);
>>>>> +     }
>>>>> +
>>>>> +     printf("# Total:");
>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>> +     printf("\n");
>>>>> +
>>>>> +     printf("# Min Latencies:");
>>>>> +     printf(" %05lu", ti.ts.outer_min);
>>>>> +     printf("\n");
>>>>> +     printf("# Avg Latencies:");
>>>>> +     printf(" %05lf", ti.ts.outer_avg);
>>>>> +     printf("\n");
>>>>> +     printf("# Max Latencies:");
>>>>> +     printf(" %05lu", ti.ts.outer_max);
>>>>> +     printf("\n");
>>>>> +}
>>>>> +
>>>>> +static void cleanup(void)
>>>>> +{
>>>>> +     int ret;
>>>>> +
>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>> +             tracing(OFF);
>>>>> +
>>>>> +     ret = close(ti.fd_dev_out);
>>>>> +     if (ret < 0)
>>>>> +             printf("can't close gpio_out device\n");
>>>>> +
>>>>> +     ret = close(ti.fd_dev_intr);
>>>>> +     if (ret < 0)
>>>>> +             printf("can't close gpio_intr device\n");
>>>>> +
>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>> +             print_hist();
>>>>> +
>>>>> +}
>>>>> +
>>>>> +static void cleanup_and_exit(int sig)
>>>>> +{
>>>>> +     printf("Signal %d received\n", sig);
>>>>> +     cleanup();
>>>>> +     exit(0);
>>>>> +}
>>>>> +
>>>>> +int main(int argc, char **argv)
>>>>> +{
>>>>> +     struct sigaction sa __attribute__((unused));
>>>>> +     int ret = 0;
>>>>> +     pthread_attr_t tattr;
>>>>> +     int trigger, value;
>>>>> +     char dev_name[64];
>>>>> +
>>>>> +     init_ti();
>>>>> +
>>>>> +     process_options(argc, argv);
>>>>> +
>>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
>>>>> +     if (ret) {
>>>>> +             printf("mlockall failed\n");
>>>>> +             goto out;
>>>>> +     }
>>>>> +
>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
>>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
>>>>> +     if (ti.fd_dev_out < 0) {
>>>>> +             printf("can't open %s\n", dev_name);
>>>>> +             goto out;
>>>>> +     }
>>>>> +
>>>>> +     if (ti.gpio_out) {
>>>>> +             value = 0;
>>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
>>>>> +             if (ret) {
>>>>> +                     printf("ioctl gpio port output, failed\n");
>>>>> +                     goto out;
>>>>> +             }
>>>>> +     }
>>>>> +
>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
>>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
>>>>> +     if (ti.fd_dev_intr < 0) {
>>>>> +             printf("can't open %s\n", dev_name);
>>>>> +             goto out;
>>>>> +     }
>>>>> +
>>>>> +     if (ti.gpio_intr) {
>>>>> +             trigger = 
>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
>>>>> +             value = 1;
>>>>> +
>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, &trigger);
>>>>> +             if (ret) {
>>>>> +                     printf("ioctl gpio port interrupt, failed\n");
>>>>> +                     goto out;
>>>>> +             }
>>>>> +
>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
>>>>> +             if (ret) {
>>>>> +                     printf("ioctl gpio port ts, failed\n");
>>>>> +                     goto out;
>>>>> +             }
>>>>> +     }
>>>>> +
>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>> +             tracing(ON);
>>>>> +
>>>>> +     signal(SIGTERM, cleanup_and_exit);
>>>>> +     signal(SIGINT, cleanup_and_exit);
>>>>> +     setup_sched_parameters(&tattr, ti.prio);
>>>>> +
>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>> +                                     run_gpiobench_loop, NULL);
>>>>> +     else
>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>> +                                     run_gpiobench_react, NULL);
>>>>> +
>>>>> +     if (ret) {
>>>>> +             printf("pthread_create(gpiotask), failed\n");
>>>>> +             goto out;
>>>>> +     }
>>>>> +
>>>>> +     pthread_join(ti.gpio_task, NULL);
>>>>> +     pthread_attr_destroy(&tattr);
>>>>> +
>>>>> +out:
>>>>> +     cleanup();
>>>>> +     return 0;
>>>>> +}
>>>>>
>>>>
>>>> Looks good to me. Greg, any comments from your side?
>>>>
>>>> Jan
>>>>
>>>> -- 
>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>>> Corporate Competence Center Embedded Linux
>>>
>>> Hi,
>>>    I'll run this on the zynq platform for testing tonight.  It looks
>>> good, my only concern is the 'ti.pin_controller' name may be a little
>>> bit misleading, but I want to confirm that assumption tonight.
>>>
>>> Thanks
>>>
>>> Greg
>>
>> Tested well on zynq.  After looking at gpio-core again, the name
>> pinctrl makes sense.  Looks good to me :)
>>
> 
> Perfect - applied to next.
> 

Had to fix 
https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was 
trivial enough to force-push a new version.

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12 15:28         ` Jan Kiszka
@ 2020-08-12 15:43           ` Jan Kiszka
  2020-08-12 16:10             ` Greg Gallagher
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-12 15:43 UTC (permalink / raw)
  To: Greg Gallagher, chensong; +Cc: Xenomai@xenomai.org

On 12.08.20 17:28, Jan Kiszka wrote:
> On 12.08.20 16:55, Jan Kiszka wrote:
>> On 12.08.20 05:36, Greg Gallagher wrote:
>>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher 
>>> <greg@embeddedgreg.com> wrote:
>>>>
>>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com> 
>>>> wrote:
>>>>>
>>>>> On 03.08.20 08:04, chensong wrote:
>>>>>> This a tool to benchmark the latency of GPIO driver,
>>>>>> it's able to run 2 kinds of benchmark test:
>>>>>>
>>>>>> 1, loopback mode
>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>     like gpio-bcm2835 and gpio-core.c, one is as output,
>>>>>>     the other is as interrupt
>>>>>> 2) call write_rt to send a pulse from output
>>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
>>>>>> 4) also record timespace in user space(outer_loop)
>>>>>>     outer_loop is inner_loop plus overhead of event wakeup
>>>>>> 5) ftrace enable/disable
>>>>>>
>>>>>> 2, react mode
>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>     like gpio-bcm2835 and gpio-core.c, one is as ourput,
>>>>>>     the other is as interrupt
>>>>>> 2) call read_rt to wait for a pulse from latency box
>>>>>> 3) call write_rt to send a signal back to latency box
>>>>>>     as a reaction
>>>>>> 4) latency box calculates the diff and makes the histogram
>>>>>>
>>>>>> e.g.:
>>>>>> 1) react mode:
>>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
>>>>>> 2) loopback mode:
>>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100 
>>>>>> -b 50
>>>>>>
>>>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>>>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
>>>>>>
>>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
>>>>>> ---
>>>>>>   configure.ac                     |   1 +
>>>>>>   doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>>>>>>   testsuite/Makefile.am            |   3 +-
>>>>>>   testsuite/gpiobench/Makefile.am  |  18 ++
>>>>>>   testsuite/gpiobench/gpiobench.c  | 654 
>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>   5 files changed, 745 insertions(+), 1 deletion(-)
>>>>>>   create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>>>>>>   create mode 100644 testsuite/gpiobench/Makefile.am
>>>>>>   create mode 100644 testsuite/gpiobench/gpiobench.c
>>>>>>
>>>>>> diff --git a/configure.ac b/configure.ac
>>>>>> index 29fefab..164c449 100644
>>>>>> --- a/configure.ac
>>>>>> +++ b/configure.ac
>>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>>>>>>        testsuite/latency/Makefile \
>>>>>>        testsuite/switchtest/Makefile \
>>>>>>        testsuite/gpiotest/Makefile \
>>>>>> +     testsuite/gpiobench/Makefile \
>>>>>>        testsuite/spitest/Makefile \
>>>>>>        testsuite/smokey/Makefile \
>>>>>>        testsuite/smokey/arith/Makefile \
>>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc 
>>>>>> b/doc/asciidoc/man1/gpiobench.adoc
>>>>>> new file mode 100644
>>>>>> index 0000000..bd32ea8
>>>>>> --- /dev/null
>>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
>>>>>> @@ -0,0 +1,70 @@
>>>>>> +// ** The above line should force tbl to be a preprocessor **
>>>>>> +// Man page for gpiobench
>>>>>> +//
>>>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
>>>>>> +//
>>>>>> +// You may distribute under the terms of the GNU General Public
>>>>>> +// License as specified in the file COPYING that comes with the
>>>>>> +// Xenomai distribution.
>>>>>> +//
>>>>>> +//
>>>>>> +GPIOBENCH(1)
>>>>>> +==========
>>>>>> +:doctype: manpage
>>>>>> +:revdate: 2020/08/03
>>>>>> +:man source: Xenomai
>>>>>> +:man version: {xenover}
>>>>>> +:man manual: Xenomai Manual
>>>>>> +
>>>>>> +NAME
>>>>>> +-----
>>>>>> +gpiobench - Xenomai gpio latency benchmark
>>>>>> +
>>>>>> +SYNOPSIS
>>>>>> +---------
>>>>>> +// The general command line
>>>>>> +*gpiobench* [ options ]
>>>>>> +
>>>>>> +DESCRIPTION
>>>>>> +------------
>>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
>>>>>> +benchmark program.  The system must run a suitable Xenomai 
>>>>>> enabled kernel with
>>>>>> +the respective module (xeno_timerbench).
>>>>>> +
>>>>>> +OPTIONS
>>>>>> +--------
>>>>>> +*gpiobench* accepts the following options:
>>>>>> +
>>>>>> +*-h <histogram-size>*::
>>>>>> +default = 100, increase if your last bucket is full
>>>>>> +
>>>>>> +*-l <num-of-loops>*::
>>>>>> +default=1000, number of loops to run the test
>>>>>> +
>>>>>> +*-q <quiet>*::
>>>>>> +print only a summary on exit
>>>>>> +
>>>>>> +*-m <test-mode>*::
>>>>>> +0 = loopback (default), 1 = react
>>>>>> +
>>>>>> +*-c <pin-controller>*::
>>>>>> +name of pin controller
>>>>
>>>> Consider changing this to from pin controller to something a little
>>>> more generic.  On the bcm2835 the gpios are
>>>>>> +
>>>>>> +*-o <output-pin>*::
>>>>>> +number of gpio pin as output
>>>>>> +
>>>>>> +*-i <interrupt-pin>*::
>>>>>> +number of gpin pin as interrupt
>>>>>> +
>>>>>> +*-p <priority>*::
>>>>>> +default = 99, task priority
>>>>>> +
>>>>>> +*-b <bracktrace>*::
>>>>>> +default = 1000, send break trace command when latency > breaktrace
>>>>>> +
>>>>>> +
>>>>>> +
>>>>>> +AUTHOR
>>>>>> +-------
>>>>>> +*gpiobench* was written by song chen. This man page
>>>>>> +was written by song chen.
>>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
>>>>>> index 76d108e..4932f6d 100644
>>>>>> --- a/testsuite/Makefile.am
>>>>>> +++ b/testsuite/Makefile.am
>>>>>> @@ -1,5 +1,5 @@
>>>>>>
>>>>>> -SUBDIRS = latency smokey
>>>>>> +SUBDIRS = latency smokey gpiobench
>>>>>>
>>>>>>   if XENO_COBALT
>>>>>>   SUBDIRS +=           \
>>>>>> @@ -13,6 +13,7 @@ endif
>>>>>>   DIST_SUBDIRS =               \
>>>>>>        clocktest       \
>>>>>>        gpiotest        \
>>>>>> +     gpiobench   \
>>>>>>        latency         \
>>>>>>        smokey          \
>>>>>>        spitest         \
>>>>>> diff --git a/testsuite/gpiobench/Makefile.am 
>>>>>> b/testsuite/gpiobench/Makefile.am
>>>>>> new file mode 100644
>>>>>> index 0000000..cca3395
>>>>>> --- /dev/null
>>>>>> +++ b/testsuite/gpiobench/Makefile.am
>>>>>> @@ -0,0 +1,18 @@
>>>>>> +testdir = @XENO_TEST_DIR@
>>>>>> +
>>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
>>>>>> +
>>>>>> +test_PROGRAMS = gpiobench
>>>>>> +
>>>>>> +gpiobench_SOURCES = gpiobench.c
>>>>>> +
>>>>>> +gpiobench_CPPFLAGS =                 \
>>>>>> +     $(XENO_USER_CFLAGS)     \
>>>>>> +     -I$(top_srcdir)/include
>>>>>> +
>>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
>>>>>> +
>>>>>> +gpiobench_LDADD =                    \
>>>>>> +     @XENO_CORE_LDADD@       \
>>>>>> +     @XENO_USER_LDADD@       \
>>>>>> +     -lpthread -lrt -lm
>>>>>> diff --git a/testsuite/gpiobench/gpiobench.c 
>>>>>> b/testsuite/gpiobench/gpiobench.c
>>>>>> new file mode 100644
>>>>>> index 0000000..7f19837
>>>>>> --- /dev/null
>>>>>> +++ b/testsuite/gpiobench/gpiobench.c
>>>>>> @@ -0,0 +1,654 @@
>>>>>> +/*
>>>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
>>>>>> + *
>>>>>> + * Permission is hereby granted, free of charge, to any person 
>>>>>> obtaining
>>>>>> + * a copy of this software and associated documentation files (the
>>>>>> + * "Software"), to deal in the Software without restriction, 
>>>>>> including
>>>>>> + * without limitation the rights to use, copy, modify, merge, 
>>>>>> publish,
>>>>>> + * distribute, sublicense, and/or sell copies of the Software, 
>>>>>> and to
>>>>>> + * permit persons to whom the Software is furnished to do so, 
>>>>>> subject to
>>>>>> + * the following conditions:
>>>>>> + *
>>>>>> + * The above copyright notice and this permission notice shall be 
>>>>>> included
>>>>>> + * in all copies or substantial portions of the Software.
>>>>>> + *
>>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
>>>>>> WARRANTIES OF
>>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
>>>>>> NONINFRINGEMENT.
>>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
>>>>>> FOR ANY
>>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
>>>>>> CONTRACT,
>>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
>>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>>>>>> + */
>>>>>> +#include <stdlib.h>
>>>>>> +#include <math.h>
>>>>>> +#include <stdio.h>
>>>>>> +#include <string.h>
>>>>>> +#include <errno.h>
>>>>>> +#include <error.h>
>>>>>> +#include <signal.h>
>>>>>> +#include <sched.h>
>>>>>> +#include <time.h>
>>>>>> +#include <sys/time.h>
>>>>>> +#include <unistd.h>
>>>>>> +#include <pthread.h>
>>>>>> +#include <semaphore.h>
>>>>>> +#include <sys/timerfd.h>
>>>>>> +#include <xeno_config.h>
>>>>>> +#include <rtdm/testing.h>
>>>>>> +#include <rtdm/gpio.h>
>>>>>> +#include <boilerplate/trace.h>
>>>>>> +#include <xenomai/init.h>
>>>>>> +#include <sys/mman.h>
>>>>>> +#include <getopt.h>
>>>>>> +
>>>>>> +#define NS_PER_MS (1000000)
>>>>>> +#define NS_PER_S (1000000000)
>>>>>> +
>>>>>> +#define DEFAULT_PRIO 99
>>>>>> +#define VERSION_STRING "0.1"
>>>>>> +#define GPIO_HIGH 1
>>>>>> +#define GPIO_LOW  0
>>>>>> +#define MAX_HIST             100
>>>>>> +#define MAX_CYCLES 1000000
>>>>>> +#define DEFAULT_LIMIT 1000
>>>>>> +#define DEV_PATH    "/dev/rtdm/"
>>>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
>>>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
>>>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
>>>>>> +#define ON  "1"
>>>>>> +#define OFF "0"
>>>>>> +
>>>>>> +enum {
>>>>>> +     MODE_LOOPBACK,
>>>>>> +     MODE_REACT,
>>>>>> +     MODE_ALL
>>>>>> +};
>>>>>> +
>>>>>> +/* Struct for statistics */
>>>>>> +struct test_stat {
>>>>>> +     long inner_min;
>>>>>> +     long inner_max;
>>>>>> +     double inner_avg;
>>>>>> +     long *inner_hist_array;
>>>>>> +     long inner_hist_overflow;
>>>>>> +     long outer_min;
>>>>>> +     long outer_max;
>>>>>> +     double outer_avg;
>>>>>> +     long *outer_hist_array;
>>>>>> +     long outer_hist_overflow;
>>>>>> +};
>>>>>> +
>>>>>> +/* Struct for information */
>>>>>> +struct test_info {
>>>>>> +     unsigned long max_cycles;
>>>>>> +     unsigned long total_cycles;
>>>>>> +     unsigned long max_histogram;
>>>>>> +     int mode;
>>>>>> +     int prio;
>>>>>> +     int quiet;
>>>>>> +     int tracelimit;
>>>>>> +     int fd_dev_intr;
>>>>>> +     int fd_dev_out;
>>>>>> +     char pin_controller[32];
>>>>>> +     pthread_t gpio_task;
>>>>>> +     int gpio_intr;
>>>>>> +     int gpio_out;
>>>>>> +     struct test_stat ts;
>>>>>> +};
>>>>>> +
>>>>>> +struct test_info ti;
>>>>>> +/* Print usage information */
>>>>>> +static void display_help(void)
>>>>>> +{
>>>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
>>>>>> +     printf("Usage:\n"
>>>>>> +            "gpiobench <options>\n\n"
>>>>>> +
>>>>>> +            "-b       --breaktrace=USEC  send break trace command 
>>>>>> when latency > USEC\n"
>>>>>> +            "                            default=1000\n"
>>>>>> +            "-h       --histogram=US     dump a latency histogram 
>>>>>> to stdout after the run\n"
>>>>>> +            "                            US is the max time to be 
>>>>>> tracked in microseconds,\n"
>>>>>> +            "                            default=100\n"
>>>>>> +            "-l       --loops            number of loops, 
>>>>>> default=1000\n"
>>>>>> +            "-p       --prio             priority of highest prio 
>>>>>> thread, defaults=99\n"
>>>>>> +            "-q       --quiet            print only a summary on 
>>>>>> exit\n"
>>>>>> +            "-o       --output           gpio port number for 
>>>>>> output, no default value,\n"
>>>>>> +            "                            must be specified\n"
>>>>>> +            "-i       --intr             gpio port number as an 
>>>>>> interrupt, no default value,\n"
>>>>>> +            "                            must be specified\n"
>>>>>> +            "-c       --pinctrl          gpio pin controller's 
>>>>>> name, no default value,\n"
>>>>>> +            "                            must be specified\n"
>>>>>> +            "-m       --testmode         0 is loopback mode\n"
>>>>>> +            "                            1 is react mode which 
>>>>>> works with a latency box,\n"
>>>>>> +            "                            default=0\n\n"
>>>>>> +
>>>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
>>>>>> +             );
>>>>>> +}
>>>>>> +
>>>>>> +static void process_options(int argc, char *argv[])
>>>>>> +{
>>>>>> +     int c = 0;
>>>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
>>>>>> +
>>>>>> +     struct option long_options[] = {
>>>>>> +             { "bracetrace", required_argument, 0, 'b'},
>>>>>> +             { "histogram", required_argument, 0, 'h'},
>>>>>> +             { "loops", required_argument, 0, 'l'},
>>>>>> +             { "prio", required_argument, 0, 'p'},
>>>>>> +             { "quiet", no_argument, 0, 'q'},
>>>>>> +             { "output", required_argument, 0, 'o'},
>>>>>> +             { "intr", required_argument, 0, 'i'},
>>>>>> +             { "pinctrl", required_argument, 0, 'c'},
>>>>>> +             { "testmode", required_argument, 0, 'm'},
>>>>>> +             { 0, 0, 0, 0},
>>>>>> +     };
>>>>>> +
>>>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
>>>>>> +                                             NULL)) != -1) {
>>>>>> +             switch (c) {
>>>>>> +             case 'h':
>>>>>> +                     ti.max_histogram = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'p':
>>>>>> +                     ti.prio = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'l':
>>>>>> +                     ti.max_cycles = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'q':
>>>>>> +                     ti.quiet = 1;
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'b':
>>>>>> +                     ti.tracelimit = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'i':
>>>>>> +                     ti.gpio_intr = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'o':
>>>>>> +                     ti.gpio_out = atoi(optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'c':
>>>>>> +                     strcpy(ti.pin_controller, optarg);
>>>>>> +                     break;
>>>>>> +
>>>>>> +             case 'm':
>>>>>> +                     ti.mode = atoi(optarg) >=
>>>>>> +                             MODE_REACT ? MODE_REACT : 
>>>>>> MODE_LOOPBACK;
>>>>>> +                     break;
>>>>>> +
>>>>>> +             default:
>>>>>> +                     display_help();
>>>>>> +                     exit(2);
>>>>>> +             }
>>>>>> +     }
>>>>>> +
>>>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
>>>>>> +                             || (strlen(ti.pin_controller) == 0)) {
>>>>>> +             display_help();
>>>>>> +             exit(2);
>>>>>> +     }
>>>>>> +
>>>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
>>>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES : 
>>>>>> ti.max_cycles;
>>>>>> +
>>>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
>>>>>> +             MAX_HIST : ti.max_histogram;
>>>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram, 
>>>>>> sizeof(long));
>>>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram, 
>>>>>> sizeof(long));
>>>>>> +}
>>>>>> +
>>>>>> +static int thread_msleep(unsigned int ms)
>>>>>> +{
>>>>>> +     struct timespec ts = {
>>>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
>>>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
>>>>>> +     };
>>>>>> +
>>>>>> +     return -nanosleep(&ts, NULL);
>>>>>> +}
>>>>>> +
>>>>>> +static inline int64_t calc_us(struct timespec t)
>>>>>> +{
>>>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
>>>>>> +}
>>>>>> +
>>>>>> +static int setevent(char *event, char *val)
>>>>>> +{
>>>>>> +     int fd;
>>>>>> +     int ret;
>>>>>> +
>>>>>> +     fd = open(event, O_WRONLY);
>>>>>> +     if (fd < 0) {
>>>>>> +             printf("unable to open %s\n", event);
>>>>>> +             return -1;
>>>>>> +     }
>>>>>> +
>>>>>> +     ret = write(fd, val, strlen(val));
>>>>>> +     if (ret < 0) {
>>>>>> +             printf("unable to write %s to %s\n", val, event);
>>>>>> +             close(fd);
>>>>>> +             return -1;
>>>>>> +     }
>>>>>> +
>>>>>> +     close(fd);
>>>>>> +     return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void tracing(char *enable)
>>>>>> +{
>>>>>> +     setevent(TRACING_EVENTS, enable);
>>>>>> +     setevent(TRACING_ON, enable);
>>>>>> +}
>>>>>> +
>>>>>> +#define write_check(__fd, __buf, __len)                      \
>>>>>> +     do {                                            \
>>>>>> +             int __ret = write(__fd, __buf, __len);  \
>>>>>> +             (void)__ret;                            \
>>>>>> +     } while (0)
>>>>>> +
>>>>>> +#define TRACEBUFSIZ 1024
>>>>>> +static __thread char tracebuf[TRACEBUFSIZ];
>>>>>> +
>>>>>> +static void tracemark(char *fmt, ...) 
>>>>>> __attribute__((format(printf, 1, 2)));
>>>>>> +static void tracemark(char *fmt, ...)
>>>>>> +{
>>>>>> +     va_list ap;
>>>>>> +     int len;
>>>>>> +     int tracemark_fd;
>>>>>> +
>>>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
>>>>>> +     if (tracemark_fd == -1) {
>>>>>> +             printf("unable to open trace_marker file: %s\n", 
>>>>>> TRACE_MARKER);
>>>>>> +             return;
>>>>>> +     }
>>>>>> +
>>>>>> +     /* bail out if we're not tracing */
>>>>>> +     /* or if the kernel doesn't support trace_mark */
>>>>>> +     if (tracemark_fd < 0)
>>>>>> +             return;
>>>>>> +
>>>>>> +     va_start(ap, fmt);
>>>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
>>>>>> +     va_end(ap);
>>>>>> +     write_check(tracemark_fd, tracebuf, len);
>>>>>> +
>>>>>> +     close(tracemark_fd);
>>>>>> +}
>>>>>> +
>>>>>> +static int rw_gpio(int value, int index)
>>>>>> +{
>>>>>> +     int ret;
>>>>>> +     struct timespec timestamp;
>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
>>>>>> +
>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>> +     gpio_write = calc_us(timestamp);
>>>>>> +
>>>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>> +     if (ret < 0) {
>>>>>> +             printf("write GPIO, failed\n");
>>>>>> +             return ret;
>>>>>> +     }
>>>>>> +
>>>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct 
>>>>>> rtdm_gpio_readout));
>>>>>> +     if (ret < 0) {
>>>>>> +             printf("read GPIO, failed\n");
>>>>>> +             return ret;
>>>>>> +     }
>>>>>> +
>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>> +     gpio_read = calc_us(timestamp);
>>>>>> +
>>>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
>>>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
>>>>>> +
>>>>>> +     if (inner_diff < ti.ts.inner_min)
>>>>>> +             ti.ts.inner_min = inner_diff;
>>>>>> +     if (inner_diff > ti.ts.inner_max)
>>>>>> +             ti.ts.inner_max = inner_diff;
>>>>>> +     ti.ts.inner_avg += (double) inner_diff;
>>>>>> +     if (inner_diff >= ti.max_histogram)
>>>>>> +             ti.ts.inner_hist_overflow++;
>>>>>> +     else
>>>>>> +             ti.ts.inner_hist_array[inner_diff]++;
>>>>>> +
>>>>>> +     if (outer_diff < ti.ts.outer_min)
>>>>>> +             ti.ts.outer_min = outer_diff;
>>>>>> +     if (inner_diff > ti.ts.outer_max)
>>>>>> +             ti.ts.outer_max = outer_diff;
>>>>>> +     ti.ts.outer_avg += (double) outer_diff;
>>>>>> +     if (outer_diff >= ti.max_histogram)
>>>>>> +             ti.ts.outer_hist_overflow++;
>>>>>> +     else
>>>>>> +             ti.ts.outer_hist_array[outer_diff]++;
>>>>>> +
>>>>>> +     if (ti.quiet == 0)
>>>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff: 
>>>>>> %8ld\n",
>>>>>> +                                     index, inner_diff, outer_diff);
>>>>>> +
>>>>>> +     return outer_diff;
>>>>>> +}
>>>>>> +
>>>>>> +static void *run_gpiobench_loop(void *cookie)
>>>>>> +{
>>>>>> +     int i, ret;
>>>>>> +
>>>>>> +     printf("----rt task, gpio loop, test run----\n");
>>>>>> +
>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>> +             ti.total_cycles = i;
>>>>>> +             /* send a high level pulse from gpio output pin and
>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>> +              * measuring the time elapsed between the two events
>>>>>> +              */
>>>>>> +             ret = rw_gpio(GPIO_HIGH, i);
>>>>>> +             if (ret < 0) {
>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>> +                     break;
>>>>>> +             } else if (ret > ti.tracelimit) {
>>>>>> +                     tracemark("hit latency threshold (%d > %d), 
>>>>>> index: %d",
>>>>>> +                                             ret, ti.tracelimit, i);
>>>>>> +                     break;
>>>>>> +             }
>>>>>> +
>>>>>> +             /*take a break, nanosleep here will not jeopardize 
>>>>>> the latency*/
>>>>>> +             thread_msleep(10);
>>>>>> +
>>>>>> +             ret = rw_gpio(GPIO_LOW, i);
>>>>>> +             /* send a low level pulse from gpio output pin and
>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>> +              * measuring the time elapsed between the two events
>>>>>> +              */
>>>>>> +             if (ret < 0) {
>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>> +                     break;
>>>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
>>>>>> +                     tracemark("hit latency threshold (%d > %d), 
>>>>>> index: %d",
>>>>>> +                                             ret, ti.tracelimit, i);
>>>>>> +                     break;
>>>>>> +             }
>>>>>> +
>>>>>> +             /*take a break, nanosleep here will not jeopardize 
>>>>>> the latency*/
>>>>>> +             thread_msleep(10);
>>>>>> +     }
>>>>>> +
>>>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
>>>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
>>>>>> +
>>>>>> +     return NULL;
>>>>>> +}
>>>>>> +
>>>>>> +static void *run_gpiobench_react(void *cookie)
>>>>>> +{
>>>>>> +     int value, ret, i;
>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>> +
>>>>>> +     printf("----rt task, gpio react, test run----\n");
>>>>>> +
>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>> +             /* received a pulse from latency box from one of
>>>>>> +              * the gpio pin pair
>>>>>> +              */
>>>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
>>>>>> +             if (ret < 0) {
>>>>>> +                     printf("RW GPIO read, failed\n");
>>>>>> +                     break;
>>>>>> +             }
>>>>>> +
>>>>>> +             if (ti.quiet == 0)
>>>>>> +                     printf("idx: %d, received signal from 
>>>>>> latency box\n",
>>>>>> +                                 i);
>>>>>> +
>>>>>> +             /* send a signal back from the other gpio pin
>>>>>> +              * to latency box as the acknowledge,
>>>>>> +              * latency box will measure the time elapsed
>>>>>> +              * between the two events
>>>>>> +              */
>>>>>> +             value = GPIO_HIGH;
>>>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>> +             if (ret < 0) {
>>>>>> +                     printf("RW GPIO write, failed\n");
>>>>>> +                     break;
>>>>>> +             }
>>>>>> +
>>>>>> +             if (ti.quiet == 0)
>>>>>> +                     printf("idx: %d, sent reaction to latency 
>>>>>> box\n", i);
>>>>>> +     }
>>>>>> +
>>>>>> +     return NULL;
>>>>>> +}
>>>>>> +
>>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
>>>>>> +{
>>>>>> +     struct sched_param p;
>>>>>> +     int ret;
>>>>>> +
>>>>>> +     ret = pthread_attr_init(attr);
>>>>>> +     if (ret) {
>>>>>> +             printf("pthread_attr_init(), failed\n");
>>>>>> +             return;
>>>>>> +     }
>>>>>> +
>>>>>> +     ret = pthread_attr_setinheritsched(attr, 
>>>>>> PTHREAD_EXPLICIT_SCHED);
>>>>>> +     if (ret) {
>>>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
>>>>>> +             return;
>>>>>> +     }
>>>>>> +
>>>>>> +     ret = pthread_attr_setschedpolicy(attr,
>>>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
>>>>>> +     if (ret) {
>>>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
>>>>>> +             return;
>>>>>> +     }
>>>>>> +
>>>>>> +     p.sched_priority = prio;
>>>>>> +     ret = pthread_attr_setschedparam(attr, &p);
>>>>>> +     if (ret) {
>>>>>> +             printf("pthread_attr_setschedparam(), failed\n");
>>>>>> +             return;
>>>>>> +     }
>>>>>> +}
>>>>>> +
>>>>>> +static void init_ti(void)
>>>>>> +{
>>>>>> +     memset(&ti, 0, sizeof(struct test_info));
>>>>>> +     ti.prio = DEFAULT_PRIO;
>>>>>> +     ti.max_cycles = MAX_CYCLES;
>>>>>> +     ti.total_cycles = MAX_CYCLES;
>>>>>> +     ti.max_histogram = MAX_HIST;
>>>>>> +     ti.tracelimit = DEFAULT_LIMIT;
>>>>>> +     ti.quiet = 0;
>>>>>> +     ti.gpio_out = -1;
>>>>>> +     ti.gpio_intr = -1;
>>>>>> +     ti.mode = MODE_LOOPBACK;
>>>>>> +
>>>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
>>>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
>>>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
>>>>>> +}
>>>>>> +
>>>>>> +static void print_hist(void)
>>>>>> +{
>>>>>> +     int i;
>>>>>> +
>>>>>> +     printf("\n");
>>>>>> +     printf("# Inner Loop Histogram\n");
>>>>>> +     printf("# Inner Loop latency is the latency in kernel space\n"
>>>>>> +                "# between gpio_set_value and irq handler\n");
>>>>>> +
>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
>>>>>> +
>>>>>> +             printf("%06d ", i);
>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>> +     }
>>>>>> +
>>>>>> +     printf("# Total:");
>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>> +     printf("\n");
>>>>>> +
>>>>>> +     printf("# Min Latencies:");
>>>>>> +     printf(" %05lu", ti.ts.inner_min);
>>>>>> +     printf("\n");
>>>>>> +     printf("# Avg Latencies:");
>>>>>> +     printf(" %05lf", ti.ts.inner_avg);
>>>>>> +     printf("\n");
>>>>>> +     printf("# Max Latencies:");
>>>>>> +     printf(" %05lu", ti.ts.inner_max);
>>>>>> +     printf("\n");
>>>>>> +
>>>>>> +     printf("\n");
>>>>>> +     printf("\n");
>>>>>> +
>>>>>> +     printf("# Outer Loop Histogram\n");
>>>>>> +     printf("# Outer Loop latency is the latency in user space\n"
>>>>>> +                "# between write and read\n"
>>>>>> +                "# Technically, outer loop latency is inner loop 
>>>>>> latercy\n"
>>>>>> +                "# plus overhead of event wakeup\n");
>>>>>> +
>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
>>>>>> +
>>>>>> +             printf("%06d ", i);
>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>> +     }
>>>>>> +
>>>>>> +     printf("# Total:");
>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>> +     printf("\n");
>>>>>> +
>>>>>> +     printf("# Min Latencies:");
>>>>>> +     printf(" %05lu", ti.ts.outer_min);
>>>>>> +     printf("\n");
>>>>>> +     printf("# Avg Latencies:");
>>>>>> +     printf(" %05lf", ti.ts.outer_avg);
>>>>>> +     printf("\n");
>>>>>> +     printf("# Max Latencies:");
>>>>>> +     printf(" %05lu", ti.ts.outer_max);
>>>>>> +     printf("\n");
>>>>>> +}
>>>>>> +
>>>>>> +static void cleanup(void)
>>>>>> +{
>>>>>> +     int ret;
>>>>>> +
>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>> +             tracing(OFF);
>>>>>> +
>>>>>> +     ret = close(ti.fd_dev_out);
>>>>>> +     if (ret < 0)
>>>>>> +             printf("can't close gpio_out device\n");
>>>>>> +
>>>>>> +     ret = close(ti.fd_dev_intr);
>>>>>> +     if (ret < 0)
>>>>>> +             printf("can't close gpio_intr device\n");
>>>>>> +
>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>> +             print_hist();
>>>>>> +
>>>>>> +}
>>>>>> +
>>>>>> +static void cleanup_and_exit(int sig)
>>>>>> +{
>>>>>> +     printf("Signal %d received\n", sig);
>>>>>> +     cleanup();
>>>>>> +     exit(0);
>>>>>> +}
>>>>>> +
>>>>>> +int main(int argc, char **argv)
>>>>>> +{
>>>>>> +     struct sigaction sa __attribute__((unused));
>>>>>> +     int ret = 0;
>>>>>> +     pthread_attr_t tattr;
>>>>>> +     int trigger, value;
>>>>>> +     char dev_name[64];
>>>>>> +
>>>>>> +     init_ti();
>>>>>> +
>>>>>> +     process_options(argc, argv);
>>>>>> +
>>>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
>>>>>> +     if (ret) {
>>>>>> +             printf("mlockall failed\n");
>>>>>> +             goto out;
>>>>>> +     }
>>>>>> +
>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
>>>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
>>>>>> +     if (ti.fd_dev_out < 0) {
>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>> +             goto out;
>>>>>> +     }
>>>>>> +
>>>>>> +     if (ti.gpio_out) {
>>>>>> +             value = 0;
>>>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
>>>>>> +             if (ret) {
>>>>>> +                     printf("ioctl gpio port output, failed\n");
>>>>>> +                     goto out;
>>>>>> +             }
>>>>>> +     }
>>>>>> +
>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
>>>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
>>>>>> +     if (ti.fd_dev_intr < 0) {
>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>> +             goto out;
>>>>>> +     }
>>>>>> +
>>>>>> +     if (ti.gpio_intr) {
>>>>>> +             trigger = 
>>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
>>>>>> +             value = 1;
>>>>>> +
>>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN, 
>>>>>> &trigger);
>>>>>> +             if (ret) {
>>>>>> +                     printf("ioctl gpio port interrupt, failed\n");
>>>>>> +                     goto out;
>>>>>> +             }
>>>>>> +
>>>>>> +  ��          ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
>>>>>> +             if (ret) {
>>>>>> +                     printf("ioctl gpio port ts, failed\n");
>>>>>> +                     goto out;
>>>>>> +             }
>>>>>> +     }
>>>>>> +
>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>> +             tracing(ON);
>>>>>> +
>>>>>> +     signal(SIGTERM, cleanup_and_exit);
>>>>>> +     signal(SIGINT, cleanup_and_exit);
>>>>>> +     setup_sched_parameters(&tattr, ti.prio);
>>>>>> +
>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>> +                                     run_gpiobench_loop, NULL);
>>>>>> +     else
>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>> +                                     run_gpiobench_react, NULL);
>>>>>> +
>>>>>> +     if (ret) {
>>>>>> +             printf("pthread_create(gpiotask), failed\n");
>>>>>> +             goto out;
>>>>>> +     }
>>>>>> +
>>>>>> +     pthread_join(ti.gpio_task, NULL);
>>>>>> +     pthread_attr_destroy(&tattr);
>>>>>> +
>>>>>> +out:
>>>>>> +     cleanup();
>>>>>> +     return 0;
>>>>>> +}
>>>>>>
>>>>>
>>>>> Looks good to me. Greg, any comments from your side?
>>>>>
>>>>> Jan
>>>>>
>>>>> -- 
>>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>>>> Corporate Competence Center Embedded Linux
>>>>
>>>> Hi,
>>>>    I'll run this on the zynq platform for testing tonight.  It looks
>>>> good, my only concern is the 'ti.pin_controller' name may be a little
>>>> bit misleading, but I want to confirm that assumption tonight.
>>>>
>>>> Thanks
>>>>
>>>> Greg
>>>
>>> Tested well on zynq.  After looking at gpio-core again, the name
>>> pinctrl makes sense.  Looks good to me :)
>>>
>>
>> Perfect - applied to next.
>>
> 
> Had to fix 
> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was 
> trivial enough to force-push a new version.
> 

...but it wasn't trivial. I migrated from uint64_t to long long, please 
have a look at the result in next. Should be tested again on both 64 and 
32-bit targets.

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12 15:43           ` Jan Kiszka
@ 2020-08-12 16:10             ` Greg Gallagher
  2020-08-12 16:14               ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: Greg Gallagher @ 2020-08-12 16:10 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: chensong, Xenomai@xenomai.org

On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com> wrote:
>
> On 12.08.20 17:28, Jan Kiszka wrote:
> > On 12.08.20 16:55, Jan Kiszka wrote:
> >> On 12.08.20 05:36, Greg Gallagher wrote:
> >>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher
> >>> <greg@embeddedgreg.com> wrote:
> >>>>
> >>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com>
> >>>> wrote:
> >>>>>
> >>>>> On 03.08.20 08:04, chensong wrote:
> >>>>>> This a tool to benchmark the latency of GPIO driver,
> >>>>>> it's able to run 2 kinds of benchmark test:
> >>>>>>
> >>>>>> 1, loopback mode
> >>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
> >>>>>>     like gpio-bcm2835 and gpio-core.c, one is as output,
> >>>>>>     the other is as interrupt
> >>>>>> 2) call write_rt to send a pulse from output
> >>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
> >>>>>> 4) also record timespace in user space(outer_loop)
> >>>>>>     outer_loop is inner_loop plus overhead of event wakeup
> >>>>>> 5) ftrace enable/disable
> >>>>>>
> >>>>>> 2, react mode
> >>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
> >>>>>>     like gpio-bcm2835 and gpio-core.c, one is as ourput,
> >>>>>>     the other is as interrupt
> >>>>>> 2) call read_rt to wait for a pulse from latency box
> >>>>>> 3) call write_rt to send a signal back to latency box
> >>>>>>     as a reaction
> >>>>>> 4) latency box calculates the diff and makes the histogram
> >>>>>>
> >>>>>> e.g.:
> >>>>>> 1) react mode:
> >>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
> >>>>>> 2) loopback mode:
> >>>>>>     gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100
> >>>>>> -b 50
> >>>>>>
> >>>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
> >>>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
> >>>>>>
> >>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
> >>>>>> ---
> >>>>>>   configure.ac                     |   1 +
> >>>>>>   doc/asciidoc/man1/gpiobench.adoc |  70 +++++
> >>>>>>   testsuite/Makefile.am            |   3 +-
> >>>>>>   testsuite/gpiobench/Makefile.am  |  18 ++
> >>>>>>   testsuite/gpiobench/gpiobench.c  | 654
> >>>>>> +++++++++++++++++++++++++++++++++++++++
> >>>>>>   5 files changed, 745 insertions(+), 1 deletion(-)
> >>>>>>   create mode 100644 doc/asciidoc/man1/gpiobench.adoc
> >>>>>>   create mode 100644 testsuite/gpiobench/Makefile.am
> >>>>>>   create mode 100644 testsuite/gpiobench/gpiobench.c
> >>>>>>
> >>>>>> diff --git a/configure.ac b/configure.ac
> >>>>>> index 29fefab..164c449 100644
> >>>>>> --- a/configure.ac
> >>>>>> +++ b/configure.ac
> >>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
> >>>>>>        testsuite/latency/Makefile \
> >>>>>>        testsuite/switchtest/Makefile \
> >>>>>>        testsuite/gpiotest/Makefile \
> >>>>>> +     testsuite/gpiobench/Makefile \
> >>>>>>        testsuite/spitest/Makefile \
> >>>>>>        testsuite/smokey/Makefile \
> >>>>>>        testsuite/smokey/arith/Makefile \
> >>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc
> >>>>>> b/doc/asciidoc/man1/gpiobench.adoc
> >>>>>> new file mode 100644
> >>>>>> index 0000000..bd32ea8
> >>>>>> --- /dev/null
> >>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
> >>>>>> @@ -0,0 +1,70 @@
> >>>>>> +// ** The above line should force tbl to be a preprocessor **
> >>>>>> +// Man page for gpiobench
> >>>>>> +//
> >>>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
> >>>>>> +//
> >>>>>> +// You may distribute under the terms of the GNU General Public
> >>>>>> +// License as specified in the file COPYING that comes with the
> >>>>>> +// Xenomai distribution.
> >>>>>> +//
> >>>>>> +//
> >>>>>> +GPIOBENCH(1)
> >>>>>> +==========
> >>>>>> +:doctype: manpage
> >>>>>> +:revdate: 2020/08/03
> >>>>>> +:man source: Xenomai
> >>>>>> +:man version: {xenover}
> >>>>>> +:man manual: Xenomai Manual
> >>>>>> +
> >>>>>> +NAME
> >>>>>> +-----
> >>>>>> +gpiobench - Xenomai gpio latency benchmark
> >>>>>> +
> >>>>>> +SYNOPSIS
> >>>>>> +---------
> >>>>>> +// The general command line
> >>>>>> +*gpiobench* [ options ]
> >>>>>> +
> >>>>>> +DESCRIPTION
> >>>>>> +------------
> >>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
> >>>>>> +benchmark program.  The system must run a suitable Xenomai
> >>>>>> enabled kernel with
> >>>>>> +the respective module (xeno_timerbench).
> >>>>>> +
> >>>>>> +OPTIONS
> >>>>>> +--------
> >>>>>> +*gpiobench* accepts the following options:
> >>>>>> +
> >>>>>> +*-h <histogram-size>*::
> >>>>>> +default = 100, increase if your last bucket is full
> >>>>>> +
> >>>>>> +*-l <num-of-loops>*::
> >>>>>> +default=1000, number of loops to run the test
> >>>>>> +
> >>>>>> +*-q <quiet>*::
> >>>>>> +print only a summary on exit
> >>>>>> +
> >>>>>> +*-m <test-mode>*::
> >>>>>> +0 = loopback (default), 1 = react
> >>>>>> +
> >>>>>> +*-c <pin-controller>*::
> >>>>>> +name of pin controller
> >>>>
> >>>> Consider changing this to from pin controller to something a little
> >>>> more generic.  On the bcm2835 the gpios are
> >>>>>> +
> >>>>>> +*-o <output-pin>*::
> >>>>>> +number of gpio pin as output
> >>>>>> +
> >>>>>> +*-i <interrupt-pin>*::
> >>>>>> +number of gpin pin as interrupt
> >>>>>> +
> >>>>>> +*-p <priority>*::
> >>>>>> +default = 99, task priority
> >>>>>> +
> >>>>>> +*-b <bracktrace>*::
> >>>>>> +default = 1000, send break trace command when latency > breaktrace
> >>>>>> +
> >>>>>> +
> >>>>>> +
> >>>>>> +AUTHOR
> >>>>>> +-------
> >>>>>> +*gpiobench* was written by song chen. This man page
> >>>>>> +was written by song chen.
> >>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
> >>>>>> index 76d108e..4932f6d 100644
> >>>>>> --- a/testsuite/Makefile.am
> >>>>>> +++ b/testsuite/Makefile.am
> >>>>>> @@ -1,5 +1,5 @@
> >>>>>>
> >>>>>> -SUBDIRS = latency smokey
> >>>>>> +SUBDIRS = latency smokey gpiobench
> >>>>>>
> >>>>>>   if XENO_COBALT
> >>>>>>   SUBDIRS +=           \
> >>>>>> @@ -13,6 +13,7 @@ endif
> >>>>>>   DIST_SUBDIRS =               \
> >>>>>>        clocktest       \
> >>>>>>        gpiotest        \
> >>>>>> +     gpiobench   \
> >>>>>>        latency         \
> >>>>>>        smokey          \
> >>>>>>        spitest         \
> >>>>>> diff --git a/testsuite/gpiobench/Makefile.am
> >>>>>> b/testsuite/gpiobench/Makefile.am
> >>>>>> new file mode 100644
> >>>>>> index 0000000..cca3395
> >>>>>> --- /dev/null
> >>>>>> +++ b/testsuite/gpiobench/Makefile.am
> >>>>>> @@ -0,0 +1,18 @@
> >>>>>> +testdir = @XENO_TEST_DIR@
> >>>>>> +
> >>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
> >>>>>> +
> >>>>>> +test_PROGRAMS = gpiobench
> >>>>>> +
> >>>>>> +gpiobench_SOURCES = gpiobench.c
> >>>>>> +
> >>>>>> +gpiobench_CPPFLAGS =                 \
> >>>>>> +     $(XENO_USER_CFLAGS)     \
> >>>>>> +     -I$(top_srcdir)/include
> >>>>>> +
> >>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
> >>>>>> +
> >>>>>> +gpiobench_LDADD =                    \
> >>>>>> +     @XENO_CORE_LDADD@       \
> >>>>>> +     @XENO_USER_LDADD@       \
> >>>>>> +     -lpthread -lrt -lm
> >>>>>> diff --git a/testsuite/gpiobench/gpiobench.c
> >>>>>> b/testsuite/gpiobench/gpiobench.c
> >>>>>> new file mode 100644
> >>>>>> index 0000000..7f19837
> >>>>>> --- /dev/null
> >>>>>> +++ b/testsuite/gpiobench/gpiobench.c
> >>>>>> @@ -0,0 +1,654 @@
> >>>>>> +/*
> >>>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
> >>>>>> + *
> >>>>>> + * Permission is hereby granted, free of charge, to any person
> >>>>>> obtaining
> >>>>>> + * a copy of this software and associated documentation files (the
> >>>>>> + * "Software"), to deal in the Software without restriction,
> >>>>>> including
> >>>>>> + * without limitation the rights to use, copy, modify, merge,
> >>>>>> publish,
> >>>>>> + * distribute, sublicense, and/or sell copies of the Software,
> >>>>>> and to
> >>>>>> + * permit persons to whom the Software is furnished to do so,
> >>>>>> subject to
> >>>>>> + * the following conditions:
> >>>>>> + *
> >>>>>> + * The above copyright notice and this permission notice shall be
> >>>>>> included
> >>>>>> + * in all copies or substantial portions of the Software.
> >>>>>> + *
> >>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> >>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
> >>>>>> WARRANTIES OF
> >>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> >>>>>> NONINFRINGEMENT.
> >>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
> >>>>>> FOR ANY
> >>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
> >>>>>> CONTRACT,
> >>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> >>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> >>>>>> + */
> >>>>>> +#include <stdlib.h>
> >>>>>> +#include <math.h>
> >>>>>> +#include <stdio.h>
> >>>>>> +#include <string.h>
> >>>>>> +#include <errno.h>
> >>>>>> +#include <error.h>
> >>>>>> +#include <signal.h>
> >>>>>> +#include <sched.h>
> >>>>>> +#include <time.h>
> >>>>>> +#include <sys/time.h>
> >>>>>> +#include <unistd.h>
> >>>>>> +#include <pthread.h>
> >>>>>> +#include <semaphore.h>
> >>>>>> +#include <sys/timerfd.h>
> >>>>>> +#include <xeno_config.h>
> >>>>>> +#include <rtdm/testing.h>
> >>>>>> +#include <rtdm/gpio.h>
> >>>>>> +#include <boilerplate/trace.h>
> >>>>>> +#include <xenomai/init.h>
> >>>>>> +#include <sys/mman.h>
> >>>>>> +#include <getopt.h>
> >>>>>> +
> >>>>>> +#define NS_PER_MS (1000000)
> >>>>>> +#define NS_PER_S (1000000000)
> >>>>>> +
> >>>>>> +#define DEFAULT_PRIO 99
> >>>>>> +#define VERSION_STRING "0.1"
> >>>>>> +#define GPIO_HIGH 1
> >>>>>> +#define GPIO_LOW  0
> >>>>>> +#define MAX_HIST             100
> >>>>>> +#define MAX_CYCLES 1000000
> >>>>>> +#define DEFAULT_LIMIT 1000
> >>>>>> +#define DEV_PATH    "/dev/rtdm/"
> >>>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
> >>>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
> >>>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
> >>>>>> +#define ON  "1"
> >>>>>> +#define OFF "0"
> >>>>>> +
> >>>>>> +enum {
> >>>>>> +     MODE_LOOPBACK,
> >>>>>> +     MODE_REACT,
> >>>>>> +     MODE_ALL
> >>>>>> +};
> >>>>>> +
> >>>>>> +/* Struct for statistics */
> >>>>>> +struct test_stat {
> >>>>>> +     long inner_min;
> >>>>>> +     long inner_max;
> >>>>>> +     double inner_avg;
> >>>>>> +     long *inner_hist_array;
> >>>>>> +     long inner_hist_overflow;
> >>>>>> +     long outer_min;
> >>>>>> +     long outer_max;
> >>>>>> +     double outer_avg;
> >>>>>> +     long *outer_hist_array;
> >>>>>> +     long outer_hist_overflow;
> >>>>>> +};
> >>>>>> +
> >>>>>> +/* Struct for information */
> >>>>>> +struct test_info {
> >>>>>> +     unsigned long max_cycles;
> >>>>>> +     unsigned long total_cycles;
> >>>>>> +     unsigned long max_histogram;
> >>>>>> +     int mode;
> >>>>>> +     int prio;
> >>>>>> +     int quiet;
> >>>>>> +     int tracelimit;
> >>>>>> +     int fd_dev_intr;
> >>>>>> +     int fd_dev_out;
> >>>>>> +     char pin_controller[32];
> >>>>>> +     pthread_t gpio_task;
> >>>>>> +     int gpio_intr;
> >>>>>> +     int gpio_out;
> >>>>>> +     struct test_stat ts;
> >>>>>> +};
> >>>>>> +
> >>>>>> +struct test_info ti;
> >>>>>> +/* Print usage information */
> >>>>>> +static void display_help(void)
> >>>>>> +{
> >>>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
> >>>>>> +     printf("Usage:\n"
> >>>>>> +            "gpiobench <options>\n\n"
> >>>>>> +
> >>>>>> +            "-b       --breaktrace=USEC  send break trace command
> >>>>>> when latency > USEC\n"
> >>>>>> +            "                            default=1000\n"
> >>>>>> +            "-h       --histogram=US     dump a latency histogram
> >>>>>> to stdout after the run\n"
> >>>>>> +            "                            US is the max time to be
> >>>>>> tracked in microseconds,\n"
> >>>>>> +            "                            default=100\n"
> >>>>>> +            "-l       --loops            number of loops,
> >>>>>> default=1000\n"
> >>>>>> +            "-p       --prio             priority of highest prio
> >>>>>> thread, defaults=99\n"
> >>>>>> +            "-q       --quiet            print only a summary on
> >>>>>> exit\n"
> >>>>>> +            "-o       --output           gpio port number for
> >>>>>> output, no default value,\n"
> >>>>>> +            "                            must be specified\n"
> >>>>>> +            "-i       --intr             gpio port number as an
> >>>>>> interrupt, no default value,\n"
> >>>>>> +            "                            must be specified\n"
> >>>>>> +            "-c       --pinctrl          gpio pin controller's
> >>>>>> name, no default value,\n"
> >>>>>> +            "                            must be specified\n"
> >>>>>> +            "-m       --testmode         0 is loopback mode\n"
> >>>>>> +            "                            1 is react mode which
> >>>>>> works with a latency box,\n"
> >>>>>> +            "                            default=0\n\n"
> >>>>>> +
> >>>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
> >>>>>> +             );
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void process_options(int argc, char *argv[])
> >>>>>> +{
> >>>>>> +     int c = 0;
> >>>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
> >>>>>> +
> >>>>>> +     struct option long_options[] = {
> >>>>>> +             { "bracetrace", required_argument, 0, 'b'},
> >>>>>> +             { "histogram", required_argument, 0, 'h'},
> >>>>>> +             { "loops", required_argument, 0, 'l'},
> >>>>>> +             { "prio", required_argument, 0, 'p'},
> >>>>>> +             { "quiet", no_argument, 0, 'q'},
> >>>>>> +             { "output", required_argument, 0, 'o'},
> >>>>>> +             { "intr", required_argument, 0, 'i'},
> >>>>>> +             { "pinctrl", required_argument, 0, 'c'},
> >>>>>> +             { "testmode", required_argument, 0, 'm'},
> >>>>>> +             { 0, 0, 0, 0},
> >>>>>> +     };
> >>>>>> +
> >>>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
> >>>>>> +                                             NULL)) != -1) {
> >>>>>> +             switch (c) {
> >>>>>> +             case 'h':
> >>>>>> +                     ti.max_histogram = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'p':
> >>>>>> +                     ti.prio = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'l':
> >>>>>> +                     ti.max_cycles = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'q':
> >>>>>> +                     ti.quiet = 1;
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'b':
> >>>>>> +                     ti.tracelimit = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'i':
> >>>>>> +                     ti.gpio_intr = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'o':
> >>>>>> +                     ti.gpio_out = atoi(optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'c':
> >>>>>> +                     strcpy(ti.pin_controller, optarg);
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             case 'm':
> >>>>>> +                     ti.mode = atoi(optarg) >=
> >>>>>> +                             MODE_REACT ? MODE_REACT :
> >>>>>> MODE_LOOPBACK;
> >>>>>> +                     break;
> >>>>>> +
> >>>>>> +             default:
> >>>>>> +                     display_help();
> >>>>>> +                     exit(2);
> >>>>>> +             }
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
> >>>>>> +                             || (strlen(ti.pin_controller) == 0)) {
> >>>>>> +             display_help();
> >>>>>> +             exit(2);
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
> >>>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES :
> >>>>>> ti.max_cycles;
> >>>>>> +
> >>>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
> >>>>>> +             MAX_HIST : ti.max_histogram;
> >>>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram,
> >>>>>> sizeof(long));
> >>>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram,
> >>>>>> sizeof(long));
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int thread_msleep(unsigned int ms)
> >>>>>> +{
> >>>>>> +     struct timespec ts = {
> >>>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
> >>>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
> >>>>>> +     };
> >>>>>> +
> >>>>>> +     return -nanosleep(&ts, NULL);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static inline int64_t calc_us(struct timespec t)
> >>>>>> +{
> >>>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int setevent(char *event, char *val)
> >>>>>> +{
> >>>>>> +     int fd;
> >>>>>> +     int ret;
> >>>>>> +
> >>>>>> +     fd = open(event, O_WRONLY);
> >>>>>> +     if (fd < 0) {
> >>>>>> +             printf("unable to open %s\n", event);
> >>>>>> +             return -1;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ret = write(fd, val, strlen(val));
> >>>>>> +     if (ret < 0) {
> >>>>>> +             printf("unable to write %s to %s\n", val, event);
> >>>>>> +             close(fd);
> >>>>>> +             return -1;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     close(fd);
> >>>>>> +     return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void tracing(char *enable)
> >>>>>> +{
> >>>>>> +     setevent(TRACING_EVENTS, enable);
> >>>>>> +     setevent(TRACING_ON, enable);
> >>>>>> +}
> >>>>>> +
> >>>>>> +#define write_check(__fd, __buf, __len)                      \
> >>>>>> +     do {                                            \
> >>>>>> +             int __ret = write(__fd, __buf, __len);  \
> >>>>>> +             (void)__ret;                            \
> >>>>>> +     } while (0)
> >>>>>> +
> >>>>>> +#define TRACEBUFSIZ 1024
> >>>>>> +static __thread char tracebuf[TRACEBUFSIZ];
> >>>>>> +
> >>>>>> +static void tracemark(char *fmt, ...)
> >>>>>> __attribute__((format(printf, 1, 2)));
> >>>>>> +static void tracemark(char *fmt, ...)
> >>>>>> +{
> >>>>>> +     va_list ap;
> >>>>>> +     int len;
> >>>>>> +     int tracemark_fd;
> >>>>>> +
> >>>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
> >>>>>> +     if (tracemark_fd == -1) {
> >>>>>> +             printf("unable to open trace_marker file: %s\n",
> >>>>>> TRACE_MARKER);
> >>>>>> +             return;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     /* bail out if we're not tracing */
> >>>>>> +     /* or if the kernel doesn't support trace_mark */
> >>>>>> +     if (tracemark_fd < 0)
> >>>>>> +             return;
> >>>>>> +
> >>>>>> +     va_start(ap, fmt);
> >>>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
> >>>>>> +     va_end(ap);
> >>>>>> +     write_check(tracemark_fd, tracebuf, len);
> >>>>>> +
> >>>>>> +     close(tracemark_fd);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int rw_gpio(int value, int index)
> >>>>>> +{
> >>>>>> +     int ret;
> >>>>>> +     struct timespec timestamp;
> >>>>>> +     struct rtdm_gpio_readout rdo;
> >>>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
> >>>>>> +
> >>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> >>>>>> +     gpio_write = calc_us(timestamp);
> >>>>>> +
> >>>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
> >>>>>> +     if (ret < 0) {
> >>>>>> +             printf("write GPIO, failed\n");
> >>>>>> +             return ret;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct
> >>>>>> rtdm_gpio_readout));
> >>>>>> +     if (ret < 0) {
> >>>>>> +             printf("read GPIO, failed\n");
> >>>>>> +             return ret;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
> >>>>>> +     gpio_read = calc_us(timestamp);
> >>>>>> +
> >>>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
> >>>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
> >>>>>> +
> >>>>>> +     if (inner_diff < ti.ts.inner_min)
> >>>>>> +             ti.ts.inner_min = inner_diff;
> >>>>>> +     if (inner_diff > ti.ts.inner_max)
> >>>>>> +             ti.ts.inner_max = inner_diff;
> >>>>>> +     ti.ts.inner_avg += (double) inner_diff;
> >>>>>> +     if (inner_diff >= ti.max_histogram)
> >>>>>> +             ti.ts.inner_hist_overflow++;
> >>>>>> +     else
> >>>>>> +             ti.ts.inner_hist_array[inner_diff]++;
> >>>>>> +
> >>>>>> +     if (outer_diff < ti.ts.outer_min)
> >>>>>> +             ti.ts.outer_min = outer_diff;
> >>>>>> +     if (inner_diff > ti.ts.outer_max)
> >>>>>> +             ti.ts.outer_max = outer_diff;
> >>>>>> +     ti.ts.outer_avg += (double) outer_diff;
> >>>>>> +     if (outer_diff >= ti.max_histogram)
> >>>>>> +             ti.ts.outer_hist_overflow++;
> >>>>>> +     else
> >>>>>> +             ti.ts.outer_hist_array[outer_diff]++;
> >>>>>> +
> >>>>>> +     if (ti.quiet == 0)
> >>>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff:
> >>>>>> %8ld\n",
> >>>>>> +                                     index, inner_diff, outer_diff);
> >>>>>> +
> >>>>>> +     return outer_diff;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void *run_gpiobench_loop(void *cookie)
> >>>>>> +{
> >>>>>> +     int i, ret;
> >>>>>> +
> >>>>>> +     printf("----rt task, gpio loop, test run----\n");
> >>>>>> +
> >>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
> >>>>>> +             ti.total_cycles = i;
> >>>>>> +             /* send a high level pulse from gpio output pin and
> >>>>>> +              * receive an interrupt from the other gpio pin,
> >>>>>> +              * measuring the time elapsed between the two events
> >>>>>> +              */
> >>>>>> +             ret = rw_gpio(GPIO_HIGH, i);
> >>>>>> +             if (ret < 0) {
> >>>>>> +                     printf("RW GPIO, failed\n");
> >>>>>> +                     break;
> >>>>>> +             } else if (ret > ti.tracelimit) {
> >>>>>> +                     tracemark("hit latency threshold (%d > %d),
> >>>>>> index: %d",
> >>>>>> +                                             ret, ti.tracelimit, i);
> >>>>>> +                     break;
> >>>>>> +             }
> >>>>>> +
> >>>>>> +             /*take a break, nanosleep here will not jeopardize
> >>>>>> the latency*/
> >>>>>> +             thread_msleep(10);
> >>>>>> +
> >>>>>> +             ret = rw_gpio(GPIO_LOW, i);
> >>>>>> +             /* send a low level pulse from gpio output pin and
> >>>>>> +              * receive an interrupt from the other gpio pin,
> >>>>>> +              * measuring the time elapsed between the two events
> >>>>>> +              */
> >>>>>> +             if (ret < 0) {
> >>>>>> +                     printf("RW GPIO, failed\n");
> >>>>>> +                     break;
> >>>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
> >>>>>> +                     tracemark("hit latency threshold (%d > %d),
> >>>>>> index: %d",
> >>>>>> +                                             ret, ti.tracelimit, i);
> >>>>>> +                     break;
> >>>>>> +             }
> >>>>>> +
> >>>>>> +             /*take a break, nanosleep here will not jeopardize
> >>>>>> the latency*/
> >>>>>> +             thread_msleep(10);
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
> >>>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
> >>>>>> +
> >>>>>> +     return NULL;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void *run_gpiobench_react(void *cookie)
> >>>>>> +{
> >>>>>> +     int value, ret, i;
> >>>>>> +     struct rtdm_gpio_readout rdo;
> >>>>>> +
> >>>>>> +     printf("----rt task, gpio react, test run----\n");
> >>>>>> +
> >>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
> >>>>>> +             /* received a pulse from latency box from one of
> >>>>>> +              * the gpio pin pair
> >>>>>> +              */
> >>>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
> >>>>>> +             if (ret < 0) {
> >>>>>> +                     printf("RW GPIO read, failed\n");
> >>>>>> +                     break;
> >>>>>> +             }
> >>>>>> +
> >>>>>> +             if (ti.quiet == 0)
> >>>>>> +                     printf("idx: %d, received signal from
> >>>>>> latency box\n",
> >>>>>> +                                 i);
> >>>>>> +
> >>>>>> +             /* send a signal back from the other gpio pin
> >>>>>> +              * to latency box as the acknowledge,
> >>>>>> +              * latency box will measure the time elapsed
> >>>>>> +              * between the two events
> >>>>>> +              */
> >>>>>> +             value = GPIO_HIGH;
> >>>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
> >>>>>> +             if (ret < 0) {
> >>>>>> +                     printf("RW GPIO write, failed\n");
> >>>>>> +                     break;
> >>>>>> +             }
> >>>>>> +
> >>>>>> +             if (ti.quiet == 0)
> >>>>>> +                     printf("idx: %d, sent reaction to latency
> >>>>>> box\n", i);
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     return NULL;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
> >>>>>> +{
> >>>>>> +     struct sched_param p;
> >>>>>> +     int ret;
> >>>>>> +
> >>>>>> +     ret = pthread_attr_init(attr);
> >>>>>> +     if (ret) {
> >>>>>> +             printf("pthread_attr_init(), failed\n");
> >>>>>> +             return;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ret = pthread_attr_setinheritsched(attr,
> >>>>>> PTHREAD_EXPLICIT_SCHED);
> >>>>>> +     if (ret) {
> >>>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
> >>>>>> +             return;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     ret = pthread_attr_setschedpolicy(attr,
> >>>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
> >>>>>> +     if (ret) {
> >>>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
> >>>>>> +             return;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     p.sched_priority = prio;
> >>>>>> +     ret = pthread_attr_setschedparam(attr, &p);
> >>>>>> +     if (ret) {
> >>>>>> +             printf("pthread_attr_setschedparam(), failed\n");
> >>>>>> +             return;
> >>>>>> +     }
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void init_ti(void)
> >>>>>> +{
> >>>>>> +     memset(&ti, 0, sizeof(struct test_info));
> >>>>>> +     ti.prio = DEFAULT_PRIO;
> >>>>>> +     ti.max_cycles = MAX_CYCLES;
> >>>>>> +     ti.total_cycles = MAX_CYCLES;
> >>>>>> +     ti.max_histogram = MAX_HIST;
> >>>>>> +     ti.tracelimit = DEFAULT_LIMIT;
> >>>>>> +     ti.quiet = 0;
> >>>>>> +     ti.gpio_out = -1;
> >>>>>> +     ti.gpio_intr = -1;
> >>>>>> +     ti.mode = MODE_LOOPBACK;
> >>>>>> +
> >>>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
> >>>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
> >>>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void print_hist(void)
> >>>>>> +{
> >>>>>> +     int i;
> >>>>>> +
> >>>>>> +     printf("\n");
> >>>>>> +     printf("# Inner Loop Histogram\n");
> >>>>>> +     printf("# Inner Loop latency is the latency in kernel space\n"
> >>>>>> +                "# between gpio_set_value and irq handler\n");
> >>>>>> +
> >>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
> >>>>>> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
> >>>>>> +
> >>>>>> +             printf("%06d ", i);
> >>>>>> +             printf("%06lu\n", curr_latency);
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     printf("# Total:");
> >>>>>> +     printf(" %09lu", ti.total_cycles);
> >>>>>> +     printf("\n");
> >>>>>> +
> >>>>>> +     printf("# Min Latencies:");
> >>>>>> +     printf(" %05lu", ti.ts.inner_min);
> >>>>>> +     printf("\n");
> >>>>>> +     printf("# Avg Latencies:");
> >>>>>> +     printf(" %05lf", ti.ts.inner_avg);
> >>>>>> +     printf("\n");
> >>>>>> +     printf("# Max Latencies:");
> >>>>>> +     printf(" %05lu", ti.ts.inner_max);
> >>>>>> +     printf("\n");
> >>>>>> +
> >>>>>> +     printf("\n");
> >>>>>> +     printf("\n");
> >>>>>> +
> >>>>>> +     printf("# Outer Loop Histogram\n");
> >>>>>> +     printf("# Outer Loop latency is the latency in user space\n"
> >>>>>> +                "# between write and read\n"
> >>>>>> +                "# Technically, outer loop latency is inner loop
> >>>>>> latercy\n"
> >>>>>> +                "# plus overhead of event wakeup\n");
> >>>>>> +
> >>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
> >>>>>> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
> >>>>>> +
> >>>>>> +             printf("%06d ", i);
> >>>>>> +             printf("%06lu\n", curr_latency);
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     printf("# Total:");
> >>>>>> +     printf(" %09lu", ti.total_cycles);
> >>>>>> +     printf("\n");
> >>>>>> +
> >>>>>> +     printf("# Min Latencies:");
> >>>>>> +     printf(" %05lu", ti.ts.outer_min);
> >>>>>> +     printf("\n");
> >>>>>> +     printf("# Avg Latencies:");
> >>>>>> +     printf(" %05lf", ti.ts.outer_avg);
> >>>>>> +     printf("\n");
> >>>>>> +     printf("# Max Latencies:");
> >>>>>> +     printf(" %05lu", ti.ts.outer_max);
> >>>>>> +     printf("\n");
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void cleanup(void)
> >>>>>> +{
> >>>>>> +     int ret;
> >>>>>> +
> >>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
> >>>>>> +             tracing(OFF);
> >>>>>> +
> >>>>>> +     ret = close(ti.fd_dev_out);
> >>>>>> +     if (ret < 0)
> >>>>>> +             printf("can't close gpio_out device\n");
> >>>>>> +
> >>>>>> +     ret = close(ti.fd_dev_intr);
> >>>>>> +     if (ret < 0)
> >>>>>> +             printf("can't close gpio_intr device\n");
> >>>>>> +
> >>>>>> +     if (ti.mode == MODE_LOOPBACK)
> >>>>>> +             print_hist();
> >>>>>> +
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void cleanup_and_exit(int sig)
> >>>>>> +{
> >>>>>> +     printf("Signal %d received\n", sig);
> >>>>>> +     cleanup();
> >>>>>> +     exit(0);
> >>>>>> +}
> >>>>>> +
> >>>>>> +int main(int argc, char **argv)
> >>>>>> +{
> >>>>>> +     struct sigaction sa __attribute__((unused));
> >>>>>> +     int ret = 0;
> >>>>>> +     pthread_attr_t tattr;
> >>>>>> +     int trigger, value;
> >>>>>> +     char dev_name[64];
> >>>>>> +
> >>>>>> +     init_ti();
> >>>>>> +
> >>>>>> +     process_options(argc, argv);
> >>>>>> +
> >>>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
> >>>>>> +     if (ret) {
> >>>>>> +             printf("mlockall failed\n");
> >>>>>> +             goto out;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
> >>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
> >>>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
> >>>>>> +     if (ti.fd_dev_out < 0) {
> >>>>>> +             printf("can't open %s\n", dev_name);
> >>>>>> +             goto out;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     if (ti.gpio_out) {
> >>>>>> +             value = 0;
> >>>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
> >>>>>> +             if (ret) {
> >>>>>> +                     printf("ioctl gpio port output, failed\n");
> >>>>>> +                     goto out;
> >>>>>> +             }
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
> >>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
> >>>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
> >>>>>> +     if (ti.fd_dev_intr < 0) {
> >>>>>> +             printf("can't open %s\n", dev_name);
> >>>>>> +             goto out;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     if (ti.gpio_intr) {
> >>>>>> +             trigger =
> >>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
> >>>>>> +             value = 1;
> >>>>>> +
> >>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN,
> >>>>>> &trigger);
> >>>>>> +             if (ret) {
> >>>>>> +                     printf("ioctl gpio port interrupt, failed\n");
> >>>>>> +                     goto out;
> >>>>>> +             }
> >>>>>> +
> >>>>>> +  ��          ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
> >>>>>> +             if (ret) {
> >>>>>> +                     printf("ioctl gpio port ts, failed\n");
> >>>>>> +                     goto out;
> >>>>>> +             }
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
> >>>>>> +             tracing(ON);
> >>>>>> +
> >>>>>> +     signal(SIGTERM, cleanup_and_exit);
> >>>>>> +     signal(SIGINT, cleanup_and_exit);
> >>>>>> +     setup_sched_parameters(&tattr, ti.prio);
> >>>>>> +
> >>>>>> +     if (ti.mode == MODE_LOOPBACK)
> >>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
> >>>>>> +                                     run_gpiobench_loop, NULL);
> >>>>>> +     else
> >>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
> >>>>>> +                                     run_gpiobench_react, NULL);
> >>>>>> +
> >>>>>> +     if (ret) {
> >>>>>> +             printf("pthread_create(gpiotask), failed\n");
> >>>>>> +             goto out;
> >>>>>> +     }
> >>>>>> +
> >>>>>> +     pthread_join(ti.gpio_task, NULL);
> >>>>>> +     pthread_attr_destroy(&tattr);
> >>>>>> +
> >>>>>> +out:
> >>>>>> +     cleanup();
> >>>>>> +     return 0;
> >>>>>> +}
> >>>>>>
> >>>>>
> >>>>> Looks good to me. Greg, any comments from your side?
> >>>>>
> >>>>> Jan
> >>>>>
> >>>>> --
> >>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
> >>>>> Corporate Competence Center Embedded Linux
> >>>>
> >>>> Hi,
> >>>>    I'll run this on the zynq platform for testing tonight.  It looks
> >>>> good, my only concern is the 'ti.pin_controller' name may be a little
> >>>> bit misleading, but I want to confirm that assumption tonight.
> >>>>
> >>>> Thanks
> >>>>
> >>>> Greg
> >>>
> >>> Tested well on zynq.  After looking at gpio-core again, the name
> >>> pinctrl makes sense.  Looks good to me :)
> >>>
> >>
> >> Perfect - applied to next.
> >>
> >
> > Had to fix
> > https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was
> > trivial enough to force-push a new version.
> >
>
> ...but it wasn't trivial. I migrated from uint64_t to long long, please
> have a look at the result in next. Should be tested again on both 64 and
> 32-bit targets.
>
> Jan
>
> --
> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
> Corporate Competence Center Embedded Linux

Don't know why i didn't hit it in my testing, I can run it again on
ARM64/ARM platforms. I need to do an ipipe release on both soon
anyway.

Thanks

Greg


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12 16:10             ` Greg Gallagher
@ 2020-08-12 16:14               ` Jan Kiszka
  2020-08-13  1:57                 ` chensong
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-12 16:14 UTC (permalink / raw)
  To: Greg Gallagher; +Cc: chensong, Xenomai@xenomai.org

On 12.08.20 18:10, Greg Gallagher wrote:
> On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com> wrote:
>>
>> On 12.08.20 17:28, Jan Kiszka wrote:
>>> On 12.08.20 16:55, Jan Kiszka wrote:
>>>> On 12.08.20 05:36, Greg Gallagher wrote:
>>>>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher
>>>>> <greg@embeddedgreg.com> wrote:
>>>>>>
>>>>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com>
>>>>>> wrote:
>>>>>>>
>>>>>>> On 03.08.20 08:04, chensong wrote:
>>>>>>>> This a tool to benchmark the latency of GPIO driver,
>>>>>>>> it's able to run 2 kinds of benchmark test:
>>>>>>>>
>>>>>>>> 1, loopback mode
>>>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>>>      like gpio-bcm2835 and gpio-core.c, one is as output,
>>>>>>>>      the other is as interrupt
>>>>>>>> 2) call write_rt to send a pulse from output
>>>>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
>>>>>>>> 4) also record timespace in user space(outer_loop)
>>>>>>>>      outer_loop is inner_loop plus overhead of event wakeup
>>>>>>>> 5) ftrace enable/disable
>>>>>>>>
>>>>>>>> 2, react mode
>>>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>>>      like gpio-bcm2835 and gpio-core.c, one is as ourput,
>>>>>>>>      the other is as interrupt
>>>>>>>> 2) call read_rt to wait for a pulse from latency box
>>>>>>>> 3) call write_rt to send a signal back to latency box
>>>>>>>>      as a reaction
>>>>>>>> 4) latency box calculates the diff and makes the histogram
>>>>>>>>
>>>>>>>> e.g.:
>>>>>>>> 1) react mode:
>>>>>>>>      gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
>>>>>>>> 2) loopback mode:
>>>>>>>>      gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100
>>>>>>>> -b 50
>>>>>>>>
>>>>>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>>>>>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
>>>>>>>>
>>>>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
>>>>>>>> ---
>>>>>>>>    configure.ac                     |   1 +
>>>>>>>>    doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>>>>>>>>    testsuite/Makefile.am            |   3 +-
>>>>>>>>    testsuite/gpiobench/Makefile.am  |  18 ++
>>>>>>>>    testsuite/gpiobench/gpiobench.c  | 654
>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>    5 files changed, 745 insertions(+), 1 deletion(-)
>>>>>>>>    create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>>>>>>>>    create mode 100644 testsuite/gpiobench/Makefile.am
>>>>>>>>    create mode 100644 testsuite/gpiobench/gpiobench.c
>>>>>>>>
>>>>>>>> diff --git a/configure.ac b/configure.ac
>>>>>>>> index 29fefab..164c449 100644
>>>>>>>> --- a/configure.ac
>>>>>>>> +++ b/configure.ac
>>>>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>>>>>>>>         testsuite/latency/Makefile \
>>>>>>>>         testsuite/switchtest/Makefile \
>>>>>>>>         testsuite/gpiotest/Makefile \
>>>>>>>> +     testsuite/gpiobench/Makefile \
>>>>>>>>         testsuite/spitest/Makefile \
>>>>>>>>         testsuite/smokey/Makefile \
>>>>>>>>         testsuite/smokey/arith/Makefile \
>>>>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>> b/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..bd32ea8
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>> @@ -0,0 +1,70 @@
>>>>>>>> +// ** The above line should force tbl to be a preprocessor **
>>>>>>>> +// Man page for gpiobench
>>>>>>>> +//
>>>>>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
>>>>>>>> +//
>>>>>>>> +// You may distribute under the terms of the GNU General Public
>>>>>>>> +// License as specified in the file COPYING that comes with the
>>>>>>>> +// Xenomai distribution.
>>>>>>>> +//
>>>>>>>> +//
>>>>>>>> +GPIOBENCH(1)
>>>>>>>> +==========
>>>>>>>> +:doctype: manpage
>>>>>>>> +:revdate: 2020/08/03
>>>>>>>> +:man source: Xenomai
>>>>>>>> +:man version: {xenover}
>>>>>>>> +:man manual: Xenomai Manual
>>>>>>>> +
>>>>>>>> +NAME
>>>>>>>> +-----
>>>>>>>> +gpiobench - Xenomai gpio latency benchmark
>>>>>>>> +
>>>>>>>> +SYNOPSIS
>>>>>>>> +---------
>>>>>>>> +// The general command line
>>>>>>>> +*gpiobench* [ options ]
>>>>>>>> +
>>>>>>>> +DESCRIPTION
>>>>>>>> +------------
>>>>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio latency
>>>>>>>> +benchmark program.  The system must run a suitable Xenomai
>>>>>>>> enabled kernel with
>>>>>>>> +the respective module (xeno_timerbench).
>>>>>>>> +
>>>>>>>> +OPTIONS
>>>>>>>> +--------
>>>>>>>> +*gpiobench* accepts the following options:
>>>>>>>> +
>>>>>>>> +*-h <histogram-size>*::
>>>>>>>> +default = 100, increase if your last bucket is full
>>>>>>>> +
>>>>>>>> +*-l <num-of-loops>*::
>>>>>>>> +default=1000, number of loops to run the test
>>>>>>>> +
>>>>>>>> +*-q <quiet>*::
>>>>>>>> +print only a summary on exit
>>>>>>>> +
>>>>>>>> +*-m <test-mode>*::
>>>>>>>> +0 = loopback (default), 1 = react
>>>>>>>> +
>>>>>>>> +*-c <pin-controller>*::
>>>>>>>> +name of pin controller
>>>>>>
>>>>>> Consider changing this to from pin controller to something a little
>>>>>> more generic.  On the bcm2835 the gpios are
>>>>>>>> +
>>>>>>>> +*-o <output-pin>*::
>>>>>>>> +number of gpio pin as output
>>>>>>>> +
>>>>>>>> +*-i <interrupt-pin>*::
>>>>>>>> +number of gpin pin as interrupt
>>>>>>>> +
>>>>>>>> +*-p <priority>*::
>>>>>>>> +default = 99, task priority
>>>>>>>> +
>>>>>>>> +*-b <bracktrace>*::
>>>>>>>> +default = 1000, send break trace command when latency > breaktrace
>>>>>>>> +
>>>>>>>> +
>>>>>>>> +
>>>>>>>> +AUTHOR
>>>>>>>> +-------
>>>>>>>> +*gpiobench* was written by song chen. This man page
>>>>>>>> +was written by song chen.
>>>>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
>>>>>>>> index 76d108e..4932f6d 100644
>>>>>>>> --- a/testsuite/Makefile.am
>>>>>>>> +++ b/testsuite/Makefile.am
>>>>>>>> @@ -1,5 +1,5 @@
>>>>>>>>
>>>>>>>> -SUBDIRS = latency smokey
>>>>>>>> +SUBDIRS = latency smokey gpiobench
>>>>>>>>
>>>>>>>>    if XENO_COBALT
>>>>>>>>    SUBDIRS +=           \
>>>>>>>> @@ -13,6 +13,7 @@ endif
>>>>>>>>    DIST_SUBDIRS =               \
>>>>>>>>         clocktest       \
>>>>>>>>         gpiotest        \
>>>>>>>> +     gpiobench   \
>>>>>>>>         latency         \
>>>>>>>>         smokey          \
>>>>>>>>         spitest         \
>>>>>>>> diff --git a/testsuite/gpiobench/Makefile.am
>>>>>>>> b/testsuite/gpiobench/Makefile.am
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..cca3395
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/testsuite/gpiobench/Makefile.am
>>>>>>>> @@ -0,0 +1,18 @@
>>>>>>>> +testdir = @XENO_TEST_DIR@
>>>>>>>> +
>>>>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
>>>>>>>> +
>>>>>>>> +test_PROGRAMS = gpiobench
>>>>>>>> +
>>>>>>>> +gpiobench_SOURCES = gpiobench.c
>>>>>>>> +
>>>>>>>> +gpiobench_CPPFLAGS =                 \
>>>>>>>> +     $(XENO_USER_CFLAGS)     \
>>>>>>>> +     -I$(top_srcdir)/include
>>>>>>>> +
>>>>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
>>>>>>>> +
>>>>>>>> +gpiobench_LDADD =                    \
>>>>>>>> +     @XENO_CORE_LDADD@       \
>>>>>>>> +     @XENO_USER_LDADD@       \
>>>>>>>> +     -lpthread -lrt -lm
>>>>>>>> diff --git a/testsuite/gpiobench/gpiobench.c
>>>>>>>> b/testsuite/gpiobench/gpiobench.c
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..7f19837
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/testsuite/gpiobench/gpiobench.c
>>>>>>>> @@ -0,0 +1,654 @@
>>>>>>>> +/*
>>>>>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
>>>>>>>> + *
>>>>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>>>>> obtaining
>>>>>>>> + * a copy of this software and associated documentation files (the
>>>>>>>> + * "Software"), to deal in the Software without restriction,
>>>>>>>> including
>>>>>>>> + * without limitation the rights to use, copy, modify, merge,
>>>>>>>> publish,
>>>>>>>> + * distribute, sublicense, and/or sell copies of the Software,
>>>>>>>> and to
>>>>>>>> + * permit persons to whom the Software is furnished to do so,
>>>>>>>> subject to
>>>>>>>> + * the following conditions:
>>>>>>>> + *
>>>>>>>> + * The above copyright notice and this permission notice shall be
>>>>>>>> included
>>>>>>>> + * in all copies or substantial portions of the Software.
>>>>>>>> + *
>>>>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>>>>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
>>>>>>>> WARRANTIES OF
>>>>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>>>>>>>> NONINFRINGEMENT.
>>>>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
>>>>>>>> FOR ANY
>>>>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
>>>>>>>> CONTRACT,
>>>>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
>>>>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>>>>>>>> + */
>>>>>>>> +#include <stdlib.h>
>>>>>>>> +#include <math.h>
>>>>>>>> +#include <stdio.h>
>>>>>>>> +#include <string.h>
>>>>>>>> +#include <errno.h>
>>>>>>>> +#include <error.h>
>>>>>>>> +#include <signal.h>
>>>>>>>> +#include <sched.h>
>>>>>>>> +#include <time.h>
>>>>>>>> +#include <sys/time.h>
>>>>>>>> +#include <unistd.h>
>>>>>>>> +#include <pthread.h>
>>>>>>>> +#include <semaphore.h>
>>>>>>>> +#include <sys/timerfd.h>
>>>>>>>> +#include <xeno_config.h>
>>>>>>>> +#include <rtdm/testing.h>
>>>>>>>> +#include <rtdm/gpio.h>
>>>>>>>> +#include <boilerplate/trace.h>
>>>>>>>> +#include <xenomai/init.h>
>>>>>>>> +#include <sys/mman.h>
>>>>>>>> +#include <getopt.h>
>>>>>>>> +
>>>>>>>> +#define NS_PER_MS (1000000)
>>>>>>>> +#define NS_PER_S (1000000000)
>>>>>>>> +
>>>>>>>> +#define DEFAULT_PRIO 99
>>>>>>>> +#define VERSION_STRING "0.1"
>>>>>>>> +#define GPIO_HIGH 1
>>>>>>>> +#define GPIO_LOW  0
>>>>>>>> +#define MAX_HIST             100
>>>>>>>> +#define MAX_CYCLES 1000000
>>>>>>>> +#define DEFAULT_LIMIT 1000
>>>>>>>> +#define DEV_PATH    "/dev/rtdm/"
>>>>>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
>>>>>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
>>>>>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
>>>>>>>> +#define ON  "1"
>>>>>>>> +#define OFF "0"
>>>>>>>> +
>>>>>>>> +enum {
>>>>>>>> +     MODE_LOOPBACK,
>>>>>>>> +     MODE_REACT,
>>>>>>>> +     MODE_ALL
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +/* Struct for statistics */
>>>>>>>> +struct test_stat {
>>>>>>>> +     long inner_min;
>>>>>>>> +     long inner_max;
>>>>>>>> +     double inner_avg;
>>>>>>>> +     long *inner_hist_array;
>>>>>>>> +     long inner_hist_overflow;
>>>>>>>> +     long outer_min;
>>>>>>>> +     long outer_max;
>>>>>>>> +     double outer_avg;
>>>>>>>> +     long *outer_hist_array;
>>>>>>>> +     long outer_hist_overflow;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +/* Struct for information */
>>>>>>>> +struct test_info {
>>>>>>>> +     unsigned long max_cycles;
>>>>>>>> +     unsigned long total_cycles;
>>>>>>>> +     unsigned long max_histogram;
>>>>>>>> +     int mode;
>>>>>>>> +     int prio;
>>>>>>>> +     int quiet;
>>>>>>>> +     int tracelimit;
>>>>>>>> +     int fd_dev_intr;
>>>>>>>> +     int fd_dev_out;
>>>>>>>> +     char pin_controller[32];
>>>>>>>> +     pthread_t gpio_task;
>>>>>>>> +     int gpio_intr;
>>>>>>>> +     int gpio_out;
>>>>>>>> +     struct test_stat ts;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +struct test_info ti;
>>>>>>>> +/* Print usage information */
>>>>>>>> +static void display_help(void)
>>>>>>>> +{
>>>>>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
>>>>>>>> +     printf("Usage:\n"
>>>>>>>> +            "gpiobench <options>\n\n"
>>>>>>>> +
>>>>>>>> +            "-b       --breaktrace=USEC  send break trace command
>>>>>>>> when latency > USEC\n"
>>>>>>>> +            "                            default=1000\n"
>>>>>>>> +            "-h       --histogram=US     dump a latency histogram
>>>>>>>> to stdout after the run\n"
>>>>>>>> +            "                            US is the max time to be
>>>>>>>> tracked in microseconds,\n"
>>>>>>>> +            "                            default=100\n"
>>>>>>>> +            "-l       --loops            number of loops,
>>>>>>>> default=1000\n"
>>>>>>>> +            "-p       --prio             priority of highest prio
>>>>>>>> thread, defaults=99\n"
>>>>>>>> +            "-q       --quiet            print only a summary on
>>>>>>>> exit\n"
>>>>>>>> +            "-o       --output           gpio port number for
>>>>>>>> output, no default value,\n"
>>>>>>>> +            "                            must be specified\n"
>>>>>>>> +            "-i       --intr             gpio port number as an
>>>>>>>> interrupt, no default value,\n"
>>>>>>>> +            "                            must be specified\n"
>>>>>>>> +            "-c       --pinctrl          gpio pin controller's
>>>>>>>> name, no default value,\n"
>>>>>>>> +            "                            must be specified\n"
>>>>>>>> +            "-m       --testmode         0 is loopback mode\n"
>>>>>>>> +            "                            1 is react mode which
>>>>>>>> works with a latency box,\n"
>>>>>>>> +            "                            default=0\n\n"
>>>>>>>> +
>>>>>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
>>>>>>>> +             );
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void process_options(int argc, char *argv[])
>>>>>>>> +{
>>>>>>>> +     int c = 0;
>>>>>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
>>>>>>>> +
>>>>>>>> +     struct option long_options[] = {
>>>>>>>> +             { "bracetrace", required_argument, 0, 'b'},
>>>>>>>> +             { "histogram", required_argument, 0, 'h'},
>>>>>>>> +             { "loops", required_argument, 0, 'l'},
>>>>>>>> +             { "prio", required_argument, 0, 'p'},
>>>>>>>> +             { "quiet", no_argument, 0, 'q'},
>>>>>>>> +             { "output", required_argument, 0, 'o'},
>>>>>>>> +             { "intr", required_argument, 0, 'i'},
>>>>>>>> +             { "pinctrl", required_argument, 0, 'c'},
>>>>>>>> +             { "testmode", required_argument, 0, 'm'},
>>>>>>>> +             { 0, 0, 0, 0},
>>>>>>>> +     };
>>>>>>>> +
>>>>>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
>>>>>>>> +                                             NULL)) != -1) {
>>>>>>>> +             switch (c) {
>>>>>>>> +             case 'h':
>>>>>>>> +                     ti.max_histogram = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'p':
>>>>>>>> +                     ti.prio = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'l':
>>>>>>>> +                     ti.max_cycles = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'q':
>>>>>>>> +                     ti.quiet = 1;
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'b':
>>>>>>>> +                     ti.tracelimit = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'i':
>>>>>>>> +                     ti.gpio_intr = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'o':
>>>>>>>> +                     ti.gpio_out = atoi(optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'c':
>>>>>>>> +                     strcpy(ti.pin_controller, optarg);
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             case 'm':
>>>>>>>> +                     ti.mode = atoi(optarg) >=
>>>>>>>> +                             MODE_REACT ? MODE_REACT :
>>>>>>>> MODE_LOOPBACK;
>>>>>>>> +                     break;
>>>>>>>> +
>>>>>>>> +             default:
>>>>>>>> +                     display_help();
>>>>>>>> +                     exit(2);
>>>>>>>> +             }
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
>>>>>>>> +                             || (strlen(ti.pin_controller) == 0)) {
>>>>>>>> +             display_help();
>>>>>>>> +             exit(2);
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
>>>>>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES :
>>>>>>>> ti.max_cycles;
>>>>>>>> +
>>>>>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
>>>>>>>> +             MAX_HIST : ti.max_histogram;
>>>>>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram,
>>>>>>>> sizeof(long));
>>>>>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram,
>>>>>>>> sizeof(long));
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int thread_msleep(unsigned int ms)
>>>>>>>> +{
>>>>>>>> +     struct timespec ts = {
>>>>>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
>>>>>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
>>>>>>>> +     };
>>>>>>>> +
>>>>>>>> +     return -nanosleep(&ts, NULL);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static inline int64_t calc_us(struct timespec t)
>>>>>>>> +{
>>>>>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int setevent(char *event, char *val)
>>>>>>>> +{
>>>>>>>> +     int fd;
>>>>>>>> +     int ret;
>>>>>>>> +
>>>>>>>> +     fd = open(event, O_WRONLY);
>>>>>>>> +     if (fd < 0) {
>>>>>>>> +             printf("unable to open %s\n", event);
>>>>>>>> +             return -1;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ret = write(fd, val, strlen(val));
>>>>>>>> +     if (ret < 0) {
>>>>>>>> +             printf("unable to write %s to %s\n", val, event);
>>>>>>>> +             close(fd);
>>>>>>>> +             return -1;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     close(fd);
>>>>>>>> +     return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void tracing(char *enable)
>>>>>>>> +{
>>>>>>>> +     setevent(TRACING_EVENTS, enable);
>>>>>>>> +     setevent(TRACING_ON, enable);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +#define write_check(__fd, __buf, __len)                      \
>>>>>>>> +     do {                                            \
>>>>>>>> +             int __ret = write(__fd, __buf, __len);  \
>>>>>>>> +             (void)__ret;                            \
>>>>>>>> +     } while (0)
>>>>>>>> +
>>>>>>>> +#define TRACEBUFSIZ 1024
>>>>>>>> +static __thread char tracebuf[TRACEBUFSIZ];
>>>>>>>> +
>>>>>>>> +static void tracemark(char *fmt, ...)
>>>>>>>> __attribute__((format(printf, 1, 2)));
>>>>>>>> +static void tracemark(char *fmt, ...)
>>>>>>>> +{
>>>>>>>> +     va_list ap;
>>>>>>>> +     int len;
>>>>>>>> +     int tracemark_fd;
>>>>>>>> +
>>>>>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
>>>>>>>> +     if (tracemark_fd == -1) {
>>>>>>>> +             printf("unable to open trace_marker file: %s\n",
>>>>>>>> TRACE_MARKER);
>>>>>>>> +             return;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     /* bail out if we're not tracing */
>>>>>>>> +     /* or if the kernel doesn't support trace_mark */
>>>>>>>> +     if (tracemark_fd < 0)
>>>>>>>> +             return;
>>>>>>>> +
>>>>>>>> +     va_start(ap, fmt);
>>>>>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
>>>>>>>> +     va_end(ap);
>>>>>>>> +     write_check(tracemark_fd, tracebuf, len);
>>>>>>>> +
>>>>>>>> +     close(tracemark_fd);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int rw_gpio(int value, int index)
>>>>>>>> +{
>>>>>>>> +     int ret;
>>>>>>>> +     struct timespec timestamp;
>>>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
>>>>>>>> +
>>>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>>>> +     gpio_write = calc_us(timestamp);
>>>>>>>> +
>>>>>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>>>> +     if (ret < 0) {
>>>>>>>> +             printf("write GPIO, failed\n");
>>>>>>>> +             return ret;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct
>>>>>>>> rtdm_gpio_readout));
>>>>>>>> +     if (ret < 0) {
>>>>>>>> +             printf("read GPIO, failed\n");
>>>>>>>> +             return ret;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>>>> +     gpio_read = calc_us(timestamp);
>>>>>>>> +
>>>>>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
>>>>>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
>>>>>>>> +
>>>>>>>> +     if (inner_diff < ti.ts.inner_min)
>>>>>>>> +             ti.ts.inner_min = inner_diff;
>>>>>>>> +     if (inner_diff > ti.ts.inner_max)
>>>>>>>> +             ti.ts.inner_max = inner_diff;
>>>>>>>> +     ti.ts.inner_avg += (double) inner_diff;
>>>>>>>> +     if (inner_diff >= ti.max_histogram)
>>>>>>>> +             ti.ts.inner_hist_overflow++;
>>>>>>>> +     else
>>>>>>>> +             ti.ts.inner_hist_array[inner_diff]++;
>>>>>>>> +
>>>>>>>> +     if (outer_diff < ti.ts.outer_min)
>>>>>>>> +             ti.ts.outer_min = outer_diff;
>>>>>>>> +     if (inner_diff > ti.ts.outer_max)
>>>>>>>> +             ti.ts.outer_max = outer_diff;
>>>>>>>> +     ti.ts.outer_avg += (double) outer_diff;
>>>>>>>> +     if (outer_diff >= ti.max_histogram)
>>>>>>>> +             ti.ts.outer_hist_overflow++;
>>>>>>>> +     else
>>>>>>>> +             ti.ts.outer_hist_array[outer_diff]++;
>>>>>>>> +
>>>>>>>> +     if (ti.quiet == 0)
>>>>>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff:
>>>>>>>> %8ld\n",
>>>>>>>> +                                     index, inner_diff, outer_diff);
>>>>>>>> +
>>>>>>>> +     return outer_diff;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void *run_gpiobench_loop(void *cookie)
>>>>>>>> +{
>>>>>>>> +     int i, ret;
>>>>>>>> +
>>>>>>>> +     printf("----rt task, gpio loop, test run----\n");
>>>>>>>> +
>>>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>>>> +             ti.total_cycles = i;
>>>>>>>> +             /* send a high level pulse from gpio output pin and
>>>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>>>> +              * measuring the time elapsed between the two events
>>>>>>>> +              */
>>>>>>>> +             ret = rw_gpio(GPIO_HIGH, i);
>>>>>>>> +             if (ret < 0) {
>>>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>>>> +                     break;
>>>>>>>> +             } else if (ret > ti.tracelimit) {
>>>>>>>> +                     tracemark("hit latency threshold (%d > %d),
>>>>>>>> index: %d",
>>>>>>>> +                                             ret, ti.tracelimit, i);
>>>>>>>> +                     break;
>>>>>>>> +             }
>>>>>>>> +
>>>>>>>> +             /*take a break, nanosleep here will not jeopardize
>>>>>>>> the latency*/
>>>>>>>> +             thread_msleep(10);
>>>>>>>> +
>>>>>>>> +             ret = rw_gpio(GPIO_LOW, i);
>>>>>>>> +             /* send a low level pulse from gpio output pin and
>>>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>>>> +              * measuring the time elapsed between the two events
>>>>>>>> +              */
>>>>>>>> +             if (ret < 0) {
>>>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>>>> +                     break;
>>>>>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
>>>>>>>> +                     tracemark("hit latency threshold (%d > %d),
>>>>>>>> index: %d",
>>>>>>>> +                                             ret, ti.tracelimit, i);
>>>>>>>> +                     break;
>>>>>>>> +             }
>>>>>>>> +
>>>>>>>> +             /*take a break, nanosleep here will not jeopardize
>>>>>>>> the latency*/
>>>>>>>> +             thread_msleep(10);
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
>>>>>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
>>>>>>>> +
>>>>>>>> +     return NULL;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void *run_gpiobench_react(void *cookie)
>>>>>>>> +{
>>>>>>>> +     int value, ret, i;
>>>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>>>> +
>>>>>>>> +     printf("----rt task, gpio react, test run----\n");
>>>>>>>> +
>>>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>>>> +             /* received a pulse from latency box from one of
>>>>>>>> +              * the gpio pin pair
>>>>>>>> +              */
>>>>>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
>>>>>>>> +             if (ret < 0) {
>>>>>>>> +                     printf("RW GPIO read, failed\n");
>>>>>>>> +                     break;
>>>>>>>> +             }
>>>>>>>> +
>>>>>>>> +             if (ti.quiet == 0)
>>>>>>>> +                     printf("idx: %d, received signal from
>>>>>>>> latency box\n",
>>>>>>>> +                                 i);
>>>>>>>> +
>>>>>>>> +             /* send a signal back from the other gpio pin
>>>>>>>> +              * to latency box as the acknowledge,
>>>>>>>> +              * latency box will measure the time elapsed
>>>>>>>> +              * between the two events
>>>>>>>> +              */
>>>>>>>> +             value = GPIO_HIGH;
>>>>>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>>>> +             if (ret < 0) {
>>>>>>>> +                     printf("RW GPIO write, failed\n");
>>>>>>>> +                     break;
>>>>>>>> +             }
>>>>>>>> +
>>>>>>>> +             if (ti.quiet == 0)
>>>>>>>> +                     printf("idx: %d, sent reaction to latency
>>>>>>>> box\n", i);
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     return NULL;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int prio)
>>>>>>>> +{
>>>>>>>> +     struct sched_param p;
>>>>>>>> +     int ret;
>>>>>>>> +
>>>>>>>> +     ret = pthread_attr_init(attr);
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("pthread_attr_init(), failed\n");
>>>>>>>> +             return;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ret = pthread_attr_setinheritsched(attr,
>>>>>>>> PTHREAD_EXPLICIT_SCHED);
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
>>>>>>>> +             return;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     ret = pthread_attr_setschedpolicy(attr,
>>>>>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
>>>>>>>> +             return;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     p.sched_priority = prio;
>>>>>>>> +     ret = pthread_attr_setschedparam(attr, &p);
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("pthread_attr_setschedparam(), failed\n");
>>>>>>>> +             return;
>>>>>>>> +     }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void init_ti(void)
>>>>>>>> +{
>>>>>>>> +     memset(&ti, 0, sizeof(struct test_info));
>>>>>>>> +     ti.prio = DEFAULT_PRIO;
>>>>>>>> +     ti.max_cycles = MAX_CYCLES;
>>>>>>>> +     ti.total_cycles = MAX_CYCLES;
>>>>>>>> +     ti.max_histogram = MAX_HIST;
>>>>>>>> +     ti.tracelimit = DEFAULT_LIMIT;
>>>>>>>> +     ti.quiet = 0;
>>>>>>>> +     ti.gpio_out = -1;
>>>>>>>> +     ti.gpio_intr = -1;
>>>>>>>> +     ti.mode = MODE_LOOPBACK;
>>>>>>>> +
>>>>>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
>>>>>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
>>>>>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void print_hist(void)
>>>>>>>> +{
>>>>>>>> +     int i;
>>>>>>>> +
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("# Inner Loop Histogram\n");
>>>>>>>> +     printf("# Inner Loop latency is the latency in kernel space\n"
>>>>>>>> +                "# between gpio_set_value and irq handler\n");
>>>>>>>> +
>>>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>>>> +             unsigned long curr_latency = ti.ts.inner_hist_array[i];
>>>>>>>> +
>>>>>>>> +             printf("%06d ", i);
>>>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     printf("# Total:");
>>>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>>>> +     printf("\n");
>>>>>>>> +
>>>>>>>> +     printf("# Min Latencies:");
>>>>>>>> +     printf(" %05lu", ti.ts.inner_min);
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("# Avg Latencies:");
>>>>>>>> +     printf(" %05lf", ti.ts.inner_avg);
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("# Max Latencies:");
>>>>>>>> +     printf(" %05lu", ti.ts.inner_max);
>>>>>>>> +     printf("\n");
>>>>>>>> +
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("\n");
>>>>>>>> +
>>>>>>>> +     printf("# Outer Loop Histogram\n");
>>>>>>>> +     printf("# Outer Loop latency is the latency in user space\n"
>>>>>>>> +                "# between write and read\n"
>>>>>>>> +                "# Technically, outer loop latency is inner loop
>>>>>>>> latercy\n"
>>>>>>>> +                "# plus overhead of event wakeup\n");
>>>>>>>> +
>>>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>>>> +             unsigned long curr_latency = ti.ts.outer_hist_array[i];
>>>>>>>> +
>>>>>>>> +             printf("%06d ", i);
>>>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     printf("# Total:");
>>>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>>>> +     printf("\n");
>>>>>>>> +
>>>>>>>> +     printf("# Min Latencies:");
>>>>>>>> +     printf(" %05lu", ti.ts.outer_min);
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("# Avg Latencies:");
>>>>>>>> +     printf(" %05lf", ti.ts.outer_avg);
>>>>>>>> +     printf("\n");
>>>>>>>> +     printf("# Max Latencies:");
>>>>>>>> +     printf(" %05lu", ti.ts.outer_max);
>>>>>>>> +     printf("\n");
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void cleanup(void)
>>>>>>>> +{
>>>>>>>> +     int ret;
>>>>>>>> +
>>>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>>>> +             tracing(OFF);
>>>>>>>> +
>>>>>>>> +     ret = close(ti.fd_dev_out);
>>>>>>>> +     if (ret < 0)
>>>>>>>> +             printf("can't close gpio_out device\n");
>>>>>>>> +
>>>>>>>> +     ret = close(ti.fd_dev_intr);
>>>>>>>> +     if (ret < 0)
>>>>>>>> +             printf("can't close gpio_intr device\n");
>>>>>>>> +
>>>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>>>> +             print_hist();
>>>>>>>> +
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static void cleanup_and_exit(int sig)
>>>>>>>> +{
>>>>>>>> +     printf("Signal %d received\n", sig);
>>>>>>>> +     cleanup();
>>>>>>>> +     exit(0);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +int main(int argc, char **argv)
>>>>>>>> +{
>>>>>>>> +     struct sigaction sa __attribute__((unused));
>>>>>>>> +     int ret = 0;
>>>>>>>> +     pthread_attr_t tattr;
>>>>>>>> +     int trigger, value;
>>>>>>>> +     char dev_name[64];
>>>>>>>> +
>>>>>>>> +     init_ti();
>>>>>>>> +
>>>>>>>> +     process_options(argc, argv);
>>>>>>>> +
>>>>>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("mlockall failed\n");
>>>>>>>> +             goto out;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
>>>>>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
>>>>>>>> +     if (ti.fd_dev_out < 0) {
>>>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>>>> +             goto out;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     if (ti.gpio_out) {
>>>>>>>> +             value = 0;
>>>>>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT, &value);
>>>>>>>> +             if (ret) {
>>>>>>>> +                     printf("ioctl gpio port output, failed\n");
>>>>>>>> +                     goto out;
>>>>>>>> +             }
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
>>>>>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
>>>>>>>> +     if (ti.fd_dev_intr < 0) {
>>>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>>>> +             goto out;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     if (ti.gpio_intr) {
>>>>>>>> +             trigger =
>>>>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
>>>>>>>> +             value = 1;
>>>>>>>> +
>>>>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN,
>>>>>>>> &trigger);
>>>>>>>> +             if (ret) {
>>>>>>>> +                     printf("ioctl gpio port interrupt, failed\n");
>>>>>>>> +                     goto out;
>>>>>>>> +             }
>>>>>>>> +
>>>>>>>> +  ��          ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
>>>>>>>> +             if (ret) {
>>>>>>>> +                     printf("ioctl gpio port ts, failed\n");
>>>>>>>> +                     goto out;
>>>>>>>> +             }
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>>>> +             tracing(ON);
>>>>>>>> +
>>>>>>>> +     signal(SIGTERM, cleanup_and_exit);
>>>>>>>> +     signal(SIGINT, cleanup_and_exit);
>>>>>>>> +     setup_sched_parameters(&tattr, ti.prio);
>>>>>>>> +
>>>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>>>> +                                     run_gpiobench_loop, NULL);
>>>>>>>> +     else
>>>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>>>> +                                     run_gpiobench_react, NULL);
>>>>>>>> +
>>>>>>>> +     if (ret) {
>>>>>>>> +             printf("pthread_create(gpiotask), failed\n");
>>>>>>>> +             goto out;
>>>>>>>> +     }
>>>>>>>> +
>>>>>>>> +     pthread_join(ti.gpio_task, NULL);
>>>>>>>> +     pthread_attr_destroy(&tattr);
>>>>>>>> +
>>>>>>>> +out:
>>>>>>>> +     cleanup();
>>>>>>>> +     return 0;
>>>>>>>> +}
>>>>>>>>
>>>>>>>
>>>>>>> Looks good to me. Greg, any comments from your side?
>>>>>>>
>>>>>>> Jan
>>>>>>>
>>>>>>> --
>>>>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>>>>>> Corporate Competence Center Embedded Linux
>>>>>>
>>>>>> Hi,
>>>>>>     I'll run this on the zynq platform for testing tonight.  It looks
>>>>>> good, my only concern is the 'ti.pin_controller' name may be a little
>>>>>> bit misleading, but I want to confirm that assumption tonight.
>>>>>>
>>>>>> Thanks
>>>>>>
>>>>>> Greg
>>>>>
>>>>> Tested well on zynq.  After looking at gpio-core again, the name
>>>>> pinctrl makes sense.  Looks good to me :)
>>>>>
>>>>
>>>> Perfect - applied to next.
>>>>
>>>
>>> Had to fix
>>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was
>>> trivial enough to force-push a new version.
>>>
>>
>> ...but it wasn't trivial. I migrated from uint64_t to long long, please
>> have a look at the result in next. Should be tested again on both 64 and
>> 32-bit targets.
>>
>> Jan
>>
>> --
>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>> Corporate Competence Center Embedded Linux
> 
> Don't know why i didn't hit it in my testing, I can run it again on
> ARM64/ARM platforms. I need to do an ipipe release on both soon
> anyway.

Different compiler warning defaults, probably.

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-12 16:14               ` Jan Kiszka
@ 2020-08-13  1:57                 ` chensong
  2020-08-13  8:54                   ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: chensong @ 2020-08-13  1:57 UTC (permalink / raw)
  To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org

I read the code in cyclictest, looks like

printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index,
					(unsigned long long)inner_diff,
					(unsigned long long)outer_diff);

can fix the error reported in 
https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149.

Do i need to apply a new version?

chensong

On 2020年08月13日 00:14, Jan Kiszka wrote:
> On 12.08.20 18:10, Greg Gallagher wrote:
>> On Wed, Aug 12, 2020 at 11:43 AM Jan Kiszka <jan.kiszka@siemens.com>
>> wrote:
>>>
>>> On 12.08.20 17:28, Jan Kiszka wrote:
>>>> On 12.08.20 16:55, Jan Kiszka wrote:
>>>>> On 12.08.20 05:36, Greg Gallagher wrote:
>>>>>> On Tue, Aug 11, 2020 at 11:36 AM Greg Gallagher
>>>>>> <greg@embeddedgreg.com> wrote:
>>>>>>>
>>>>>>> On Tue, Aug 11, 2020 at 3:15 AM Jan Kiszka <jan.kiszka@siemens.com>
>>>>>>> wrote:
>>>>>>>>
>>>>>>>> On 03.08.20 08:04, chensong wrote:
>>>>>>>>> This a tool to benchmark the latency of GPIO driver,
>>>>>>>>> it's able to run 2 kinds of benchmark test:
>>>>>>>>>
>>>>>>>>> 1, loopback mode
>>>>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>>>>      like gpio-bcm2835 and gpio-core.c, one is as output,
>>>>>>>>>      the other is as interrupt
>>>>>>>>> 2) call write_rt to send a pulse from output
>>>>>>>>> 3) call read_rt to get timestamps recorded in driver (inner loop)
>>>>>>>>> 4) also record timespace in user space(outer_loop)
>>>>>>>>>      outer_loop is inner_loop plus overhead of event wakeup
>>>>>>>>> 5) ftrace enable/disable
>>>>>>>>>
>>>>>>>>> 2, react mode
>>>>>>>>> 1) apply 2 gpio pins by calling service in gpio RTDM driver
>>>>>>>>>      like gpio-bcm2835 and gpio-core.c, one is as ourput,
>>>>>>>>>      the other is as interrupt
>>>>>>>>> 2) call read_rt to wait for a pulse from latency box
>>>>>>>>> 3) call write_rt to send a signal back to latency box
>>>>>>>>>      as a reaction
>>>>>>>>> 4) latency box calculates the diff and makes the histogram
>>>>>>>>>
>>>>>>>>> e.g.:
>>>>>>>>> 1) react mode:
>>>>>>>>>      gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 1 -l 1000
>>>>>>>>> 2) loopback mode:
>>>>>>>>>      gpiobench -o 20 -i 21 -c pinctrl-bcm2835 -m 0 -l 1000 -h 100
>>>>>>>>> -b 50
>>>>>>>>>
>>>>>>>>> CC: Jan Kiszka <jan.kiszka@siemens.com>
>>>>>>>>> CC: Greg Gallagher <greg@embeddedgreg.com>
>>>>>>>>>
>>>>>>>>> Signed-off-by: chensong <chensong@tj.kylinos.cn>
>>>>>>>>> ---
>>>>>>>>>    configure.ac                     |   1 +
>>>>>>>>>    doc/asciidoc/man1/gpiobench.adoc |  70 +++++
>>>>>>>>>    testsuite/Makefile.am            |   3 +-
>>>>>>>>>    testsuite/gpiobench/Makefile.am  |  18 ++
>>>>>>>>>    testsuite/gpiobench/gpiobench.c  | 654
>>>>>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>>>>>    5 files changed, 745 insertions(+), 1 deletion(-)
>>>>>>>>>    create mode 100644 doc/asciidoc/man1/gpiobench.adoc
>>>>>>>>>    create mode 100644 testsuite/gpiobench/Makefile.am
>>>>>>>>>    create mode 100644 testsuite/gpiobench/gpiobench.c
>>>>>>>>>
>>>>>>>>> diff --git a/configure.ac b/configure.ac
>>>>>>>>> index 29fefab..164c449 100644
>>>>>>>>> --- a/configure.ac
>>>>>>>>> +++ b/configure.ac
>>>>>>>>> @@ -939,6 +939,7 @@ AC_CONFIG_FILES([ \
>>>>>>>>>         testsuite/latency/Makefile \
>>>>>>>>>         testsuite/switchtest/Makefile \
>>>>>>>>>         testsuite/gpiotest/Makefile \
>>>>>>>>> +     testsuite/gpiobench/Makefile \
>>>>>>>>>         testsuite/spitest/Makefile \
>>>>>>>>>         testsuite/smokey/Makefile \
>>>>>>>>>         testsuite/smokey/arith/Makefile \
>>>>>>>>> diff --git a/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>>> b/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..bd32ea8
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/doc/asciidoc/man1/gpiobench.adoc
>>>>>>>>> @@ -0,0 +1,70 @@
>>>>>>>>> +// ** The above line should force tbl to be a preprocessor **
>>>>>>>>> +// Man page for gpiobench
>>>>>>>>> +//
>>>>>>>>> +// Copyright (C) 2020 song chen <chensong@tj.kylinos.cn>
>>>>>>>>> +//
>>>>>>>>> +// You may distribute under the terms of the GNU General Public
>>>>>>>>> +// License as specified in the file COPYING that comes with the
>>>>>>>>> +// Xenomai distribution.
>>>>>>>>> +//
>>>>>>>>> +//
>>>>>>>>> +GPIOBENCH(1)
>>>>>>>>> +==========
>>>>>>>>> +:doctype: manpage
>>>>>>>>> +:revdate: 2020/08/03
>>>>>>>>> +:man source: Xenomai
>>>>>>>>> +:man version: {xenover}
>>>>>>>>> +:man manual: Xenomai Manual
>>>>>>>>> +
>>>>>>>>> +NAME
>>>>>>>>> +-----
>>>>>>>>> +gpiobench - Xenomai gpio latency benchmark
>>>>>>>>> +
>>>>>>>>> +SYNOPSIS
>>>>>>>>> +---------
>>>>>>>>> +// The general command line
>>>>>>>>> +*gpiobench* [ options ]
>>>>>>>>> +
>>>>>>>>> +DESCRIPTION
>>>>>>>>> +------------
>>>>>>>>> +*gpiobench* is part of the Xenomai test suite. It is a gpio
>>>>>>>>> latency
>>>>>>>>> +benchmark program.  The system must run a suitable Xenomai
>>>>>>>>> enabled kernel with
>>>>>>>>> +the respective module (xeno_timerbench).
>>>>>>>>> +
>>>>>>>>> +OPTIONS
>>>>>>>>> +--------
>>>>>>>>> +*gpiobench* accepts the following options:
>>>>>>>>> +
>>>>>>>>> +*-h <histogram-size>*::
>>>>>>>>> +default = 100, increase if your last bucket is full
>>>>>>>>> +
>>>>>>>>> +*-l <num-of-loops>*::
>>>>>>>>> +default=1000, number of loops to run the test
>>>>>>>>> +
>>>>>>>>> +*-q <quiet>*::
>>>>>>>>> +print only a summary on exit
>>>>>>>>> +
>>>>>>>>> +*-m <test-mode>*::
>>>>>>>>> +0 = loopback (default), 1 = react
>>>>>>>>> +
>>>>>>>>> +*-c <pin-controller>*::
>>>>>>>>> +name of pin controller
>>>>>>>
>>>>>>> Consider changing this to from pin controller to something a little
>>>>>>> more generic.  On the bcm2835 the gpios are
>>>>>>>>> +
>>>>>>>>> +*-o <output-pin>*::
>>>>>>>>> +number of gpio pin as output
>>>>>>>>> +
>>>>>>>>> +*-i <interrupt-pin>*::
>>>>>>>>> +number of gpin pin as interrupt
>>>>>>>>> +
>>>>>>>>> +*-p <priority>*::
>>>>>>>>> +default = 99, task priority
>>>>>>>>> +
>>>>>>>>> +*-b <bracktrace>*::
>>>>>>>>> +default = 1000, send break trace command when latency >
>>>>>>>>> breaktrace
>>>>>>>>> +
>>>>>>>>> +
>>>>>>>>> +
>>>>>>>>> +AUTHOR
>>>>>>>>> +-------
>>>>>>>>> +*gpiobench* was written by song chen. This man page
>>>>>>>>> +was written by song chen.
>>>>>>>>> diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
>>>>>>>>> index 76d108e..4932f6d 100644
>>>>>>>>> --- a/testsuite/Makefile.am
>>>>>>>>> +++ b/testsuite/Makefile.am
>>>>>>>>> @@ -1,5 +1,5 @@
>>>>>>>>>
>>>>>>>>> -SUBDIRS = latency smokey
>>>>>>>>> +SUBDIRS = latency smokey gpiobench
>>>>>>>>>
>>>>>>>>>    if XENO_COBALT
>>>>>>>>>    SUBDIRS +=           \
>>>>>>>>> @@ -13,6 +13,7 @@ endif
>>>>>>>>>    DIST_SUBDIRS =               \
>>>>>>>>>         clocktest       \
>>>>>>>>>         gpiotest        \
>>>>>>>>> +     gpiobench   \
>>>>>>>>>         latency         \
>>>>>>>>>         smokey          \
>>>>>>>>>         spitest         \
>>>>>>>>> diff --git a/testsuite/gpiobench/Makefile.am
>>>>>>>>> b/testsuite/gpiobench/Makefile.am
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..cca3395
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/testsuite/gpiobench/Makefile.am
>>>>>>>>> @@ -0,0 +1,18 @@
>>>>>>>>> +testdir = @XENO_TEST_DIR@
>>>>>>>>> +
>>>>>>>>> +CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
>>>>>>>>> +
>>>>>>>>> +test_PROGRAMS = gpiobench
>>>>>>>>> +
>>>>>>>>> +gpiobench_SOURCES = gpiobench.c
>>>>>>>>> +
>>>>>>>>> +gpiobench_CPPFLAGS =                 \
>>>>>>>>> +     $(XENO_USER_CFLAGS)     \
>>>>>>>>> +     -I$(top_srcdir)/include
>>>>>>>>> +
>>>>>>>>> +gpiobench_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@
>>>>>>>>> $(XENO_POSIX_WRAPPERS)
>>>>>>>>> +
>>>>>>>>> +gpiobench_LDADD =                    \
>>>>>>>>> +     @XENO_CORE_LDADD@       \
>>>>>>>>> +     @XENO_USER_LDADD@       \
>>>>>>>>> +     -lpthread -lrt -lm
>>>>>>>>> diff --git a/testsuite/gpiobench/gpiobench.c
>>>>>>>>> b/testsuite/gpiobench/gpiobench.c
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..7f19837
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/testsuite/gpiobench/gpiobench.c
>>>>>>>>> @@ -0,0 +1,654 @@
>>>>>>>>> +/*
>>>>>>>>> + * Copyright (C) 2020 Song Chen <chensong@tj.kylinos.cn>
>>>>>>>>> + *
>>>>>>>>> + * Permission is hereby granted, free of charge, to any person
>>>>>>>>> obtaining
>>>>>>>>> + * a copy of this software and associated documentation files
>>>>>>>>> (the
>>>>>>>>> + * "Software"), to deal in the Software without restriction,
>>>>>>>>> including
>>>>>>>>> + * without limitation the rights to use, copy, modify, merge,
>>>>>>>>> publish,
>>>>>>>>> + * distribute, sublicense, and/or sell copies of the Software,
>>>>>>>>> and to
>>>>>>>>> + * permit persons to whom the Software is furnished to do so,
>>>>>>>>> subject to
>>>>>>>>> + * the following conditions:
>>>>>>>>> + *
>>>>>>>>> + * The above copyright notice and this permission notice shall be
>>>>>>>>> included
>>>>>>>>> + * in all copies or substantial portions of the Software.
>>>>>>>>> + *
>>>>>>>>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
>>>>>>>>> KIND,
>>>>>>>>> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
>>>>>>>>> WARRANTIES OF
>>>>>>>>> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>>>>>>>>> NONINFRINGEMENT.
>>>>>>>>> + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
>>>>>>>>> FOR ANY
>>>>>>>>> + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
>>>>>>>>> CONTRACT,
>>>>>>>>> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
>>>>>>>>> WITH THE
>>>>>>>>> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
>>>>>>>>> + */
>>>>>>>>> +#include <stdlib.h>
>>>>>>>>> +#include <math.h>
>>>>>>>>> +#include <stdio.h>
>>>>>>>>> +#include <string.h>
>>>>>>>>> +#include <errno.h>
>>>>>>>>> +#include <error.h>
>>>>>>>>> +#include <signal.h>
>>>>>>>>> +#include <sched.h>
>>>>>>>>> +#include <time.h>
>>>>>>>>> +#include <sys/time.h>
>>>>>>>>> +#include <unistd.h>
>>>>>>>>> +#include <pthread.h>
>>>>>>>>> +#include <semaphore.h>
>>>>>>>>> +#include <sys/timerfd.h>
>>>>>>>>> +#include <xeno_config.h>
>>>>>>>>> +#include <rtdm/testing.h>
>>>>>>>>> +#include <rtdm/gpio.h>
>>>>>>>>> +#include <boilerplate/trace.h>
>>>>>>>>> +#include <xenomai/init.h>
>>>>>>>>> +#include <sys/mman.h>
>>>>>>>>> +#include <getopt.h>
>>>>>>>>> +
>>>>>>>>> +#define NS_PER_MS (1000000)
>>>>>>>>> +#define NS_PER_S (1000000000)
>>>>>>>>> +
>>>>>>>>> +#define DEFAULT_PRIO 99
>>>>>>>>> +#define VERSION_STRING "0.1"
>>>>>>>>> +#define GPIO_HIGH 1
>>>>>>>>> +#define GPIO_LOW  0
>>>>>>>>> +#define MAX_HIST             100
>>>>>>>>> +#define MAX_CYCLES 1000000
>>>>>>>>> +#define DEFAULT_LIMIT 1000
>>>>>>>>> +#define DEV_PATH    "/dev/rtdm/"
>>>>>>>>> +#define TRACING_ON  "/sys/kernel/debug/tracing/tracing_on"
>>>>>>>>> +#define TRACING_EVENTS  "/sys/kernel/debug/tracing/events/enable"
>>>>>>>>> +#define TRACE_MARKER  "/sys/kernel/debug/tracing/trace_marker"
>>>>>>>>> +#define ON  "1"
>>>>>>>>> +#define OFF "0"
>>>>>>>>> +
>>>>>>>>> +enum {
>>>>>>>>> +     MODE_LOOPBACK,
>>>>>>>>> +     MODE_REACT,
>>>>>>>>> +     MODE_ALL
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +/* Struct for statistics */
>>>>>>>>> +struct test_stat {
>>>>>>>>> +     long inner_min;
>>>>>>>>> +     long inner_max;
>>>>>>>>> +     double inner_avg;
>>>>>>>>> +     long *inner_hist_array;
>>>>>>>>> +     long inner_hist_overflow;
>>>>>>>>> +     long outer_min;
>>>>>>>>> +     long outer_max;
>>>>>>>>> +     double outer_avg;
>>>>>>>>> +     long *outer_hist_array;
>>>>>>>>> +     long outer_hist_overflow;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +/* Struct for information */
>>>>>>>>> +struct test_info {
>>>>>>>>> +     unsigned long max_cycles;
>>>>>>>>> +     unsigned long total_cycles;
>>>>>>>>> +     unsigned long max_histogram;
>>>>>>>>> +     int mode;
>>>>>>>>> +     int prio;
>>>>>>>>> +     int quiet;
>>>>>>>>> +     int tracelimit;
>>>>>>>>> +     int fd_dev_intr;
>>>>>>>>> +     int fd_dev_out;
>>>>>>>>> +     char pin_controller[32];
>>>>>>>>> +     pthread_t gpio_task;
>>>>>>>>> +     int gpio_intr;
>>>>>>>>> +     int gpio_out;
>>>>>>>>> +     struct test_stat ts;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct test_info ti;
>>>>>>>>> +/* Print usage information */
>>>>>>>>> +static void display_help(void)
>>>>>>>>> +{
>>>>>>>>> +     printf("gpiobench V %s\n", VERSION_STRING);
>>>>>>>>> +     printf("Usage:\n"
>>>>>>>>> +            "gpiobench <options>\n\n"
>>>>>>>>> +
>>>>>>>>> +            "-b       --breaktrace=USEC  send break trace command
>>>>>>>>> when latency > USEC\n"
>>>>>>>>> +            "                            default=1000\n"
>>>>>>>>> +            "-h       --histogram=US     dump a latency histogram
>>>>>>>>> to stdout after the run\n"
>>>>>>>>> +            "                            US is the max time to be
>>>>>>>>> tracked in microseconds,\n"
>>>>>>>>> +            "                            default=100\n"
>>>>>>>>> +            "-l       --loops            number of loops,
>>>>>>>>> default=1000\n"
>>>>>>>>> +            "-p       --prio             priority of highest prio
>>>>>>>>> thread, defaults=99\n"
>>>>>>>>> +            "-q       --quiet            print only a summary on
>>>>>>>>> exit\n"
>>>>>>>>> +            "-o       --output           gpio port number for
>>>>>>>>> output, no default value,\n"
>>>>>>>>> +            "                            must be specified\n"
>>>>>>>>> +            "-i       --intr             gpio port number as an
>>>>>>>>> interrupt, no default value,\n"
>>>>>>>>> +            "                            must be specified\n"
>>>>>>>>> +            "-c       --pinctrl          gpio pin controller's
>>>>>>>>> name, no default value,\n"
>>>>>>>>> +            "                            must be specified\n"
>>>>>>>>> +            "-m       --testmode         0 is loopback mode\n"
>>>>>>>>> +            "                            1 is react mode which
>>>>>>>>> works with a latency box,\n"
>>>>>>>>> +            "                            default=0\n\n"
>>>>>>>>> +
>>>>>>>>> +            "e.g.     gpiobench -o 20 -i 21 -c pinctrl-bcm2835\n"
>>>>>>>>> +             );
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void process_options(int argc, char *argv[])
>>>>>>>>> +{
>>>>>>>>> +     int c = 0;
>>>>>>>>> +     static const char optstring[] = "h:p:m:l:c:b:i:o:q";
>>>>>>>>> +
>>>>>>>>> +     struct option long_options[] = {
>>>>>>>>> +             { "bracetrace", required_argument, 0, 'b'},
>>>>>>>>> +             { "histogram", required_argument, 0, 'h'},
>>>>>>>>> +             { "loops", required_argument, 0, 'l'},
>>>>>>>>> +             { "prio", required_argument, 0, 'p'},
>>>>>>>>> +             { "quiet", no_argument, 0, 'q'},
>>>>>>>>> +             { "output", required_argument, 0, 'o'},
>>>>>>>>> +             { "intr", required_argument, 0, 'i'},
>>>>>>>>> +             { "pinctrl", required_argument, 0, 'c'},
>>>>>>>>> +             { "testmode", required_argument, 0, 'm'},
>>>>>>>>> +             { 0, 0, 0, 0},
>>>>>>>>> +     };
>>>>>>>>> +
>>>>>>>>> +     while ((c = getopt_long(argc, argv, optstring, long_options,
>>>>>>>>> +                                             NULL)) != -1) {
>>>>>>>>> +             switch (c) {
>>>>>>>>> +             case 'h':
>>>>>>>>> +                     ti.max_histogram = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'p':
>>>>>>>>> +                     ti.prio = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'l':
>>>>>>>>> +                     ti.max_cycles = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'q':
>>>>>>>>> +                     ti.quiet = 1;
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'b':
>>>>>>>>> +                     ti.tracelimit = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'i':
>>>>>>>>> +                     ti.gpio_intr = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'o':
>>>>>>>>> +                     ti.gpio_out = atoi(optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'c':
>>>>>>>>> +                     strcpy(ti.pin_controller, optarg);
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             case 'm':
>>>>>>>>> +                     ti.mode = atoi(optarg) >=
>>>>>>>>> +                             MODE_REACT ? MODE_REACT :
>>>>>>>>> MODE_LOOPBACK;
>>>>>>>>> +                     break;
>>>>>>>>> +
>>>>>>>>> +             default:
>>>>>>>>> +                     display_help();
>>>>>>>>> +                     exit(2);
>>>>>>>>> +             }
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     if ((ti.gpio_out == -1) || (ti.gpio_intr == -1)
>>>>>>>>> +                             || (strlen(ti.pin_controller) ==
>>>>>>>>> 0)) {
>>>>>>>>> +             display_help();
>>>>>>>>> +             exit(2);
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ti.prio = ti.prio > DEFAULT_PRIO ? DEFAULT_PRIO : ti.prio;
>>>>>>>>> +     ti.max_cycles = ti.max_cycles > MAX_CYCLES ? MAX_CYCLES :
>>>>>>>>> ti.max_cycles;
>>>>>>>>> +
>>>>>>>>> +     ti.max_histogram = ti.max_histogram > MAX_HIST ?
>>>>>>>>> +             MAX_HIST : ti.max_histogram;
>>>>>>>>> +     ti.ts.inner_hist_array = calloc(ti.max_histogram,
>>>>>>>>> sizeof(long));
>>>>>>>>> +     ti.ts.outer_hist_array = calloc(ti.max_histogram,
>>>>>>>>> sizeof(long));
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int thread_msleep(unsigned int ms)
>>>>>>>>> +{
>>>>>>>>> +     struct timespec ts = {
>>>>>>>>> +             .tv_sec = (ms * NS_PER_MS) / NS_PER_S,
>>>>>>>>> +             .tv_nsec = (ms * NS_PER_MS) % NS_PER_S,
>>>>>>>>> +     };
>>>>>>>>> +
>>>>>>>>> +     return -nanosleep(&ts, NULL);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static inline int64_t calc_us(struct timespec t)
>>>>>>>>> +{
>>>>>>>>> +     return (t.tv_sec * NS_PER_S + t.tv_nsec);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int setevent(char *event, char *val)
>>>>>>>>> +{
>>>>>>>>> +     int fd;
>>>>>>>>> +     int ret;
>>>>>>>>> +
>>>>>>>>> +     fd = open(event, O_WRONLY);
>>>>>>>>> +     if (fd < 0) {
>>>>>>>>> +             printf("unable to open %s\n", event);
>>>>>>>>> +             return -1;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ret = write(fd, val, strlen(val));
>>>>>>>>> +     if (ret < 0) {
>>>>>>>>> +             printf("unable to write %s to %s\n", val, event);
>>>>>>>>> +             close(fd);
>>>>>>>>> +             return -1;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     close(fd);
>>>>>>>>> +     return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void tracing(char *enable)
>>>>>>>>> +{
>>>>>>>>> +     setevent(TRACING_EVENTS, enable);
>>>>>>>>> +     setevent(TRACING_ON, enable);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +#define write_check(__fd, __buf, __len)                      \
>>>>>>>>> +     do {                                            \
>>>>>>>>> +             int __ret = write(__fd, __buf, __len);  \
>>>>>>>>> +             (void)__ret;                            \
>>>>>>>>> +     } while (0)
>>>>>>>>> +
>>>>>>>>> +#define TRACEBUFSIZ 1024
>>>>>>>>> +static __thread char tracebuf[TRACEBUFSIZ];
>>>>>>>>> +
>>>>>>>>> +static void tracemark(char *fmt, ...)
>>>>>>>>> __attribute__((format(printf, 1, 2)));
>>>>>>>>> +static void tracemark(char *fmt, ...)
>>>>>>>>> +{
>>>>>>>>> +     va_list ap;
>>>>>>>>> +     int len;
>>>>>>>>> +     int tracemark_fd;
>>>>>>>>> +
>>>>>>>>> +     tracemark_fd = open(TRACE_MARKER, O_WRONLY);
>>>>>>>>> +     if (tracemark_fd == -1) {
>>>>>>>>> +             printf("unable to open trace_marker file: %s\n",
>>>>>>>>> TRACE_MARKER);
>>>>>>>>> +             return;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     /* bail out if we're not tracing */
>>>>>>>>> +     /* or if the kernel doesn't support trace_mark */
>>>>>>>>> +     if (tracemark_fd < 0)
>>>>>>>>> +             return;
>>>>>>>>> +
>>>>>>>>> +     va_start(ap, fmt);
>>>>>>>>> +     len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap);
>>>>>>>>> +     va_end(ap);
>>>>>>>>> +     write_check(tracemark_fd, tracebuf, len);
>>>>>>>>> +
>>>>>>>>> +     close(tracemark_fd);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int rw_gpio(int value, int index)
>>>>>>>>> +{
>>>>>>>>> +     int ret;
>>>>>>>>> +     struct timespec timestamp;
>>>>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>>>>> +     uint64_t gpio_write, gpio_read, inner_diff, outer_diff;
>>>>>>>>> +
>>>>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>>>>> +     gpio_write = calc_us(timestamp);
>>>>>>>>> +
>>>>>>>>> +     ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>>>>> +     if (ret < 0) {
>>>>>>>>> +             printf("write GPIO, failed\n");
>>>>>>>>> +             return ret;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ret = read(ti.fd_dev_intr, &rdo, sizeof(struct
>>>>>>>>> rtdm_gpio_readout));
>>>>>>>>> +     if (ret < 0) {
>>>>>>>>> +             printf("read GPIO, failed\n");
>>>>>>>>> +             return ret;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     clock_gettime(CLOCK_MONOTONIC, &timestamp);
>>>>>>>>> +     gpio_read = calc_us(timestamp);
>>>>>>>>> +
>>>>>>>>> +     inner_diff = (rdo.timestamp - gpio_write) / 1000;
>>>>>>>>> +     outer_diff = (gpio_read - gpio_write) / 1000;
>>>>>>>>> +
>>>>>>>>> +     if (inner_diff < ti.ts.inner_min)
>>>>>>>>> +             ti.ts.inner_min = inner_diff;
>>>>>>>>> +     if (inner_diff > ti.ts.inner_max)
>>>>>>>>> +             ti.ts.inner_max = inner_diff;
>>>>>>>>> +     ti.ts.inner_avg += (double) inner_diff;
>>>>>>>>> +     if (inner_diff >= ti.max_histogram)
>>>>>>>>> +             ti.ts.inner_hist_overflow++;
>>>>>>>>> +     else
>>>>>>>>> +             ti.ts.inner_hist_array[inner_diff]++;
>>>>>>>>> +
>>>>>>>>> +     if (outer_diff < ti.ts.outer_min)
>>>>>>>>> +             ti.ts.outer_min = outer_diff;
>>>>>>>>> +     if (inner_diff > ti.ts.outer_max)
>>>>>>>>> +             ti.ts.outer_max = outer_diff;
>>>>>>>>> +     ti.ts.outer_avg += (double) outer_diff;
>>>>>>>>> +     if (outer_diff >= ti.max_histogram)
>>>>>>>>> +             ti.ts.outer_hist_overflow++;
>>>>>>>>> +     else
>>>>>>>>> +             ti.ts.outer_hist_array[outer_diff]++;
>>>>>>>>> +
>>>>>>>>> +     if (ti.quiet == 0)
>>>>>>>>> +             printf("index: %d, inner_diff: %8ld, outer_diff:
>>>>>>>>> %8ld\n",
>>>>>>>>> +                                     index, inner_diff,
>>>>>>>>> outer_diff);
>>>>>>>>> +
>>>>>>>>> +     return outer_diff;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void *run_gpiobench_loop(void *cookie)
>>>>>>>>> +{
>>>>>>>>> +     int i, ret;
>>>>>>>>> +
>>>>>>>>> +     printf("----rt task, gpio loop, test run----\n");
>>>>>>>>> +
>>>>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>>>>> +             ti.total_cycles = i;
>>>>>>>>> +             /* send a high level pulse from gpio output pin and
>>>>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>>>>> +              * measuring the time elapsed between the two events
>>>>>>>>> +              */
>>>>>>>>> +             ret = rw_gpio(GPIO_HIGH, i);
>>>>>>>>> +             if (ret < 0) {
>>>>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>>>>> +                     break;
>>>>>>>>> +             } else if (ret > ti.tracelimit) {
>>>>>>>>> +                     tracemark("hit latency threshold (%d > %d),
>>>>>>>>> index: %d",
>>>>>>>>> +                                             ret,
>>>>>>>>> ti.tracelimit, i);
>>>>>>>>> +                     break;
>>>>>>>>> +             }
>>>>>>>>> +
>>>>>>>>> +             /*take a break, nanosleep here will not jeopardize
>>>>>>>>> the latency*/
>>>>>>>>> +             thread_msleep(10);
>>>>>>>>> +
>>>>>>>>> +             ret = rw_gpio(GPIO_LOW, i);
>>>>>>>>> +             /* send a low level pulse from gpio output pin and
>>>>>>>>> +              * receive an interrupt from the other gpio pin,
>>>>>>>>> +              * measuring the time elapsed between the two events
>>>>>>>>> +              */
>>>>>>>>> +             if (ret < 0) {
>>>>>>>>> +                     printf("RW GPIO, failed\n");
>>>>>>>>> +                     break;
>>>>>>>>> +             } else if (ti.tracelimit && ret > ti.tracelimit) {
>>>>>>>>> +                     tracemark("hit latency threshold (%d > %d),
>>>>>>>>> index: %d",
>>>>>>>>> +                                             ret,
>>>>>>>>> ti.tracelimit, i);
>>>>>>>>> +                     break;
>>>>>>>>> +             }
>>>>>>>>> +
>>>>>>>>> +             /*take a break, nanosleep here will not jeopardize
>>>>>>>>> the latency*/
>>>>>>>>> +             thread_msleep(10);
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ti.ts.inner_avg /= (ti.total_cycles * 2);
>>>>>>>>> +     ti.ts.outer_avg /= (ti.total_cycles * 2);
>>>>>>>>> +
>>>>>>>>> +     return NULL;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void *run_gpiobench_react(void *cookie)
>>>>>>>>> +{
>>>>>>>>> +     int value, ret, i;
>>>>>>>>> +     struct rtdm_gpio_readout rdo;
>>>>>>>>> +
>>>>>>>>> +     printf("----rt task, gpio react, test run----\n");
>>>>>>>>> +
>>>>>>>>> +     for (i = 0; i < ti.max_cycles; i++) {
>>>>>>>>> +             /* received a pulse from latency box from one of
>>>>>>>>> +              * the gpio pin pair
>>>>>>>>> +              */
>>>>>>>>> +             ret = read(ti.fd_dev_intr, &rdo, sizeof(rdo));
>>>>>>>>> +             if (ret < 0) {
>>>>>>>>> +                     printf("RW GPIO read, failed\n");
>>>>>>>>> +                     break;
>>>>>>>>> +             }
>>>>>>>>> +
>>>>>>>>> +             if (ti.quiet == 0)
>>>>>>>>> +                     printf("idx: %d, received signal from
>>>>>>>>> latency box\n",
>>>>>>>>> +                                 i);
>>>>>>>>> +
>>>>>>>>> +             /* send a signal back from the other gpio pin
>>>>>>>>> +              * to latency box as the acknowledge,
>>>>>>>>> +              * latency box will measure the time elapsed
>>>>>>>>> +              * between the two events
>>>>>>>>> +              */
>>>>>>>>> +             value = GPIO_HIGH;
>>>>>>>>> +             ret = write(ti.fd_dev_out, &value, sizeof(value));
>>>>>>>>> +             if (ret < 0) {
>>>>>>>>> +                     printf("RW GPIO write, failed\n");
>>>>>>>>> +                     break;
>>>>>>>>> +             }
>>>>>>>>> +
>>>>>>>>> +             if (ti.quiet == 0)
>>>>>>>>> +                     printf("idx: %d, sent reaction to latency
>>>>>>>>> box\n", i);
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     return NULL;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void setup_sched_parameters(pthread_attr_t *attr, int
>>>>>>>>> prio)
>>>>>>>>> +{
>>>>>>>>> +     struct sched_param p;
>>>>>>>>> +     int ret;
>>>>>>>>> +
>>>>>>>>> +     ret = pthread_attr_init(attr);
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("pthread_attr_init(), failed\n");
>>>>>>>>> +             return;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ret = pthread_attr_setinheritsched(attr,
>>>>>>>>> PTHREAD_EXPLICIT_SCHED);
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("pthread_attr_setinheritsched(), failed\n");
>>>>>>>>> +             return;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     ret = pthread_attr_setschedpolicy(attr,
>>>>>>>>> +                             prio ? SCHED_FIFO : SCHED_OTHER);
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("pthread_attr_setschedpolicy(), failed\n");
>>>>>>>>> +             return;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     p.sched_priority = prio;
>>>>>>>>> +     ret = pthread_attr_setschedparam(attr, &p);
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("pthread_attr_setschedparam(), failed\n");
>>>>>>>>> +             return;
>>>>>>>>> +     }
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void init_ti(void)
>>>>>>>>> +{
>>>>>>>>> +     memset(&ti, 0, sizeof(struct test_info));
>>>>>>>>> +     ti.prio = DEFAULT_PRIO;
>>>>>>>>> +     ti.max_cycles = MAX_CYCLES;
>>>>>>>>> +     ti.total_cycles = MAX_CYCLES;
>>>>>>>>> +     ti.max_histogram = MAX_HIST;
>>>>>>>>> +     ti.tracelimit = DEFAULT_LIMIT;
>>>>>>>>> +     ti.quiet = 0;
>>>>>>>>> +     ti.gpio_out = -1;
>>>>>>>>> +     ti.gpio_intr = -1;
>>>>>>>>> +     ti.mode = MODE_LOOPBACK;
>>>>>>>>> +
>>>>>>>>> +     ti.ts.inner_min = ti.ts.outer_min = DEFAULT_LIMIT;
>>>>>>>>> +     ti.ts.inner_max = ti.ts.outer_max = 0;
>>>>>>>>> +     ti.ts.inner_avg = ti.ts.outer_avg = 0.0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void print_hist(void)
>>>>>>>>> +{
>>>>>>>>> +     int i;
>>>>>>>>> +
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("# Inner Loop Histogram\n");
>>>>>>>>> +     printf("# Inner Loop latency is the latency in kernel
>>>>>>>>> space\n"
>>>>>>>>> +                "# between gpio_set_value and irq handler\n");
>>>>>>>>> +
>>>>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>>>>> +             unsigned long curr_latency =
>>>>>>>>> ti.ts.inner_hist_array[i];
>>>>>>>>> +
>>>>>>>>> +             printf("%06d ", i);
>>>>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     printf("# Total:");
>>>>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +
>>>>>>>>> +     printf("# Min Latencies:");
>>>>>>>>> +     printf(" %05lu", ti.ts.inner_min);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("# Avg Latencies:");
>>>>>>>>> +     printf(" %05lf", ti.ts.inner_avg);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("# Max Latencies:");
>>>>>>>>> +     printf(" %05lu", ti.ts.inner_max);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("\n");
>>>>>>>>> +
>>>>>>>>> +     printf("# Outer Loop Histogram\n");
>>>>>>>>> +     printf("# Outer Loop latency is the latency in user space\n"
>>>>>>>>> +                "# between write and read\n"
>>>>>>>>> +                "# Technically, outer loop latency is inner loop
>>>>>>>>> latercy\n"
>>>>>>>>> +                "# plus overhead of event wakeup\n");
>>>>>>>>> +
>>>>>>>>> +     for (i = 0; i < ti.max_histogram; i++) {
>>>>>>>>> +             unsigned long curr_latency =
>>>>>>>>> ti.ts.outer_hist_array[i];
>>>>>>>>> +
>>>>>>>>> +             printf("%06d ", i);
>>>>>>>>> +             printf("%06lu\n", curr_latency);
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     printf("# Total:");
>>>>>>>>> +     printf(" %09lu", ti.total_cycles);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +
>>>>>>>>> +     printf("# Min Latencies:");
>>>>>>>>> +     printf(" %05lu", ti.ts.outer_min);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("# Avg Latencies:");
>>>>>>>>> +     printf(" %05lf", ti.ts.outer_avg);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +     printf("# Max Latencies:");
>>>>>>>>> +     printf(" %05lu", ti.ts.outer_max);
>>>>>>>>> +     printf("\n");
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cleanup(void)
>>>>>>>>> +{
>>>>>>>>> +     int ret;
>>>>>>>>> +
>>>>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>>>>> +             tracing(OFF);
>>>>>>>>> +
>>>>>>>>> +     ret = close(ti.fd_dev_out);
>>>>>>>>> +     if (ret < 0)
>>>>>>>>> +             printf("can't close gpio_out device\n");
>>>>>>>>> +
>>>>>>>>> +     ret = close(ti.fd_dev_intr);
>>>>>>>>> +     if (ret < 0)
>>>>>>>>> +             printf("can't close gpio_intr device\n");
>>>>>>>>> +
>>>>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>>>>> +             print_hist();
>>>>>>>>> +
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cleanup_and_exit(int sig)
>>>>>>>>> +{
>>>>>>>>> +     printf("Signal %d received\n", sig);
>>>>>>>>> +     cleanup();
>>>>>>>>> +     exit(0);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +int main(int argc, char **argv)
>>>>>>>>> +{
>>>>>>>>> +     struct sigaction sa __attribute__((unused));
>>>>>>>>> +     int ret = 0;
>>>>>>>>> +     pthread_attr_t tattr;
>>>>>>>>> +     int trigger, value;
>>>>>>>>> +     char dev_name[64];
>>>>>>>>> +
>>>>>>>>> +     init_ti();
>>>>>>>>> +
>>>>>>>>> +     process_options(argc, argv);
>>>>>>>>> +
>>>>>>>>> +     ret = mlockall(MCL_CURRENT|MCL_FUTURE);
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("mlockall failed\n");
>>>>>>>>> +             goto out;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_out);
>>>>>>>>> +     ti.fd_dev_out = open(dev_name, O_RDWR);
>>>>>>>>> +     if (ti.fd_dev_out < 0) {
>>>>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>>>>> +             goto out;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     if (ti.gpio_out) {
>>>>>>>>> +             value = 0;
>>>>>>>>> +             ret = ioctl(ti.fd_dev_out, GPIO_RTIOC_DIR_OUT,
>>>>>>>>> &value);
>>>>>>>>> +             if (ret) {
>>>>>>>>> +                     printf("ioctl gpio port output, failed\n");
>>>>>>>>> +                     goto out;
>>>>>>>>> +             }
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     sprintf(dev_name, "%s%s/gpio%d",
>>>>>>>>> +                 DEV_PATH, ti.pin_controller, ti.gpio_intr);
>>>>>>>>> +     ti.fd_dev_intr = open(dev_name, O_RDWR);
>>>>>>>>> +     if (ti.fd_dev_intr < 0) {
>>>>>>>>> +             printf("can't open %s\n", dev_name);
>>>>>>>>> +             goto out;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     if (ti.gpio_intr) {
>>>>>>>>> +             trigger =
>>>>>>>>> GPIO_TRIGGER_EDGE_FALLING|GPIO_TRIGGER_EDGE_RISING;
>>>>>>>>> +             value = 1;
>>>>>>>>> +
>>>>>>>>> +             ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_IRQEN,
>>>>>>>>> &trigger);
>>>>>>>>> +             if (ret) {
>>>>>>>>> +                     printf("ioctl gpio port interrupt,
>>>>>>>>> failed\n");
>>>>>>>>> +                     goto out;
>>>>>>>>> +             }
>>>>>>>>> +
>>>>>>>>> +  ��          ret = ioctl(ti.fd_dev_intr, GPIO_RTIOC_TS, &value);
>>>>>>>>> +             if (ret) {
>>>>>>>>> +                     printf("ioctl gpio port ts, failed\n");
>>>>>>>>> +                     goto out;
>>>>>>>>> +             }
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     if (ti.tracelimit < DEFAULT_LIMIT)
>>>>>>>>> +             tracing(ON);
>>>>>>>>> +
>>>>>>>>> +     signal(SIGTERM, cleanup_and_exit);
>>>>>>>>> +     signal(SIGINT, cleanup_and_exit);
>>>>>>>>> +     setup_sched_parameters(&tattr, ti.prio);
>>>>>>>>> +
>>>>>>>>> +     if (ti.mode == MODE_LOOPBACK)
>>>>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>>>>> +                                     run_gpiobench_loop, NULL);
>>>>>>>>> +     else
>>>>>>>>> +             ret = pthread_create(&ti.gpio_task, &tattr,
>>>>>>>>> +                                     run_gpiobench_react, NULL);
>>>>>>>>> +
>>>>>>>>> +     if (ret) {
>>>>>>>>> +             printf("pthread_create(gpiotask), failed\n");
>>>>>>>>> +             goto out;
>>>>>>>>> +     }
>>>>>>>>> +
>>>>>>>>> +     pthread_join(ti.gpio_task, NULL);
>>>>>>>>> +     pthread_attr_destroy(&tattr);
>>>>>>>>> +
>>>>>>>>> +out:
>>>>>>>>> +     cleanup();
>>>>>>>>> +     return 0;
>>>>>>>>> +}
>>>>>>>>>
>>>>>>>>
>>>>>>>> Looks good to me. Greg, any comments from your side?
>>>>>>>>
>>>>>>>> Jan
>>>>>>>>
>>>>>>>> --
>>>>>>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>>>>>>> Corporate Competence Center Embedded Linux
>>>>>>>
>>>>>>> Hi,
>>>>>>>     I'll run this on the zynq platform for testing tonight.  It
>>>>>>> looks
>>>>>>> good, my only concern is the 'ti.pin_controller' name may be a
>>>>>>> little
>>>>>>> bit misleading, but I want to confirm that assumption tonight.
>>>>>>>
>>>>>>> Thanks
>>>>>>>
>>>>>>> Greg
>>>>>>
>>>>>> Tested well on zynq.  After looking at gpio-core again, the name
>>>>>> pinctrl makes sense.  Looks good to me :)
>>>>>>
>>>>>
>>>>> Perfect - applied to next.
>>>>>
>>>>
>>>> Had to fix
>>>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149. Was
>>>> trivial enough to force-push a new version.
>>>>
>>>
>>> ...but it wasn't trivial. I migrated from uint64_t to long long, please
>>> have a look at the result in next. Should be tested again on both 64 and
>>> 32-bit targets.
>>>
>>> Jan
>>>
>>> --
>>> Siemens AG, Corporate Technology, CT RDA IOT SES-DE
>>> Corporate Competence Center Embedded Linux
>>
>> Don't know why i didn't hit it in my testing, I can run it again on
>> ARM64/ARM platforms. I need to do an ipipe release on both soon
>> anyway.
>
> Different compiler warning defaults, probably.
>
> Jan
>




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-13  1:57                 ` chensong
@ 2020-08-13  8:54                   ` Jan Kiszka
  2020-08-13 10:06                     ` chensong
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-13  8:54 UTC (permalink / raw)
  To: chensong, Greg Gallagher; +Cc: Xenomai@xenomai.org

On 13.08.20 03:57, chensong wrote:
> I read the code in cyclictest, looks like
> 
> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index,
>                      (unsigned long long)inner_diff,
>                      (unsigned long long)outer_diff);

I even wonder if this is strictly correct as it ignores negative diffs.

> 
> can fix the error reported in 
> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149.
> 
> Do i need to apply a new version?

No, I've converted your patch to long long types already. Please 
cross-check what is in next branch.

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-13  8:54                   ` Jan Kiszka
@ 2020-08-13 10:06                     ` chensong
  2020-08-13 10:27                       ` Jan Kiszka
  0 siblings, 1 reply; 14+ messages in thread
From: chensong @ 2020-08-13 10:06 UTC (permalink / raw)
  To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org



On 2020年08月13日 16:54, Jan Kiszka wrote:
> On 13.08.20 03:57, chensong wrote:
>> I read the code in cyclictest, looks like
>>
>> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index,
>>                      (unsigned long long)inner_diff,
>>                      (unsigned long long)outer_diff);
>
> I even wonder if this is strictly correct as it ignores negative diffs.
>
>>
>> can fix the error reported in
>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149.
>>
>> Do i need to apply a new version?
>
> No, I've converted your patch to long long types already. Please
> cross-check what is in next branch.
i'm a little confused here, cross-check what? negative diffs? in theory, 
it should be always positive, do you mean i keep checking if there is a 
negative diff?

and what's next branch? what can i do about it?

>
> Jan
>




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-13 10:06                     ` chensong
@ 2020-08-13 10:27                       ` Jan Kiszka
  2020-08-13 10:41                         ` chensong
  0 siblings, 1 reply; 14+ messages in thread
From: Jan Kiszka @ 2020-08-13 10:27 UTC (permalink / raw)
  To: chensong, Greg Gallagher; +Cc: Xenomai@xenomai.org

On 13.08.20 12:06, chensong wrote:
> 
> 
> On 2020年08月13日 16:54, Jan Kiszka wrote:
>> On 13.08.20 03:57, chensong wrote:
>>> I read the code in cyclictest, looks like
>>>
>>> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index,
>>>                      (unsigned long long)inner_diff,
>>>                      (unsigned long long)outer_diff);
>>
>> I even wonder if this is strictly correct as it ignores negative diffs.
>>
>>>
>>> can fix the error reported in
>>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149.
>>>
>>> Do i need to apply a new version?
>>
>> No, I've converted your patch to long long types already. Please
>> cross-check what is in next branch.
> i'm a little confused here, cross-check what? negative diffs? in theory, 
> it should be always positive, do you mean i keep checking if there is a 
> negative diff?
> 
> and what's next branch? what can i do about it?

I've modified your patch in 
https://gitlab.denx.de/Xenomai/xenomai/-/commits/next. Please have a 
look if 
https://gitlab.denx.de/Xenomai/xenomai/-/commit/0bcfce8fdc8dee48fa7d85dcd3db0d0ecc6bed1a 
is fine for you as well.

Thanks,
Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] testsuite: App of gpio loopback/react benchmark
  2020-08-13 10:27                       ` Jan Kiszka
@ 2020-08-13 10:41                         ` chensong
  0 siblings, 0 replies; 14+ messages in thread
From: chensong @ 2020-08-13 10:41 UTC (permalink / raw)
  To: Jan Kiszka, Greg Gallagher; +Cc: Xenomai@xenomai.org



On 2020年08月13日 18:27, Jan Kiszka wrote:
> On 13.08.20 12:06, chensong wrote:
>>
>>
>> On 2020年08月13日 16:54, Jan Kiszka wrote:
>>> On 13.08.20 03:57, chensong wrote:
>>>> I read the code in cyclictest, looks like
>>>>
>>>> printf("index: %d, inner_diff: %llu, outer_diff: %llu\n", index,
>>>>                      (unsigned long long)inner_diff,
>>>>                      (unsigned long long)outer_diff);
>>>
>>> I even wonder if this is strictly correct as it ignores negative diffs.
>>>
>>>>
>>>> can fix the error reported in
>>>> https://travis-ci.com/github/xenomai-ci/xenomai/jobs/371291149.
>>>>
>>>> Do i need to apply a new version?
>>>
>>> No, I've converted your patch to long long types already. Please
>>> cross-check what is in next branch.
>> i'm a little confused here, cross-check what? negative diffs? in
>> theory, it should be always positive, do you mean i keep checking if
>> there is a negative diff?
>>
>> and what's next branch? what can i do about it?
>
> I've modified your patch in
> https://gitlab.denx.de/Xenomai/xenomai/-/commits/next. Please have a
> look if
> https://gitlab.denx.de/Xenomai/xenomai/-/commit/0bcfce8fdc8dee48fa7d85dcd3db0d0ecc6bed1a
> is fine for you as well.

i'm fine with that, it's thoughtful, thanks a lot.

chensong

>
> Thanks,
> Jan
>




^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2020-08-13 10:41 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-03  6:04 [PATCH v3] testsuite: App of gpio loopback/react benchmark chensong
2020-08-11  7:15 ` Jan Kiszka
2020-08-11 15:36   ` Greg Gallagher
2020-08-12  3:36     ` Greg Gallagher
2020-08-12 14:55       ` Jan Kiszka
2020-08-12 15:28         ` Jan Kiszka
2020-08-12 15:43           ` Jan Kiszka
2020-08-12 16:10             ` Greg Gallagher
2020-08-12 16:14               ` Jan Kiszka
2020-08-13  1:57                 ` chensong
2020-08-13  8:54                   ` Jan Kiszka
2020-08-13 10:06                     ` chensong
2020-08-13 10:27                       ` Jan Kiszka
2020-08-13 10:41                         ` chensong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.