Hi all,

this is fully working proposal how to re-enable in-kernel timer latency
benchmarks.

More precisely, it adds a new RTDM device "rtbenchmark<X>" (and also a
new RTDM class) which can execute either a kernel task or timer
periodically. The benchmark device generates all the usual latency data
which can be retrieved from userspace via IOCTLs. I patched the existing
latency tool to open the device and read the data from there instead of
running its own latency task.

README for a quick test:

 o apply patch and rebuild everything (don't forget to re-prepare the
   kernel and also call scripts/bootstrap, I added some files)

 o load xeno_timerbench (+ xeno_native, xeno_rtdm, ...)

 o run "latency -D0" to start in-kernel timer task test on device
   "rtbenchmark0"

 o run "latency -D0 -t" to start in-kernel timer handler test (i.e.
   without scheduling latency) on device "rtbenchmark0"

This is rather fresh code, handle with care! ;) Moreover, I would like
to hear your comments if the extension of the latency tool is the right
way to got or if we better split things up. The problem I see is that
this patch makes latency depend on xeno_rtdm being loaded.

Jan


PS: I likely broke src/testsuite/latency/runinfo.
Index: include/rtdm/rtbenchmark.h
===================================================================
--- include/rtdm/rtbenchmark.h	(Revision 0)
+++ include/rtdm/rtbenchmark.h	(Revision 0)
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Real-Time Driver Model for Xenomai, benchmark device profile header
+ *
+ * @note Copyright (C) 2005 Jan Kiszka <[EMAIL PROTECTED]>
+ *
+ * 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.
+ *
+ * @ingroup rtbenchmark
+ */
+
+/*!
+ * @ingroup profiles
+ * @defgroup rtbenchmark Benchmark Devices
+ *
+ * This group of devices is intended to provide in-kernel benchmark results.
+ * Feel free to comment on this profile via the Xenomai mailing list
+ * (Xenomai-help@gna.org) or directly to the author ([EMAIL PROTECTED]). @n
+ * @n
+ *
+ * @par Device Characteristics
+ * @ref rtdm_device.device_flags "Device Flags": @c RTDM_NAMED_DEVICE, @c RTDM_EXCLUSIVE @n
+ * @n
+ * @ref rtdm_device.device_name "Device Name": @c "rtbenchmark<N>", N >= 0 @n
+ * @n
+ * @ref rtdm_device.device_class "Device Class": @c RTDM_CLASS_BENCHMARK @n
+ * @n
+ *
+ * @par Supported Operations
+ * @b Open @n
+ * Environments: non-RT (RT optional)@n
+ * Specific return values: none @n
+ * @n
+ * @b Close @n
+ * Environments: non-RT (RT optional)@n
+ * Specific return values: none @n
+ * @n
+ * @b IOCTL @n
+ * Mandatory Environments: see @ref IOCTLs "below" @n
+ * Specific return values: see @ref IOCTLs "below" @n
+ *
+ * @{
+ */
+
+#ifndef _RTBENCHMARK_H
+#define _RTBENCHMARK_H
+
+#include <asm/types.h>
+#include <xenomai/rtdm/rtdm.h>
+
+#define RTBNCH_TIMER_TASK       0
+#define RTBNCH_TIMER_HANDLER    1
+
+typedef struct rtbnch_result {
+    long                    avg;
+    long                    min;
+    long                    max;
+    long                    overruns;
+    long                    test_loops;
+} rtbnch_result_t;
+
+typedef struct rtbnch_timerconfig {
+    int                     mode;
+    uint64_t                period;
+    int                     warmup_loops;
+    int                     histogram_size;
+    int                     histogram_bucketsize;
+} rtbnch_timerconfig_t;
+
+typedef struct rtbnch_interm_result {
+    struct rtbnch_result    last;
+    struct rtbnch_result    overall;
+} rtbnch_interm_result_t;
+
+typedef struct rtbnch_overall_result {
+    struct rtbnch_result    result;
+    long                    *histogram_avg;
+    long                    *histogram_min;
+    long                    *histogram_max;
+} rtbnch_overall_result_t;
+
+
+#define RTIOC_TYPE_BENCHMARK        RTDM_CLASS_BENCHMARK
+
+
+/*!
+ * @name Sub-Classes of RTDM_CLASS_BENCHMARK
+ * @{ */
+#define RTDM_SUBCLASS_TIMER         0
+/** @} */
+
+
+/*!
+ * @anchor IOCTLs @name IOCTLs
+ * Benchmark device IOCTLs
+ * @{ */
+
+#define RTBNCH_RTIOC_INTERM_RESULT      \
+    _IOWR(RTIOC_TYPE_BENCHMARK, 0x00, struct rtbnch_interm_result)
+
+#define RTBNCH_RTIOC_START_TMTEST       \
+    _IOW(RTIOC_TYPE_BENCHMARK, 0x10, struct rtbnch_timerconfig)
+
+#define RTBNCH_RTIOC_STOP_TMTEST        \
+    _IOWR(RTIOC_TYPE_BENCHMARK, 0x11, struct rtbnch_overall_result)
+
+/** @} */
+
+#endif /* _RTBENCHMARK_H */
Index: include/rtdm/rtdm.h
===================================================================
--- include/rtdm/rtdm.h	(Revision 288)
+++ include/rtdm/rtdm.h	(Arbeitskopie)
@@ -68,6 +68,7 @@
 #define RTDM_CLASS_CAN              3
 #define RTDM_CLASS_NETWORK          4
 #define RTDM_CLASS_RTMAC            5
