From: Jan Kiszka <jan.kiszka@domain.hid>
To: xenomai@xenomai.org
Subject: Re: [Xenomai-core] [PATCH 6/6] Introduce IRQ latency benchmark
Date: Wed, 28 Jun 2006 21:39:50 +0200 [thread overview]
Message-ID: <44A2DB06.8050000@domain.hid> (raw)
In-Reply-To: <20060626172120.322015000@domain.hid>
[-- Attachment #1.1: Type: text/plain, Size: 460 bytes --]
Here comes an update of the irqbench patch.
Changelog:
- avoid printf from signal context in irqloop
- reorder irq-enable code in the driver to avoid spurious replies on
startup
- avoid creating/destroying pthread under SCHED_FIFO (but still suffers
from prio inversion during cleanup here)
- fix broken conditional compilation of irqbench for x86 (probably
unnoticed due to forgotten bootstrap run, sigh)
- include irqbench-doc.patch
Jan
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: irqbench.patch --]
[-- Type: text/x-patch; name="irqbench.patch", Size: 40079 bytes --]
Subject: Introduce IRQ latency benchmark
This patch introduces another rttesting driver, xeno_irqbench, for measuring external IRQ latencies. The irqbench device is controlled by a user-mode tool irqloop. A second tool for plain Linux, irqbench, is provided to trigger the event over serial or parallel cross-link (the latter is incomplete yet) and measure the reaction latency.
---
configure.in | 2
doc/txt/Makefile.am | 1
doc/txt/irqbench.txt | 51 ++++
include/rtdm/rttesting.h | 38 ++
ksrc/drivers/Makefile | 2
ksrc/drivers/testing/Kconfig | 21 +
ksrc/drivers/testing/Makefile | 13 -
ksrc/drivers/testing/irqbench.c | 470 +++++++++++++++++++++++++++++++++++++
src/testsuite/Makefile.am | 2
src/testsuite/irqbench/Makefile.am | 47 +++
src/testsuite/irqbench/irqbench.c | 306 ++++++++++++++++++++++++
src/testsuite/irqbench/irqloop.c | 173 +++++++++++++
src/testsuite/irqbench/runinfo | 1
13 files changed, 1116 insertions(+), 11 deletions(-)
Index: xenomai/include/rtdm/rttesting.h
===================================================================
--- xenomai.orig/include/rtdm/rttesting.h
+++ xenomai/include/rtdm/rttesting.h
@@ -94,6 +94,28 @@ typedef struct rttst_tmbench_config {
} rttst_tmbench_config_t;
+#define RTTST_IRQBENCH_USER_TASK 0
+#define RTTST_IRQBENCH_KERNEL_TASK 1
+#define RTTST_IRQBENCH_HANDLER 2
+
+#define RTTST_IRQBENCH_SERPORT 0
+#define RTTST_IRQBENCH_PARPORT 1
+
+struct rttst_irqbench_config {
+ int mode;
+ int priority;
+ int calibration_loops;
+ unsigned int port_type;
+ unsigned long port_ioaddr;
+ unsigned int port_irq;
+} rttst_irqbench_config_t;
+
+struct rttst_irqbench_stats {
+ unsigned long long irqs_received;
+ unsigned long long irqs_acknowledged;
+} rttst_irqbench_stats_t;
+
+
#define RTTST_SWTEST_FPU 0x1
#define RTTST_SWTEST_USE_FPU 0x2 /* Only for kernel-space tasks. */
@@ -134,6 +156,22 @@ struct rttst_swtest_dir {
_IOWR(RTIOC_TYPE_TESTING, 0x11, struct rttst_overall_bench_res)
+#define RTTST_RTIOC_IRQBENCH_START \
+ _IOW(RTIOC_TYPE_TESTING, 0x20, struct rttst_irqbench_config)
+
+#define RTTST_RTIOC_IRQBENCH_STOP \
+ _IO(RTIOC_TYPE_TESTING, 0x21)
+
+#define RTTST_RTIOC_IRQBENCH_GET_STATS \
+ _IOR(RTIOC_TYPE_TESTING, 0x22, struct rttst_irqbench_stats)
+
+#define RTTST_RTIOC_IRQBENCH_WAIT_IRQ \
+ _IO(RTIOC_TYPE_TESTING, 0x23)
+
+#define RTTST_RTIOC_IRQBENCH_REPLY_IRQ \
+ _IO(RTIOC_TYPE_TESTING, 0x24)
+
+
#define RTTST_RTIOC_SWTEST_SET_TASKS_COUNT \
_IOW(RTIOC_TYPE_TESTING, 0x30, unsigned long)
Index: xenomai/configure.in
===================================================================
--- xenomai.orig/configure.in
+++ xenomai/configure.in
@@ -82,6 +82,7 @@ case "$host" in
esac
AC_MSG_RESULT([$XENO_TARGET_ARCH])
+AM_CONDITIONAL(XENO_TARGET_ARCH_I386,[test $XENO_TARGET_ARCH = i386])
dnl
dnl Parse options
@@ -602,6 +603,7 @@ AC_CONFIG_FILES([ \
src/testsuite/switchbench/Makefile \
src/testsuite/cyclic/Makefile \
src/testsuite/switchtest/Makefile \
+ src/testsuite/irqbench/Makefile \
include/Makefile \
include/asm-generic/Makefile \
include/asm-blackfin/Makefile \
Index: xenomai/ksrc/drivers/Makefile
===================================================================
--- xenomai.orig/ksrc/drivers/Makefile
+++ xenomai/ksrc/drivers/Makefile
@@ -11,7 +11,7 @@ else
subdir-$(CONFIG_XENO_DRIVERS_16550A) += 16550A
subdir-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += testing
-
+subdir-$(CONFIG_XENO_DRIVERS_IRQBENCH) += testing
subdir-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += testing
include $(TOPDIR)/Rules.make
Index: xenomai/ksrc/drivers/testing/Kconfig
===================================================================
--- xenomai.orig/ksrc/drivers/testing/Kconfig
+++ xenomai/ksrc/drivers/testing/Kconfig
@@ -6,10 +6,19 @@ config XENO_DRIVERS_TIMERBENCH
Kernel-based benchmark driver for timer latency evaluation.
See testsuite/latency for a possible front-end.
+config XENO_DRIVERS_IRQBENCH
+ depends on XENO_SKIN_RTDM
+ tristate "IRQ benchmark driver"
+ default n
+ help
+ Loopback driver for IRQ latency evaluation over serial or parallel
+ port links. Additionally requires user-space helper and a logging tool
+ (see testsuite/irqbench).
+
config XENO_DRIVERS_SWITCHTEST
- depends on XENO_SKIN_RTDM
- tristate "Context switch unit testing driver"
- default n
- help
- Kernel-based driver for unit testing context switches and
- FPU switches.
+ depends on XENO_SKIN_RTDM
+ tristate "Context switch unit testing driver"
+ default n
+ help
+ Kernel-based driver for unit testing context switches and
+ FPU switches.
Index: xenomai/ksrc/drivers/testing/Makefile
===================================================================
--- xenomai.orig/ksrc/drivers/testing/Makefile
+++ xenomai/ksrc/drivers/testing/Makefile
@@ -5,11 +5,13 @@ ifeq ($(PATCHLEVEL),6)
EXTRA_CFLAGS += -Iinclude/xenomai
obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o
-
+obj-$(CONFIG_XENO_DRIVERS_IRQBENCH) += xeno_irqbench.o
obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o
xeno_timerbench-y := timerbench.o
+xeno_irqbench-y := irqbench.o
+
xeno_switchtest-y := switchtest.o
EXTRA_CFLAGS += -Iinclude/xenomai
@@ -21,14 +23,16 @@ else
O_TARGET := built-in.o
obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o
-
+obj-$(CONFIG_XENO_DRIVERS_IRQBENCH) += xeno_irqbench.o
obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o
xeno_timerbench-objs := timerbench.o
+xeno_irqbench-objs := irqbench.o
+
xeno_switchtest-objs := switchtest.o
-export-objs := $(xeno_timerbench-objs) $(xeno_switchtest-objs)
+export-objs := $(xeno_timerbench-objs) $(xeno_irqbench-objs) $(xeno_switchtest-objs)
EXTRA_CFLAGS += -I$(TOPDIR)/include/xenomai -I$(TOPDIR)/include/xenomai/compat
@@ -37,6 +41,9 @@ include $(TOPDIR)/Rules.make
xeno_timerbench.o: $(xeno_timerbench-objs)
$(LD) -r -o $@ $(xeno_timerbench-objs)
+xeno_irqbench.o: $(xeno_irqbench-objs)
+ $(LD) -r -o $@ $(xeno_irqbench-objs)
+
xeno_switchtest.o: $(xeno_switchtest-objs)
$(LD) -r -o $@ $(xeno_switchtest-objs)
Index: xenomai/ksrc/drivers/testing/irqbench.c
===================================================================
--- /dev/null
+++ xenomai/ksrc/drivers/testing/irqbench.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2006 Jan Kiszka <jan.kiszka@domain.hid>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Xenomai is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <asm/semaphore.h>
+
+#include <rtdm/rttesting.h>
+#include <rtdm/rtdm_driver.h>
+#include <nucleus/trace.h>
+
+/* --- Serial port --- */
+
+#define MSR_DCTS 0x01
+#define MSR_DDSR 0x02
+#define MSR_DDCD 0x08
+
+#define MCR_RTS 0x02
+#define MCR_OUT2 0x08
+
+#define IER_MODEM 0x08
+
+#define RHR(ctx) (ctx->port_ioaddr + 0) /* Receive Holding Buffer */
+#define IER(ctx) (ctx->port_ioaddr + 1) /* Interrupt Enable Register */
+#define IIR(ctx) (ctx->port_ioaddr + 2) /* Interrupt Id Register */
+#define LCR(ctx) (ctx->port_ioaddr + 3) /* Line Control Register */
+#define MCR(ctx) (ctx->port_ioaddr + 4) /* Modem Control Register */
+#define LSR(ctx) (ctx->port_ioaddr + 5) /* Line Status Register */
+#define MSR(ctx) (ctx->port_ioaddr + 6) /* Modem Status Register */
+
+/* --- Parallel port --- */
+
+#define CTRL_INIT 0x04
+
+#define STAT_STROBE 0x10
+
+#define DATA(ctx) (ctx->port_ioaddr + 0) /* Data register */
+#define STAT(ctx) (ctx->port_ioaddr + 1) /* Status register */
+#define CTRL(ctx) (ctx->port_ioaddr + 2) /* Control register */
+
+struct rt_irqbench_context {
+ int mode;
+ int port_type;
+ unsigned long port_ioaddr;
+ unsigned int toggle;
+ struct rttst_irqbench_stats stats;
+ rtdm_irq_t irq_handle;
+ rtdm_event_t irq_event;
+ rtdm_task_t irq_task;
+ struct semaphore nrt_mutex;
+};
+
+static unsigned int start_index;
+
+module_param(start_index, uint, 0400);
+MODULE_PARM_DESC(start_index, "First device instance number to be used");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("jan.kiszka@domain.hid");
+
+
+static inline int rt_irqbench_check_irq(struct rt_irqbench_context *ctx)
+{
+ int status;
+
+
+ switch (ctx->port_type) {
+ case RTTST_IRQBENCH_SERPORT:
+ status = inb(MSR(ctx));
+ if (status & (MSR_DDSR | MSR_DDCD))
+ xntrace_user_freeze(0, 0);
+ if (!(status & MSR_DCTS))
+ return 0;
+ break;
+
+ case RTTST_IRQBENCH_PARPORT:
+ // todo
+ break;
+ }
+ ctx->stats.irqs_received++;
+ return 1;
+}
+
+
+static inline void rt_irqbench_hwreply(struct rt_irqbench_context *ctx)
+{
+ switch (ctx->port_type) {
+ case RTTST_IRQBENCH_SERPORT:
+ /* toggle RTS */
+ ctx->toggle ^= MCR_RTS;
+ outb(ctx->toggle, MCR(ctx));
+ break;
+
+ case RTTST_IRQBENCH_PARPORT:
+ ctx->toggle ^= 0xFF;
+ outb(ctx->toggle, DATA(ctx));
+ break;
+ }
+ xntrace_special(0xBE, 0);
+ ctx->stats.irqs_acknowledged++;
+}
+
+
+static void rt_irqbench_task(void *arg)
+{
+ struct rt_irqbench_context *ctx = (struct rt_irqbench_context *)arg;
+
+
+ while (1) {
+ if (rtdm_event_wait(&ctx->irq_event) < 0)
+ return;
+ rt_irqbench_hwreply(ctx);
+ }
+}
+
+
+static int rt_irqbench_task_irq(rtdm_irq_t *irq_handle)
+{
+ struct rt_irqbench_context *ctx;
+
+
+ ctx = rtdm_irq_get_arg(irq_handle, struct rt_irqbench_context);
+
+ if (rt_irqbench_check_irq(ctx))
+ rtdm_event_signal(&ctx->irq_event);
+
+ return RTDM_IRQ_HANDLED;
+}
+
+
+static int rt_irqbench_direct_irq(rtdm_irq_t *irq_handle)
+{
+ struct rt_irqbench_context *ctx;
+
+
+ ctx = rtdm_irq_get_arg(irq_handle, struct rt_irqbench_context);
+
+ if (rt_irqbench_check_irq(ctx))
+ rt_irqbench_hwreply(ctx);
+
+ return RTDM_IRQ_HANDLED;
+}
+
+
+static int rt_irqbench_stop(struct rt_irqbench_context *ctx)
+{
+ if (ctx->mode < 0)
+ return -EINVAL;
+
+ /* Disable hardware */
+ switch (ctx->port_type) {
+ case RTTST_IRQBENCH_SERPORT:
+ outb(0, IER(ctx));
+ break;
+
+ case RTTST_IRQBENCH_PARPORT:
+ outb(0, CTRL(ctx));
+ break;
+ }
+
+ rtdm_irq_free(&ctx->irq_handle);
+
+ if (ctx->mode == RTTST_IRQBENCH_KERNEL_TASK)
+ rtdm_task_destroy(&ctx->irq_task);
+
+ ctx->mode = -1;
+
+ return 0;
+}
+
+
+static int rt_irqbench_open(struct rtdm_dev_context *context,
+ rtdm_user_info_t *user_info, int oflags)
+{
+ struct rt_irqbench_context *ctx;
+
+
+ ctx = (struct rt_irqbench_context *)context->dev_private;
+
+ ctx->mode = -1;
+ rtdm_event_init(&ctx->irq_event, 0);
+ init_MUTEX(&ctx->nrt_mutex);
+
+ return 0;
+}
+
+
+static int rt_irqbench_close(struct rtdm_dev_context *context,
+ rtdm_user_info_t *user_info)
+{
+ struct rt_irqbench_context *ctx;
+
+
+ ctx = (struct rt_irqbench_context *)context->dev_private;
+
+ down(&ctx->nrt_mutex);
+ rt_irqbench_stop(ctx);
+ rtdm_event_destroy(&ctx->irq_event);
+ up(&ctx->nrt_mutex);
+
+ return 0;
+}
+
+
+static int rt_irqbench_ioctl_nrt(struct rtdm_dev_context *context,
+ rtdm_user_info_t *user_info, int request,
+ void *arg)
+{
+ struct rt_irqbench_context *ctx;
+ int ret = 0;
+
+
+ ctx = (struct rt_irqbench_context *)context->dev_private;
+
+ switch (request) {
+ case RTTST_RTIOC_IRQBENCH_START: {
+ struct rttst_irqbench_config config_buf;
+ struct rttst_irqbench_config *config;
+
+ config = (struct rttst_irqbench_config *)arg;
+ if (user_info) {
+ if (!rtdm_read_user_ok(user_info, arg,
+ sizeof(struct rttst_irqbench_config)) ||
+ rtdm_copy_from_user(user_info, &config_buf, arg,
+ sizeof(struct rttst_irqbench_config)))
+ return -EFAULT;
+
+ config = &config_buf;
+ }
+
+ if (config->port_type > RTTST_IRQBENCH_PARPORT)
+ return -EINVAL;
+
+ down(&ctx->nrt_mutex);
+
+ if (test_bit(RTDM_CLOSING, &context->context_flags))
+ goto unlock_start_out;
+
+ ctx->port_type = config->port_type;
+ ctx->port_ioaddr = config->port_ioaddr;
+
+ /* Initialise hardware */
+ switch (ctx->port_type) {
+ case RTTST_IRQBENCH_SERPORT:
+ ctx->toggle = MCR_OUT2;
+
+ /* Reset DLAB, reset RTS, enable OUT2 */
+ outb(0, LCR(ctx));
+ outb(MCR_OUT2, MCR(ctx));
+
+ /* Mask all UART interrupts and clear pending ones. */
+ outb(0, IER(ctx));
+ inb(IIR(ctx));
+ inb(LSR(ctx));
+ inb(RHR(ctx));
+ inb(MSR(ctx));
+ break;
+
+ case RTTST_IRQBENCH_PARPORT:
+ ctx->toggle = 0xAA;
+ outb(0xAA, DATA(ctx));
+ outb(CTRL_INIT, CTRL(ctx));
+ break;
+ }
+
+ switch (config->mode) {
+ case RTTST_IRQBENCH_USER_TASK:
+ ret = rtdm_irq_request(&ctx->irq_handle, config->port_irq,
+ rt_irqbench_task_irq, 0,
+ "irqbench", ctx);
+ break;
+
+ case RTTST_IRQBENCH_KERNEL_TASK:
+ ret = rtdm_irq_request(&ctx->irq_handle, config->port_irq,
+ rt_irqbench_task_irq, 0,
+ "irqbench", ctx);
+ if (ret < 0)
+ goto unlock_start_out;
+
+ ret = rtdm_task_init(&ctx->irq_task, "irqbench",
+ rt_irqbench_task, ctx,
+ config->priority, 0);
+ if (ret < 0)
+ rtdm_irq_free(&ctx->irq_handle);
+ break;
+
+ case RTTST_IRQBENCH_HANDLER:
+ ret = rtdm_irq_request(&ctx->irq_handle, config->port_irq,
+ rt_irqbench_direct_irq, 0,
+ "irqbench", ctx);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0)
+ goto unlock_start_out;
+
+ ctx->mode = config->mode;
+
+ memset(&ctx->stats, 0, sizeof(ctx->stats));
+
+ rtdm_irq_enable(&ctx->irq_handle);
+
+ /* Arm IRQ */
+ switch (ctx->port_type) {
+ case RTTST_IRQBENCH_SERPORT:
+ outb(IER_MODEM, IER(ctx));
+ break;
+
+ case RTTST_IRQBENCH_PARPORT:
+ outb(STAT_STROBE, CTRL(ctx));
+ break;
+ }
+
+ unlock_start_out:
+ up(&ctx->nrt_mutex);
+ break;
+ }
+
+ case RTTST_RTIOC_IRQBENCH_STOP:
+ down(&ctx->nrt_mutex);
+ ret = rt_irqbench_stop(ctx);
+ up(&ctx->nrt_mutex);
+ break;
+
+ case RTTST_RTIOC_IRQBENCH_GET_STATS: {
+ struct rttst_irqbench_stats *usr_stats;
+
+ usr_stats = (struct rttst_irqbench_stats *)arg;
+
+ if (user_info) {
+ if (!rtdm_rw_user_ok(user_info, usr_stats,
+ sizeof(struct rttst_irqbench_stats)) ||
+ rtdm_copy_to_user(user_info, usr_stats,
+ &ctx->stats,
+ sizeof(struct rttst_irqbench_stats)))
+ ret = -EFAULT;
+ } else
+ *usr_stats = ctx->stats;
+ break;
+ }
+
+ case RTTST_RTIOC_IRQBENCH_WAIT_IRQ:
+ ret = -ENOSYS;
+ break;
+
+ case RTTST_RTIOC_IRQBENCH_REPLY_IRQ:
+ rt_irqbench_hwreply(ctx);
+ break;
+
+ default:
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
+
+static int rt_irqbench_ioctl_rt(struct rtdm_dev_context *context,
+ rtdm_user_info_t *user_info, int request,
+ void *arg)
+{
+ struct rt_irqbench_context *ctx;
+ int ret = 0;
+
+
+ ctx = (struct rt_irqbench_context *)context->dev_private;
+
+ switch (request) {
+ case RTTST_RTIOC_IRQBENCH_WAIT_IRQ:
+ ret = rtdm_event_wait(&ctx->irq_event);
+ break;
+
+ case RTTST_RTIOC_IRQBENCH_REPLY_IRQ:
+ rt_irqbench_hwreply(ctx);
+ break;
+
+ case RTTST_RTIOC_IRQBENCH_START:
+ case RTTST_RTIOC_IRQBENCH_STOP:
+ case RTTST_RTIOC_IRQBENCH_GET_STATS:
+ ret = -ENOSYS;
+ break;
+
+ default:
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
+
+static struct rtdm_device device = {
+ struct_version: RTDM_DEVICE_STRUCT_VER,
+
+ device_flags: RTDM_NAMED_DEVICE,
+ context_size: sizeof(struct rt_irqbench_context),
+ device_name: "",
+
+ open_rt: NULL,
+ open_nrt: rt_irqbench_open,
+
+ ops: {
+ close_rt: NULL,
+ close_nrt: rt_irqbench_close,
+
+ ioctl_rt: rt_irqbench_ioctl_rt,
+ ioctl_nrt: rt_irqbench_ioctl_nrt,
+
+ read_rt: NULL,
+ read_nrt: NULL,
+
+ write_rt: NULL,
+ write_nrt: NULL,
+
+ recvmsg_rt: NULL,
+ recvmsg_nrt: NULL,
+
+ sendmsg_rt: NULL,
+ sendmsg_nrt: NULL,
+ },
+
+ device_class: RTDM_CLASS_TESTING,
+ device_sub_class: RTDM_SUBCLASS_IRQBENCH,
+ driver_name: "xeno_irqbench",
+ driver_version: RTDM_DRIVER_VER(0, 1, 0),
+ peripheral_name: "IRQ Latency Benchmark",
+ provider_name: "Jan Kiszka",
+ proc_name: device.device_name,
+};
+
+int __init __timerbench_init(void)
+{
+ int ret;
+
+ do {
+ snprintf(device.device_name, RTDM_MAX_DEVNAME_LEN, "rttest%d",
+ start_index);
+ ret = rtdm_dev_register(&device);
+
+ start_index++;
+ } while (ret == -EEXIST);
+
+ return ret;
+}
+
+
+void __exit __timerbench_exit(void)
+{
+ rtdm_dev_unregister(&device, 1000);
+}
+
+
+module_init(__timerbench_init);
+module_exit(__timerbench_exit);
Index: xenomai/src/testsuite/Makefile.am
===================================================================
--- xenomai.orig/src/testsuite/Makefile.am
+++ xenomai/src/testsuite/Makefile.am
@@ -1 +1 @@
-SUBDIRS = latency switchbench cyclic switchtest
+SUBDIRS = latency switchbench cyclic switchtest irqbench
Index: xenomai/src/testsuite/irqbench/Makefile.am
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/irqbench/Makefile.am
@@ -0,0 +1,47 @@
+testdir = $(prefix)/testsuite/irqbench
+
+test_PROGRAMS = irqloop
+
+if XENO_TARGET_ARCH_I386
+test_PROGRAMS += irqbench
+endif
+
+
+irqloop_SOURCES = irqloop.c
+
+irqloop_CPPFLAGS = \
+ -I$(top_srcdir)/include/posix \
+ $(XENO_USER_CFLAGS) \
+ -I$(top_srcdir)/include
+
+irqloop_LDFLAGS = \
+ $(XENO_POSIX_WRAPPERS) \
+ $(XENO_USER_LDFLAGS)
+
+irqloop_LDADD = \
+ -lpthread \
+ ../../skins/posix/.libs/libpthread_rt.a
+
+
+irqbench_SOURCES = irqbench.c
+
+irqbench_CPPFLAGS = \
+ $(XENO_USER_CFLAGS) \
+ -I$(top_srcdir)/include
+
+irqbench_LDFLAGS = $(XENO_USER_LDFLAGS)
+
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(testdir)
+ $(INSTALL_DATA) $(srcdir)/runinfo $(DESTDIR)$(testdir)/.runinfo
+ @echo "\$${DESTDIR}$(prefix)/bin/xeno-load \$$*" > $(DESTDIR)$(testdir)/run
+ @chmod +x $(DESTDIR)$(testdir)/run
+
+uninstall-local:
+ $(RM) $(DESTDIR)$(testdir)/.runinfo $(DESTDIR)$(testdir)/run
+
+run: all
+ @$(top_srcdir)/scripts/xeno-load --verbose
+
+EXTRA_DIST = runinfo
Index: xenomai/src/testsuite/irqbench/irqbench.c
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/irqbench/irqbench.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2006 Jan Kiszka <jan.kiszka@domain.hid>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Xenomai is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <sys/mman.h>
+
+
+#define SERPORT 0
+#define PARPORT 1
+
+/* --- Serial port --- */
+
+#define MCR_DTR 0x01
+#define MCR_RTS 0x02
+#define MCR_OUT2 0x08
+
+#define MSR_DELTA 0x0F
+
+#define LCR(base) (base + 3) /* Line Control Register */
+#define MCR(base) (base + 4) /* Modem Control Register */
+#define LSR(base) (base + 5) /* Line Status Register */
+#define MSR(base) (base + 6) /* Modem Status Register */
+
+/* --- Parallel port --- */
+
+#define CTRL_INIT 0x04
+
+#define STAT_STROBE 0x10
+
+#define DATA(base) (base + 0) /* Data register */
+#define STAT(base) (base + 1) /* Status register */
+#define CTRL(base) (base + 2) /* Control register */
+
+double tsc2ns_scale;
+long long min_lat = LONG_MAX;
+long long max_lat = LONG_MIN;
+long long avg_lat = 0;
+long outer_loops = 0;
+int warmup = 1;
+int terminate = 0;
+
+static inline long long rdtsc(void)
+{
+ unsigned long long tsc;
+
+ __asm__ __volatile__("rdtsc" : "=A" (tsc));
+ return tsc;
+}
+
+
+static long tsc2ns(long long tsc)
+{
+ if ((tsc > LONG_MAX) || (tsc < LONG_MIN)) {
+ fprintf(stderr, "irqbench: overflow (%lld ns)!\n",
+ (long long)(tsc2ns_scale * (double)tsc));
+ exit(2);
+ }
+ return (long)(tsc2ns_scale * (double)tsc);
+}
+
+
+static inline long long ns2tsc(long long ns)
+{
+ return (long long)(((double)ns) / tsc2ns_scale);
+}
+
+
+void calibrate_tsc(void)
+{
+ FILE *proc;
+ char *lineptr = NULL;
+ size_t len;
+ double cpu_mhz;
+
+ proc = fopen("/proc/cpuinfo", "r");
+ if (proc == NULL) {
+ perror("irqbench: Unable to open /proc/cpuinfo");
+ exit(1);
+ }
+
+ while (getline(&lineptr, &len, proc) != -1)
+ if (strncmp(lineptr, "cpu MHz", 7) == 0) {
+ sscanf(strchr(lineptr, ':') + 1, "%lf", &cpu_mhz);
+ break;
+ }
+
+ if (lineptr)
+ free(lineptr);
+ fclose(proc);
+
+ printf("CPU frequency: %.3lf MHz\n", cpu_mhz);
+
+ tsc2ns_scale = 1000.0 / cpu_mhz;
+}
+
+
+void sighand(int signal)
+{
+ if (warmup)
+ exit(0);
+ else
+ terminate = 1;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int port_type = SERPORT;
+ unsigned long port_ioaddr = 0x3F8;
+ long long period = 100000;
+ long long timeout;
+ long long start, delay;
+ unsigned int toggle;
+ int trigger_trace = 0;
+ int c;
+
+
+ signal(SIGINT, sighand);
+ signal(SIGTERM, sighand);
+ signal(SIGHUP, sighand);
+ signal(SIGALRM, sighand);
+
+ calibrate_tsc();
+
+ while ((c = getopt(argc,argv,"p:T:o:a:f")) != EOF)
+ switch (c) {
+ case 'p':
+ period = atoi(optarg) * 1000;
+ break;
+
+ case 'T':
+ alarm(atoi(optarg));
+ break;
+
+ case 'o':
+ port_type = atoi(optarg);
+ break;
+
+ case 'a':
+ port_ioaddr = strtol(optarg, NULL,
+ (strncmp(optarg, "0x", 2) == 0) ? 16 : 10);
+ break;
+
+ case 'f':
+ trigger_trace = 1;
+ break;
+
+ default:
+ fprintf(stderr, "usage: irqbench [options]\n"
+ " [-p <period_us>] # signal period, default=100 us\n"
+ " [-T <test_duration_seconds>] # default=0, so ^C to end\n"
+ " [-o <port_type>] # 0=serial (default), 1=parallel\n"
+ " [-a <port_io_address>] # default=0x3f8\n"
+ " [-f] # freeze trace for each new max latency\n");
+ exit(2);
+ }
+
+ if (iopl(3) < 0) {
+ fprintf(stderr, "irqbench: superuser permissions required\n");
+ exit(1);
+ }
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+
+ switch (port_type) {
+ case SERPORT:
+ toggle = MCR_OUT2;
+ inb(MSR(port_ioaddr));
+ break;
+
+ case PARPORT:
+ toggle = 0xAA;
+ outb(0xAA, DATA(port_ioaddr));
+ outb(CTRL_INIT, CTRL(port_ioaddr));
+ break;
+
+ default:
+ fprintf(stderr, "irqbench: invalid port type\n");
+ exit(1);
+ }
+
+ period = ns2tsc(period);
+
+ printf("Port type: %s\n"
+ "Port address: 0x%lx\n\n",
+ (port_type == SERPORT) ? "serial" : "parallel", port_ioaddr);
+
+ printf("Waiting on target...\n");
+
+ while (1)
+ if (port_type == SERPORT) {
+ toggle ^= MCR_RTS;
+ outb(toggle, MCR(port_ioaddr));
+ usleep(100000);
+ if ((inb(MSR(port_ioaddr)) & MSR_DELTA) != 0)
+ break;
+ } else {
+ int status = inb(STAT(port_ioaddr));
+
+ toggle ^= 0xFF;
+ outb(toggle, DATA(port_ioaddr));
+ if (inb(STAT(port_ioaddr)) != status)
+ break;
+ }
+
+ printf("Warming up...\n");
+
+ while (!terminate) {
+ long long loop_timeout = rdtsc() + ns2tsc(1000000000LL);
+ long loop_avg = 0;
+ int inner_loops;
+
+ for (inner_loops = 0; rdtsc() < loop_timeout; inner_loops++) {
+ long lat;
+
+ __asm__ __volatile__("cli");
+
+ if (port_type == SERPORT) {
+ start = rdtsc();
+
+ toggle ^= MCR_RTS;
+ outb(toggle, MCR(port_ioaddr));
+
+ timeout = start + period * 100;
+ while (((inb(MSR(port_ioaddr)) & MSR_DELTA) == 0) &&
+ (rdtsc() < timeout));
+
+ delay = rdtsc() - start;
+ } else {
+ int status = inb(STAT(port_ioaddr));
+
+ start = rdtsc();
+
+ toggle ^= 0xFF;
+ outb(toggle, DATA(port_ioaddr));
+
+ timeout = start + period * 100;
+ while ((inb(STAT(port_ioaddr)) == status) &&
+ (rdtsc() < timeout));
+
+ delay = rdtsc() - start;
+ }
+
+ if (!warmup) {
+ lat = tsc2ns(delay);
+
+ loop_avg += lat;
+ if (lat < min_lat)
+ min_lat = lat;
+ if (lat > max_lat) {
+ max_lat = lat;
+ if (trigger_trace) {
+ if (port_type == SERPORT) {
+ toggle ^= MCR_DTR;
+ outb(toggle, MCR(port_ioaddr));
+ } else {
+ // todo
+ }
+ }
+ }
+ }
+
+ __asm__ __volatile__("sti");
+
+ while (rdtsc() < start + period);
+ }
+ if (!warmup && !terminate) {
+ loop_avg /= inner_loops;
+
+ printf("%.3f / %.3f / %.3f us\n",
+ ((double)min_lat) / 1000.0, ((double)loop_avg) / 1000.0,
+ ((double)max_lat) / 1000.0);
+
+ avg_lat += loop_avg;
+ outer_loops++;
+ } else
+ warmup = 0;
+ }
+
+ avg_lat /= outer_loops;
+ printf("---\n%.3f / %.3f / %.3f us\n",
+ ((double)min_lat) / 1000.0, ((double)avg_lat) / 1000.0,
+ ((double)max_lat) / 1000.0);
+
+ return 0;
+}
Index: xenomai/src/testsuite/irqbench/irqloop.c
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/irqbench/irqloop.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2006 Jan Kiszka <jan.kiszka@domain.hid>.
+ *
+ * Xenomai is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Xenomai is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Xenomai; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <rtdm/rttesting.h>
+
+static int benchdev;
+static int terminate;
+
+void *irq_thread(void *arg)
+{
+ struct sched_param param = { .sched_priority = (int)arg };
+
+ pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m);
+
+ while (1) {
+ if (ioctl(benchdev, RTTST_RTIOC_IRQBENCH_WAIT_IRQ) ||
+ ioctl(benchdev, RTTST_RTIOC_IRQBENCH_REPLY_IRQ))
+ break;
+ }
+
+ param.sched_priority = 0;
+ pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m);
+
+ return NULL;
+}
+
+
+void sighand(int sig)
+{
+ terminate = 1;
+}
+
+
+int main(int argc, char *argv[])
+{
+ const char *mode_name[] =
+ { "user-space task", "kernel-space task", "IRQ handler" };
+ const char *port_type_name[] = { "serial", "parallel" };
+ char devname[RTDM_MAX_DEVNAME_LEN];
+ int benchdev_no = 0;
+ struct rttst_irqbench_config config = {
+ mode: RTTST_IRQBENCH_USER_TASK,
+ priority: sched_get_priority_max(SCHED_FIFO),
+ calibration_loops: 0,
+ port_type: RTTST_IRQBENCH_SERPORT,
+ port_ioaddr: 0x3f8,
+ port_irq: 4
+ };
+ struct rttst_irqbench_stats stats;
+ unsigned long long last_received = 0;
+ pthread_t thr;
+ int c;
+
+
+ while ((c = getopt(argc,argv,"D:t:P:o:a:i:")) != EOF)
+ switch (c) {
+ case 'D':
+ benchdev_no = atoi(optarg);
+ break;
+
+ case 't':
+ config.mode = atoi(optarg);
+ break;
+
+ case 'P':
+ config.priority = atoi(optarg);
+ break;
+
+ case 'o':
+ config.port_type = atoi(optarg);
+ break;
+
+ case 'a':
+ config.port_ioaddr = strtol(optarg, NULL,
+ (strncmp(optarg, "0x", 2) == 0) ? 16 : 10);
+ break;
+
+ case 'i':
+ config.port_irq = atoi(optarg);
+ break;
+
+ default:
+ fprintf(stderr, "usage: irqloop [options]\n"
+ " [-D <testing_device_no>] # number of testing device, default=0\n"
+ " [-t <test_mode>] # 0=user task (default), 1=kernel task, 2=IRQ\n"
+ " [-P <priority>] # task priority (test mode 0 and 1 only)\n"
+ " [-o <port_type>] # 0=serial (default), 1=parallel\n"
+ " [-a <port_io_address>] # default=0x3f8\n"
+ " [-i <port_irq>] # default=4\n");
+ exit(2);
+ }
+
+ signal(SIGINT, sighand);
+ signal(SIGTERM, sighand);
+ signal(SIGHUP, sighand);
+
+ mlockall(MCL_CURRENT|MCL_FUTURE);
+
+ snprintf(devname, RTDM_MAX_DEVNAME_LEN, "/dev/rttest%d", benchdev_no);
+ benchdev = open(devname, O_RDWR);
+ if (benchdev < 0) {
+ perror("irqloop: failed to open benchmark device");
+ fprintf(stderr, "(modprobe xeno_irqbench?)\n");
+ return 1;
+ }
+
+ if (config.mode == RTTST_IRQBENCH_USER_TASK) {
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
+
+ pthread_create(&thr, &attr, irq_thread, (void *)config.priority);
+ }
+
+ if (ioctl(benchdev, RTTST_RTIOC_IRQBENCH_START, &config)) {
+ perror("irqloop: error starting test");
+ goto cleanup;
+ }
+
+ printf("Test mode: %s\n"
+ "Port type: %s\n"
+ "Port address: 0x%lx\n"
+ "Port IRQ: %d\n\n\n\n",
+ mode_name[config.mode], port_type_name[config.port_type],
+ config.port_ioaddr, config.port_irq);
+
+ while (!terminate) {
+ if (ioctl(benchdev, RTTST_RTIOC_IRQBENCH_GET_STATS, &stats) < 0) {
+ perror("irqloop: error reading stats");
+ break;
+ }
+
+ if ((last_received > 0) && (stats.irqs_received == last_received))
+ break; /* timed out */
+ last_received = stats.irqs_received;
+
+ printf("\033[2AReceived IRQs: %lld\nAcknowledged IRQs: %lld\n",
+ stats.irqs_received, stats.irqs_acknowledged);
+ usleep(250000);
+ }
+
+ ioctl(benchdev, RTTST_RTIOC_IRQBENCH_STOP);
+
+ cleanup:
+ close(benchdev);
+ if (config.mode == RTTST_IRQBENCH_USER_TASK) {
+ pthread_cancel(thr);
+ pthread_join(thr, NULL);
+ }
+
+ return 0;
+}
Index: xenomai/src/testsuite/irqbench/runinfo
===================================================================
--- /dev/null
+++ xenomai/src/testsuite/irqbench/runinfo
@@ -0,0 +1 @@
+irqloop:posix+rtdm+irqbench:!./irqloop;popall:control_c
Index: xenomai/doc/txt/Makefile.am
===================================================================
--- xenomai.orig/doc/txt/Makefile.am
+++ xenomai/doc/txt/Makefile.am
@@ -2,6 +2,7 @@ txtdocdir = $(datadir)/doc/$(PACKAGE)-$(
dist_txtdoc_DATA = \
16550A-driver.txt \
+ irqbench.txt \
pse51-skin.txt \
psos-skin.txt \
rtai-skin.txt \
Index: xenomai/doc/txt/irqbench.txt
===================================================================
--- /dev/null
+++ xenomai/doc/txt/irqbench.txt
@@ -0,0 +1,51 @@
+IRQ Latency Benchmark
+---------------------
+
+This latency benchmark for external interrupts consists of three parts:
+
+1. xeno_irqbench (ksrc/drivers/testing)
+ RTDM driver for the test target to handle and reply to the IRQ events, or
+ forward them to user-space.
+
+2. irqloop (src/testsuite/irqbench)
+ xeno_irqbench control front-end and user-space loop-closer. Runs against
+ the POSIX skin on the test target.
+
+3. irqbench (src/testsuite/irqbench)
+ IRQ triggering and reaction measuring tool for the log system. Runs over
+ plain Linux on x86 hosts. Note that this tool will only be compiled
+ automatically with Xenomai if the target architecture is x86. To compile
+ it manually, invoke
+
+ <gcc-x86-compiler> irqbench.c -o irqbench -O2
+
+To link both test and log system, a null-modem cable between 8250-compatible
+two RS232 ports is required. The infrastructure for parallel port cross-over
+is prepared as well, but yet untested and incomplete. The null-modem cable
+must provide at least the following links:
+
+ test target log system
+ ----------- ----------
+ CTS(8) <------ RTS(7) IRQ trigger
+ RTS(7) ------> CTS(8) reply
+
+ DCD(1)
+ and/or <------ DTR(4) trace trigger (optional)
+ DSR(6)
+
+ (Pin number on 9-pin sub-D socket)
+
+Test and log system can be started in arbitrary order, they will wait on
+each other.
+
+The log system runs each single IRQ measurement with local IRQs disabled. It
+will wait up to 100 times the current period on the test system's reply (after
+initial successful synchronisation, which happens with IRQs enabled). Note that
+these timeouts may disturb other processes or drivers on the log system.
+
+Keep in mind for test evaluations that, by design, irqbench will not trigger a
+new IRQ event before the reply on the previous one has arrived or timed out.
+Thus the specified test period may be dynamically expended in case of
+overload. But maximum a latency close or above the period will clearly
+indicate this.
+
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 249 bytes --]
next prev parent reply other threads:[~2006-06-28 19:39 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-06-26 17:21 [Xenomai-core] [PATCH 0/6] Various refactoring and new IRQ test jan.kiszka
2006-06-26 17:21 ` [Xenomai-core] [PATCH 1/6] Refactor tracer API jan.kiszka
2006-06-26 17:21 ` [Xenomai-core] [PATCH 2/6] Improve fault report jan.kiszka
2006-06-28 7:42 ` Philippe Gerum
2006-06-28 7:51 ` Jan Kiszka
2006-06-28 8:04 ` Philippe Gerum
2006-06-28 8:18 ` Jan Kiszka
2006-06-28 8:36 ` Philippe Gerum
2006-06-28 8:51 ` Jan Kiszka
2006-06-28 9:00 ` Philippe Gerum
2006-06-28 9:17 ` Jan Kiszka
2006-06-28 16:36 ` Philippe Gerum
2006-06-26 17:21 ` [Xenomai-core] [PATCH 3/6] Refactor rttesting device interface jan.kiszka
2006-06-26 17:21 ` [Xenomai-core] [PATCH 4/6] Add prio switch to latency test jan.kiszka
2006-06-28 19:38 ` Jan Kiszka
2006-06-26 17:21 ` [Xenomai-core] [PATCH 5/6] Overread dev-prefix on posix open jan.kiszka
2006-06-28 19:38 ` Jan Kiszka
2006-06-26 17:21 ` [Xenomai-core] [PATCH 6/6] Introduce IRQ latency benchmark jan.kiszka
2006-06-27 16:45 ` Jan Kiszka
2006-06-28 12:11 ` Gilles Chanteperdrix
2006-06-28 12:28 ` Jan Kiszka
2006-06-28 12:35 ` Gilles Chanteperdrix
2006-06-28 13:42 ` Gilles Chanteperdrix
2006-06-28 14:14 ` Dmitry Adamushko
2006-06-28 14:37 ` Jan Kiszka
2006-06-28 15:18 ` Dmitry Adamushko
2006-06-28 14:44 ` Jan Kiszka
2006-06-28 19:39 ` Jan Kiszka [this message]
2006-06-29 11:20 ` Jan Kiszka
2006-07-01 15:38 ` Philippe Gerum
2006-07-01 18:17 ` Jan Kiszka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=44A2DB06.8050000@domain.hid \
--to=jan.kiszka@domain.hid \
--cc=xenomai@xenomai.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.