* [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, ×tamp);
+ 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, ×tamp);
+ 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, ×tamp);
> + 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, ×tamp);
> + 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, ×tamp);
> > + 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, ×tamp);
> > + 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, ×tamp);
> > > + 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, ×tamp);
> > > + 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, ×tamp);
>>>> + 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, ×tamp);
>>>> + 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, ×tamp);
>>>>> + 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, ×tamp);
>>>>> + 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, ×tamp);
>>>>>> + 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, ×tamp);
>>>>>> + 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, ×tamp);
> >>>>>> + 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, ×tamp);
> >>>>>> + 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, ×tamp);
>>>>>>>> + 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, ×tamp);
>>>>>>>> + 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, ×tamp);
>>>>>>>>> + 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, ×tamp);
>>>>>>>>> + 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.