+#define RTDM_CLASS_BENCHMARK        6
 /*
 #define RTDM_CLASS_USB              ?
 #define RTDM_CLASS_FIREWIRE         ?
Index: src/testsuite/latency/latency.c
===================================================================
--- src/testsuite/latency/latency.c	(Revision 288)
+++ src/testsuite/latency/latency.c	(Arbeitskopie)
@@ -11,6 +11,7 @@
 #include <xenomai/native/task.h>
 #include <xenomai/native/timer.h>
 #include <xenomai/native/sem.h>
+#include <xenomai/rtdm/rtbenchmark.h>
 
 RT_TASK latency_task, display_task;
 
@@ -29,6 +30,9 @@
 int test_duration = 0;  /* sec of testing, via -T <sec>, 0 is inf */
 int data_lines = 21;    /* data lines per header line, -l <lines> to change */
 int quiet = 0;          /* suppress printing of RTH, RTD lines when -T given */
+int benchdev_no = -1;
+int benchdev = -1;
+int test_mode = RTBNCH_TIMER_TASK;
 
 time_t test_start, test_end;    /* report test duration */
 int test_loops = 0;             /* outer loop count */
@@ -60,14 +64,6 @@
     RTIME expected_tsc, period_tsc, start_ticks;
     RT_TIMER_INFO timer_info;
 
-    err = rt_timer_start(TM_ONESHOT);
-
-    if (err)
-        {
-        fprintf(stderr,"latency: cannot start timer, code %d\n",err);
-        return;
-        }
-
     err = rt_timer_inquire(&timer_info);
     
     if (err)
@@ -156,14 +152,33 @@
     int err, n = 0;
     time_t start;
 
-    err = rt_sem_create(&display_sem,"dispsem",0,S_FIFO);
+    if (benchdev >= 0) {
+        struct rtbnch_timerconfig   config;
 
-    if (err)
-        {
-        fprintf(stderr,"latency: cannot create semaphore: %s\n",strerror(-err));
-        return;
-        }
+        config.mode                 = test_mode;
+        config.period               = period_ns;
+        config.warmup_loops         = WARMUP_TIME;
+        config.histogram_size       = (do_histogram || do_stats) ? histogram_size : 0;
+        config.histogram_bucketsize = bucketsize;
 
+        err = rt_dev_ioctl(benchdev, RTBNCH_RTIOC_START_TMTEST, &config);
+
+        if (err)
+            {
+            fprintf(stderr,"latency: failed to start in-kernel timer benchmark, code %d\n",err);
+            return;
+            }
+
+    } else {
+        err = rt_sem_create(&display_sem,"dispsem",0,S_FIFO);
+
+        if (err)
+            {
+            fprintf(stderr,"latency: cannot create semaphore: %s\n",strerror(-err));
+            return;
+            }
+    }
+
     time(&start);
 
     if (quiet)
@@ -172,23 +187,46 @@
     for (;;)
         {
         long minj, gminj, maxj, gmaxj, avgj;
-        err = rt_sem_p(&display_sem,TM_INFINITE);
 
-        if (err)
-            {
-            if (err != -EIDRM)
-                fprintf(stderr,"latency: failed to pend on semaphore, code %d\n",err);
+        if (benchdev >= 0) {
+            struct rtbnch_interm_result result;
 
-            rt_task_delete(NULL);
-            }
+            err = rt_dev_ioctl(benchdev, RTBNCH_RTIOC_INTERM_RESULT, &result);
 
-        /* convert jitters to nanoseconds. */
-        minj = rt_timer_tsc2ns(minjitter);
-        gminj = rt_timer_tsc2ns(gminjitter);
-        avgj = rt_timer_tsc2ns(avgjitter);
-        maxj = rt_timer_tsc2ns(maxjitter);
-        gmaxj = rt_timer_tsc2ns(gmaxjitter);
+            if (err)
+                {
+                if (err != -EIDRM)
+                    fprintf(stderr,"latency: failed to call RTBNCH_RTIOC_INTERM_RESULT, code %d\n",err);
 
+                return;
+                }
+
+            minj = result.last.min;
+            gminj = result.overall.min;
+            avgj = result.last.avg;
+            maxj = result.last.max;
+            gmaxj = result.overall.max;
+            goverrun = result.overall.overruns;
+
+        } else {
+            err = rt_sem_p(&display_sem,TM_INFINITE);
+
+            if (err)
+                {
+                if (err != -EIDRM)
+                    fprintf(stderr,"latency: failed to pend on semaphore, code %d\n",err);
+
+                return;
+                }
+
+            /* convert jitters to nanoseconds. */
+            minj = rt_timer_tsc2ns(minjitter);
+            gminj = rt_timer_tsc2ns(gminjitter);
+            avgj = rt_timer_tsc2ns(avgjitter);
+            maxj = rt_timer_tsc2ns(maxjitter);
+            gmaxj = rt_timer_tsc2ns(gmaxjitter);
+        }
+
         if (!quiet)
             {
             if (data_lines && (n++ % data_lines)==0)
@@ -295,20 +333,38 @@
 
     finished = 1;
     rt_timer_stop();
-    rt_sem_delete(&display_sem);
 
+    if (benchdev >= 0) {
+        struct rtbnch_overall_result overall;
+
+        overall.histogram_min = histogram_min;
+        overall.histogram_max = histogram_max;
+        overall.histogram_avg = histogram_avg;
+
+        rt_dev_ioctl(benchdev, RTBNCH_RTIOC_STOP_TMTEST, &overall);
+        rt_dev_close(benchdev);
+
+        gminj    = overall.result.min;
+        gmaxj    = overall.result.max;
+        gavgj    = overall.result.avg;
+        goverrun = overall.result.overruns;
+    } else {
+        rt_sem_delete(&display_sem);
+
+        gavgjitter /= (test_loops > 1 ? test_loops : 2)-1;
+
+        gminj = rt_timer_tsc2ns(gminjitter);
+        gmaxj = rt_timer_tsc2ns(gmaxjitter);
+        gavgj = rt_timer_tsc2ns(gavgjitter);
+    }
+
     if (do_histogram || do_stats)
         dump_hist_stats();
 
     time(&test_end);
     actual_duration = test_end - test_start - WARMUP_TIME;
     if (!test_duration) test_duration = actual_duration;
-    gavgjitter /= (test_loops > 1 ? test_loops : 2)-1;
 
-    gminj = rt_timer_tsc2ns(gminjitter);
-    gmaxj = rt_timer_tsc2ns(gmaxjitter);
-    gavgj = rt_timer_tsc2ns(gavgjitter);
-
     printf("---|------------|------------|------------|--------|-------------------------\n"
            "RTS|%12ld|%12ld|%12ld|%8ld|    %.2ld:%.2ld:%.2ld/%.2d:%.2d:%.2d\n",
            gminj,
@@ -333,7 +389,7 @@
 {
     int c, err;
 
-    while ((c = getopt(argc,argv,"hp:l:T:qH:B:s")) != EOF)
+    while ((c = getopt(argc,argv,"hp:l:T:qH:B:sD:t")) != EOF)
         switch (c)
             {
             case 'h':
@@ -377,6 +433,16 @@
                 quiet = 1;
                 break;
 
+            case 'D':
+
+                benchdev_no = atoi(optarg);
+                break;
+
+            case 't':
+
+                test_mode = RTBNCH_TIMER_HANDLER;
+                break;
+
             default:
 
                 fprintf(stderr, "usage: latency [options]\n"
@@ -387,7 +453,9 @@
                         "  [-p <period_us>]             # sampling period\n"
                         "  [-l <data-lines per header>] # default=21, 0 to supress headers\n"
                         "  [-T <test_duration_seconds>] # default=0, so ^C to end\n"
-                        "  [-q]                         # supresses RTD, RTH lines if -T is used\n");
+                        "  [-q]                         # supresses RTD, RTH lines if -T is used\n"
+                        "  [-D <benchmark_device_no>]   # use in-kernel benchmark of given device\n"
+                        "  [-t]                         # run in-kernel timer handler test, default: task\n");
                 exit(2);
             }
 
@@ -397,6 +465,12 @@
         quiet = 0;
         }
 
+    if ((benchdev_no < 0) && (test_mode == RTBNCH_TIMER_HANDLER))
+        {
+        fprintf(stderr, "latency: -t only works if -D has been given.\n");
+        exit(2);
+        }
+
     time(&test_start);
 
     histogram_avg = calloc(histogram_size, sizeof(long));
@@ -420,38 +494,61 @@
 
     mlockall(MCL_CURRENT|MCL_FUTURE);
 
-    err = rt_task_create(&display_task,"display",0,98,0);
+    err = rt_timer_start(TM_ONESHOT);
 
     if (err)
         {
-        fprintf(stderr,"latency: failed to create display task, code %d\n",err);
+        fprintf(stderr,"latency: cannot start timer, code %d\n",err);
         return 0;
         }
 
-    err = rt_task_start(&display_task,&display,NULL);
+    if (benchdev_no >= 0) {
+        char devname[RTDM_MAX_DEVNAME_LEN];
 
-    if (err)
-        {
-        fprintf(stderr,"latency: failed to start display task, code %d\n",err);
-        return 0;
-        }
+        snprintf(devname, RTDM_MAX_DEVNAME_LEN, "rtbenchmark%d", benchdev_no);
+        benchdev = rt_dev_open(devname, O_RDWR);
 
-    err = rt_task_create(&latency_task,"sampling",0,99,T_FPU);
+        if (benchdev)
+            {
+            fprintf(stderr,"latency: failed to open benchmark device, code %d\n",benchdev);
+            return 0;
+            }
+    }
 
+    err = rt_task_create(&display_task,"display",0,98,0);
+
     if (err)
         {
-        fprintf(stderr,"latency: failed to create latency task, code %d\n",err);
+        fprintf(stderr,"latency: failed to create display task, code %d\n",err);
         return 0;
         }
 
-    err = rt_task_start(&latency_task,&latency,NULL);
+    err = rt_task_start(&display_task,&display,NULL);
 
     if (err)
         {
-        fprintf(stderr,"latency: failed to start latency task, code %d\n",err);
+        fprintf(stderr,"latency: failed to start display task, code %d\n",err);
         return 0;
         }
 
+    if (benchdev_no < 0) {
+        err = rt_task_create(&latency_task,"sampling",0,99,T_FPU);
+
+        if (err)
+            {
+            fprintf(stderr,"latency: failed to create latency task, code %d\n",err);
+            return 0;
+            }
+
+        err = rt_task_start(&latency_task,&latency,NULL);
+
+        if (err)
+            {
+            fprintf(stderr,"latency: failed to start latency task, code %d\n",err);
+            return 0;
+            }
+    }
+
     pause();
 
     return 0;
Index: src/testsuite/latency/Makefile.am
===================================================================
--- src/testsuite/latency/Makefile.am	(Revision 288)
+++ src/testsuite/latency/Makefile.am	(Arbeitskopie)
@@ -10,6 +10,7 @@
 
 latency_LDADD = \
 	../../skins/native/libnative.la \
+	../../skins/rtdm/librtdm.la \
 	-lpthread -lm
 
 install-data-local:
Index: ksrc/drivers/Kconfig
===================================================================
--- ksrc/drivers/Kconfig	(Revision 288)
+++ ksrc/drivers/Kconfig	(Arbeitskopie)
@@ -1 +1,2 @@
 source "drivers/xenomai/16550A/Kconfig"
+source "drivers/xenomai/benchmark/Kconfig"
Index: ksrc/drivers/Config.in
===================================================================
--- ksrc/drivers/Config.in	(Revision 288)
+++ ksrc/drivers/Config.in	(Arbeitskopie)
@@ -8,5 +8,6 @@
 mainmenu_option next_comment
 comment 'Real-time drivers'
 	source drivers/xenomai/16550A/Config.in
+	source drivers/xenomai/benchmark/Config.in
 endmenu
 fi
Index: ksrc/drivers/benchmark/Kconfig
===================================================================
--- ksrc/drivers/benchmark/Kconfig	(Revision 0)
+++ ksrc/drivers/benchmark/Kconfig	(Revision 0)
@@ -0,0 +1,7 @@
+config XENO_DRIVERS_TIMERBENCH
+	depends on XENO_SKIN_RTDM
+	tristate "Timer benchmark driver"
+	default n
+	help
+	Kernel-based benchmark driver for timer latency evaluation.
+	See testsuite/latency for a possible front-end.
Index: ksrc/drivers/benchmark/timerbench.c
===================================================================
--- ksrc/drivers/benchmark/timerbench.c	(Revision 0)
+++ ksrc/drivers/benchmark/timerbench.c	(Revision 0)
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2005 Jan Kiszka <[EMAIL PROTECTED]>.
+ *
+ * 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 <xenomai/rtdm/rtbenchmark.h>
+#include <xenomai/rtdm/rtdm_driver.h>
+
+struct rt_tmbench_context {
+    int                         mode;
+    unsigned long               period;
+    int                         warmup_loops;
+    int                         samples_per_sec;
+    long                        *histogram_min;
+    long                        *histogram_max;
+    long                        *histogram_avg;
+    int                         histogram_size;
+    int                         bucketsize;
+
+    rtdm_task_t                 timer_task;
+
+    xntimer_t                   timer;
+    int                         warmup;
+    uint64_t                    start_time;
+    uint64_t                    date;
+    struct rtbnch_result        curr;
+
+    rtdm_event_t                result_event;
+    struct rtbnch_interm_result result;
+
+    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("[EMAIL PROTECTED]");
+
+
+static inline void add_histogram(struct rt_tmbench_context *ctx,
+                                 long *histogram, long addval)
+{
+    /* bucketsize steps */
+    long inabs = (addval >= 0 ? addval : -addval) / ctx->bucketsize;
+    histogram[inabs < ctx->histogram_size ? inabs : ctx->histogram_size-1]++;
+}
+
+
+void eval_inner_loop(struct rt_tmbench_context *ctx, long dt)
+{
+    if (ctx->date <= ctx->start_time)
+        ctx->curr.overruns++;
+
+    if (dt > ctx->curr.max)
+        ctx->curr.max = dt;
+    if (dt < ctx->curr.min)
+        ctx->curr.min = dt;
+    ctx->curr.avg += dt;
+
+    ctx->date += ctx->period;
+
+    if (!ctx->warmup && ctx->histogram_size)
+        add_histogram(ctx, ctx->histogram_avg, dt);
+}
+
+
+void eval_outer_loop(struct rt_tmbench_context *ctx)
+{
+    if (!ctx->warmup) {
+        if (ctx->histogram_size) {
+            add_histogram(ctx, ctx->histogram_max, ctx->curr.max);
+            add_histogram(ctx, ctx->histogram_min, ctx->curr.min);
+        }
+
+        ctx->result.last.min = ctx->curr.min;
+        if (ctx->curr.min < ctx->result.overall.min)
+            ctx->result.overall.min = ctx->curr.min;
+
+        ctx->result.last.max = ctx->curr.max;
+        if (ctx->curr.max > ctx->result.overall.max)
+            ctx->result.overall.max = ctx->curr.max;
+
+        ctx->result.last.avg = ctx->curr.avg / ctx->samples_per_sec;
+        ctx->result.overall.avg      += ctx->result.last.avg;
+        ctx->result.overall.overruns += ctx->curr.overruns;
+        rtdm_event_pulse(&ctx->result_event);
+    }
+
+    if (ctx->warmup &&
+        (ctx->result.overall.test_loops == ctx->warmup_loops)) {
+        ctx->result.overall.test_loops = 0;
+        ctx->warmup = 0;
+    }
+
+    ctx->curr.min        = 10000000;
+    ctx->curr.max        = -10000000;
+    ctx->curr.avg        = 0;
+    ctx->curr.overruns   = 0;
+
+    ctx->result.overall.test_loops++;
+}
+
+
+void timer_task_proc(void *arg)
+{
+    struct rt_tmbench_context   *ctx = (struct rt_tmbench_context *)arg;
+    int                         count;
+
+
+    /* start time: one millisecond from now. */
+    ctx->date = rtdm_clock_read() + 1000000;
+
+    while (1) {
+        int err;
+
+
+        for (count = 0; count < ctx->samples_per_sec; count++) {
+            RTDM_EXECUTE_ATOMICALLY(
+                ctx->start_time = rtdm_clock_read();
+                err = rtdm_task_sleep_until(ctx->date);
+            );
+
+            if (err)
+                return;
+
+            eval_inner_loop(ctx, (long)(rtdm_clock_read() - ctx->date));
+        }
+        eval_outer_loop(ctx);
+    }
+}
+
+
+void timer_proc(void *arg)
+{
+    struct rt_tmbench_context   *ctx = (struct rt_tmbench_context *)arg;
+
+
+    eval_inner_loop(ctx, (long)(rtdm_clock_read() - ctx->date));
+
+    ctx->start_time = rtdm_clock_read();
+    /* FIXME: convert to RTDM timers */
+    xntimer_start(&ctx->timer, xnpod_ns2ticks(ctx->date-ctx->start_time),
+                  XN_INFINITE);
+
+    if (++ctx->curr.test_loops < ctx->samples_per_sec)
+        return;
+
+    ctx->curr.test_loops = 0;
+    eval_outer_loop(ctx);
+}
+
+
+int rt_tmbench_open(struct rtdm_dev_context *context,
+                    rtdm_user_info_t *user_info, int oflags)
+{
+    struct rt_tmbench_context   *ctx;
+
+
+    ctx = (struct rt_tmbench_context *)context->dev_private;
+
+    ctx->mode = -1;
+    init_MUTEX(&ctx->nrt_mutex);
+
+    return 0;
+}
+
+
+int rt_tmbench_close(struct rtdm_dev_context *context,
+                     rtdm_user_info_t *user_info)
+{
+    struct rt_tmbench_context   *ctx;
+
+
+    ctx = (struct rt_tmbench_context *)context->dev_private;
+
+    down(&ctx->nrt_mutex);
+
+    if (ctx->mode >= 0) {
+        if (ctx->mode == RTBNCH_TIMER_TASK)
+            rtdm_task_destroy(&ctx->timer_task);
+        else
+            /* FIXME: convert to RTDM timers */
+            xntimer_destroy(&ctx->timer);
+
+        rtdm_event_destroy(&ctx->result_event);
+
+        if (ctx->histogram_size)
+            kfree(ctx->histogram_min);
+
+        ctx->mode           = -1;
+        ctx->histogram_size = 0;
+    }
+
+    up(&ctx->nrt_mutex);
+
+    return 0;
+}
+
+
+int rt_tmbench_ioctl_nrt(struct rtdm_dev_context *context,
+                         rtdm_user_info_t *user_info, int request, void *arg)
+{
+    struct rt_tmbench_context   *ctx;
+    int                         ret = 0;
+
+
+    ctx = (struct rt_tmbench_context *)context->dev_private;
+
+    switch (request) {
+        case RTBNCH_RTIOC_START_TMTEST: {
+            struct rtbnch_timerconfig   config_buf;
+            struct rtbnch_timerconfig   *config;
+
+
+            config = (struct rtbnch_timerconfig *)arg;
+            if (user_info) {
+                if (!rtdm_read_user_ok(user_info, arg,
+                                       sizeof(struct rtbnch_timerconfig)) ||
+                    rtdm_copy_from_user(user_info, &config_buf, arg,
+                                        sizeof(struct rtbnch_timerconfig)))
+                    return -EFAULT;
+
+                config = &config_buf;
+            }
+
+            down(&ctx->nrt_mutex);
+
+            ctx->period          = config->period;
+            ctx->warmup_loops    = config->warmup_loops;
+            ctx->samples_per_sec = 1000000000 / ctx->period;
+            ctx->histogram_size  = config->histogram_size;
+
+            if (ctx->histogram_size > 0) {
+                ctx->histogram_min =
+                    kmalloc(3 * ctx->histogram_size * sizeof(long),
+                            GFP_KERNEL);
+                ctx->histogram_max =
+                    ctx->histogram_min + config->histogram_size;
+                ctx->histogram_avg =
+                    ctx->histogram_max + config->histogram_size;
+
+                if (!ctx->histogram_min) {
+                    up(&ctx->nrt_mutex);
+                    return -ENOMEM;
+                }
+
+                memset(ctx->histogram_min, 0,
+                       3 * ctx->histogram_size * sizeof(long));
+                ctx->bucketsize = config->histogram_bucketsize;
+            }
+
+            ctx->result.overall.min        = 10000000;
+            ctx->result.overall.max        = -10000000;
+            ctx->result.overall.avg        = 0;
+            ctx->result.overall.test_loops = 1;
+            ctx->result.overall.overruns   = 0;
+
+            ctx->warmup = 1;
+
+            ctx->curr.min        = 10000000;
+            ctx->curr.max        = -10000000;
+            ctx->curr.avg        = 0;
+            ctx->curr.overruns   = 0;
+
+            rtdm_event_init(&ctx->result_event, 0);
+
+            if (config->mode == RTBNCH_TIMER_TASK) {
+                if (!test_bit(RTDM_CLOSING, &context->context_flags)) {
+                    ctx->mode = RTBNCH_TIMER_TASK;
+                    ret = rtdm_task_init(&ctx->timer_task, "timerbench",
+                                         timer_task_proc, ctx,
+                                         RTDM_TASK_HIGHEST_PRIORITY, 0);
+                }
+            } else {
+                /* FIXME: convert to RTDM timers */
+                xntimer_init(&ctx->timer, timer_proc, ctx);
+
+                ctx->curr.test_loops = 0;
+
+                if (!test_bit(RTDM_CLOSING, &context->context_flags)) {
+                    ctx->mode = RTBNCH_TIMER_HANDLER;
+                    RTDM_EXECUTE_ATOMICALLY(
+                        /* start time: one millisecond from now. */
+                        ctx->start_time = rtdm_clock_read() + 1000000;
+                        ctx->date       = ctx->start_time + ctx->period;
+
+                        /* FIXME: convert to RTDM timers */
+                        xntimer_start(&ctx->timer,
+                                xnpod_ns2ticks(ctx->date-rtdm_clock_read()),
+                                XN_INFINITE);
+                    );
+                }
+            }
+
+            up(&ctx->nrt_mutex);
+
+            break;
+        }
+
+        case RTBNCH_RTIOC_STOP_TMTEST: {
+            struct rtbnch_overall_result *usr_res;
+
+
+            usr_res = (struct rtbnch_overall_result *)arg;
+
+            down(&ctx->nrt_mutex);
+
+            if (ctx->mode < 0) {
+                up(&ctx->nrt_mutex);
+                return -EINVAL;
+            }
+
+            if (ctx->mode == RTBNCH_TIMER_TASK)
+                rtdm_task_destroy(&ctx->timer_task);
+            else
+                /* FIXME: convert to RTDM timers */
+                xntimer_destroy(&ctx->timer);
+
+            rtdm_event_destroy(&ctx->result_event);
+
+            ctx->mode = -1;
+
+            ctx->result.overall.avg /=
+                    ((ctx->result.overall.test_loops) > 1 ?
+                     ctx->result.overall.test_loops : 2) - 1;
+
+            if (user_info) {
+                if (!rtdm_rw_user_ok(user_info, usr_res,
+                                     sizeof(struct rtbnch_overall_result)) ||
+                    rtdm_copy_to_user(user_info, &usr_res->result,
+                                      &ctx->result.overall,
+                                      sizeof(struct rtbnch_result)))
+                    ret = -EFAULT;
+            } else {
+                memcpy(&usr_res->result, &ctx->result.overall,
+                       sizeof(struct rtbnch_result));
+            }
+
+            if (ctx->histogram_size) {
+                int size = ctx->histogram_size * sizeof(long);
+
+                if (user_info) {
+                    if (!rtdm_rw_user_ok(user_info, usr_res->histogram_min,
+                                         size) ||
+                        rtdm_copy_to_user(user_info, usr_res->histogram_min,
+                                          ctx->histogram_min, size) ||
+                        !rtdm_rw_user_ok(user_info, usr_res->histogram_max,
+                                         size) ||
+                        rtdm_copy_to_user(user_info, usr_res->histogram_max,
+                                          ctx->histogram_max, size) ||
+                        !rtdm_rw_user_ok(user_info, usr_res->histogram_avg,
+                                         size) ||
+                        rtdm_copy_to_user(user_info, usr_res->histogram_avg,
+                                          ctx->histogram_avg, size))
+                        ret = -EFAULT;
+                } else {
+                    memcpy(usr_res->histogram_min, ctx->histogram_min, size);
+                    memcpy(usr_res->histogram_max, ctx->histogram_max, size);
+                    memcpy(usr_res->histogram_avg, ctx->histogram_avg, size);
+                }
+
+                kfree(ctx->histogram_min);
+            }
+
+            up(&ctx->nrt_mutex);
+
+            break;
+        }
+
+        case RTBNCH_RTIOC_INTERM_RESULT:
+            ret = -ENOSYS;
+            break;
+
+        default:
+            ret = -ENOTTY;
+    }
+
+    return ret;
+}
+
+
+int rt_tmbench_ioctl_rt(struct rtdm_dev_context *context,
+                        rtdm_user_info_t *user_info, int request, void *arg)
+{
+    struct rt_tmbench_context   *ctx;
+    int                         ret = 0;
+
+
+    ctx = (struct rt_tmbench_context *)context->dev_private;
+
+    switch (request) {
+        case RTBNCH_RTIOC_INTERM_RESULT: {
+            struct rtbnch_interm_result *usr_res;
+
+
+            usr_res = (struct rtbnch_interm_result *)arg;
+
+            ret = rtdm_event_wait(&ctx->result_event);
+            if (ret < 0)
+                return ret;
+
+            if (user_info) {
+                if (!rtdm_rw_user_ok(user_info, usr_res,
+                                     sizeof(struct rtbnch_interm_result)) ||
+                    rtdm_copy_to_user(user_info, usr_res, &ctx->result,
+                                      sizeof(struct rtbnch_interm_result)))
+                    ret = -EFAULT;
+            } else
+                memcpy(usr_res, &ctx->result,
+                       sizeof(struct rtbnch_interm_result));
+
+            break;
+        }
+
+        case RTBNCH_RTIOC_START_TMTEST:
+        case RTBNCH_RTIOC_STOP_TMTEST:
+            ret = -ENOSYS;
+            break;
+
+        default:
+            ret = -ENOTTY;
+    }
+
+    return ret;
+}
+
+
+static struct rtdm_device device = {
+    struct_version:     RTDM_DEVICE_STRUCT_VER,
+
+    device_flags:       RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
+    context_size:       sizeof(struct rt_tmbench_context),
+    device_name:        "",
+
+    open_rt:            NULL,
+    open_nrt:           rt_tmbench_open,
+
+    ops: {
+        close_rt:       NULL,
+        close_nrt:      rt_tmbench_close,
+
+        ioctl_rt:       rt_tmbench_ioctl_rt,
+        ioctl_nrt:      rt_tmbench_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_BENCHMARK,
+    device_sub_class:   RTDM_SUBCLASS_TIMER,
+    driver_name:        "xeno_timerbench",
+    driver_version:     RTDM_DRIVER_VER(0, 1, 0),
+    peripheral_name:    "Timer Latency Benchmark",
+    provider_name:      "Jan Kiszka",
+    proc_name:          device.device_name,
+};
+
+int __init init_module(void)
+{
+    snprintf(device.device_name, RTDM_MAX_DEVNAME_LEN, "rtbenchmark%d",
+             start_index);
+
+    return rtdm_dev_register(&device);
+}
+
+
+void cleanup_module(void)
+{
+    rtdm_dev_unregister(&device, 1000);
+}
Index: ksrc/drivers/benchmark/Config.in
===================================================================
--- ksrc/drivers/benchmark/Config.in	(Revision 0)
+++ ksrc/drivers/benchmark/Config.in	(Revision 0)
@@ -0,0 +1,5 @@
+#
+# Xenomai configuration for Linux v2.4
+#
+
+dep_tristate 'Timer benchmark driver' CONFIG_XENO_DRIVERS_TIMERBENCH $CONFIG_XENO_SKIN_RTDM
Index: ksrc/drivers/benchmark/Makefile
===================================================================
--- ksrc/drivers/benchmark/Makefile	(Revision 0)
+++ ksrc/drivers/benchmark/Makefile	(Revision 0)
@@ -0,0 +1,30 @@
+ifeq ($(PATCHLEVEL),6)
+
+# Makefile frag for Linux v2.6
+
+obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o
+
+xeno_timerbench-y := timerbench.o
+
+else
+
+# Makefile frag for Linux v2.4
+
+O_TARGET := built-in.o
+
+obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) := xeno_timerbench.o
+
+list-multi := xeno_timerbench.o
+
+xeno_timerbench-objs := timerbench.o
+
+export-objs := $(xeno_timerbench-objs)
+
+EXTRA_CFLAGS += -I$(TOPDIR)/include/xenomai/compat
+
+include $(TOPDIR)/Rules.make
+
+xeno_timerbench.o: $(xeno_timerbench-objs)
+	$(LD) -r -o $@ $(xeno_timerbench-objs)
+
+endif
Index: ksrc/drivers/Makefile
===================================================================
--- ksrc/drivers/Makefile	(Revision 288)
+++ ksrc/drivers/Makefile	(Arbeitskopie)
@@ -2,7 +2,7 @@
 
 # Makefile frag for Linux v2.6
 
-obj-$(CONFIG_XENOMAI) += 16550A/
+obj-$(CONFIG_XENOMAI) += 16550A/ benchmark/
 
 else
 
@@ -10,6 +10,8 @@
 
 subdir-$(CONFIG_XENO_DRIVERS_16550A) += 16550A
 
+subdir-$(CONFIG_XENO_DRIVERS_benchmark) += benchmark
+
 include $(TOPDIR)/Rules.make
 
 endif

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to