All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
@ 2021-02-20  5:53 hongzha1
  2021-02-20  5:53 ` [PATCH 2/5] cobalt/rtdm: add interface to init timer " hongzha1
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: hongzha1 @ 2021-02-20  5:53 UTC (permalink / raw)
  To: xenomai

Initialise and start a real-time task on specified cpu

Signed-off-by: hongzha1 <hongzhan.chen@intel.com>

diff --git a/include/cobalt/kernel/rtdm/driver.h b/include/cobalt/kernel/rtdm/driver.h
index 733c49df8..6cdace324 100644
--- a/include/cobalt/kernel/rtdm/driver.h
+++ b/include/cobalt/kernel/rtdm/driver.h
@@ -1024,6 +1024,9 @@ typedef void (*rtdm_task_proc_t)(void *arg);
 int rtdm_task_init(rtdm_task_t *task, const char *name,
 		   rtdm_task_proc_t task_proc, void *arg,
 		   int priority, nanosecs_rel_t period);
+int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
+		rtdm_task_proc_t task_proc, void *arg,
+		int priority, nanosecs_rel_t period);
 int __rtdm_task_sleep(xnticks_t timeout, xntmode_t mode);
 void rtdm_task_busy_sleep(nanosecs_rel_t delay);
 
diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
index b914fa312..7452850b7 100644
--- a/kernel/cobalt/rtdm/drvlib.c
+++ b/kernel/cobalt/rtdm/drvlib.c
@@ -157,6 +157,79 @@ int rtdm_task_init(rtdm_task_t *task, const char *name,
 
 EXPORT_SYMBOL_GPL(rtdm_task_init);
 
+/**
+ * @brief Initialise and start a real-time task on specified cpu
+ *
+ * After initialising a task, the task handle remains valid and can be
+ * passed to RTDM services until either rtdm_task_destroy() or
+ * rtdm_task_join() was invoked.
+ *
+ * @param[in,out] task Task handle
+ * @param[in] cpu that task want to run on
+ * @param[in] name Optional task name
+ * @param[in] task_proc Procedure to be executed by the task
+ * @param[in] arg Custom argument passed to @c task_proc() on entry
+ * @param[in] priority Priority of the task, see also
+ * @ref rtdmtaskprio "Task Priority Range"
+ * @param[in] period Period in nanoseconds of a cyclic task, 0 for non-cyclic
+ * mode. Waiting for the first and subsequent periodic events is
+ * done using rtdm_task_wait_period().
+ *
+ * @return 0 on success, otherwise negative error code
+ *
+ * @coretags{secondary-only, might-switch}
+ */
+int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
+		rtdm_task_proc_t task_proc, void *arg,
+		int priority, nanosecs_rel_t period)
+{
+	union xnsched_policy_param param;
+	struct xnthread_start_attr sattr;
+	struct xnthread_init_attr iattr;
+	int err;
+
+	if (!realtime_core_enabled())
+		return -ENOSYS;
+
+	iattr.name = name;
+	iattr.flags = 0;
+	iattr.personality = &xenomai_personality;
+	iattr.affinity = *cpumask_of(cpu);
+	param.rt.prio = priority;
+
+	err = xnthread_init(task, &iattr, &xnsched_class_rt, &param);
+	if (err)
+		return err;
+
+	/* We need an anonymous registry entry to obtain a handle for fast
+	 * mutex locking.
+	 */
+	err = xnthread_register(task, "");
+	if (err)
+		goto cleanup_out;
+
+	if (period > 0) {
+		err = xnthread_set_periodic(task, XN_INFINITE,
+					    XN_RELATIVE, period);
+		if (err)
+			goto cleanup_out;
+	}
+
+	sattr.mode = 0;
+	sattr.entry = task_proc;
+	sattr.cookie = arg;
+	err = xnthread_start(task, &sattr);
+	if (err)
+		goto cleanup_out;
+
+	return 0;
+
+cleanup_out:
+	xnthread_cancel(task);
+	return err;
+}
+EXPORT_SYMBOL_GPL(rtdm_task_init_on_cpu);
+
 #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
 /**
  * @brief Destroy a real-time task
-- 
2.17.1



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

* [PATCH 2/5] cobalt/rtdm: add interface to init timer on specified cpu
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
@ 2021-02-20  5:53 ` hongzha1
  2021-02-20  5:53 ` [PATCH 3/5] rtdm/testing: latmus: introduce latmus driver hongzha1
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: hongzha1 @ 2021-02-20  5:53 UTC (permalink / raw)
  To: xenomai

Initialise timer on specified cpu

Signed-off-by: hongzha1 <hongzhan.chen@intel.com>

diff --git a/include/cobalt/kernel/rtdm/driver.h b/include/cobalt/kernel/rtdm/driver.h
index 6cdace324..6a431e7c5 100644
--- a/include/cobalt/kernel/rtdm/driver.h
+++ b/include/cobalt/kernel/rtdm/driver.h
@@ -966,6 +966,9 @@ enum rtdm_timer_mode {
 int rtdm_timer_init(rtdm_timer_t *timer, rtdm_timer_handler_t handler,
 		    const char *name);
 
+int rtdm_timer_init_on_cpu(rtdm_timer_t *timer, rtdm_timer_handler_t handler,
+		    const char *name, int cpu);
+
 void rtdm_timer_destroy(rtdm_timer_t *timer);
 
 int rtdm_timer_start(rtdm_timer_t *timer, nanosecs_abs_t expiry,
diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
index 7452850b7..e6145b353 100644
--- a/kernel/cobalt/rtdm/drvlib.c
+++ b/kernel/cobalt/rtdm/drvlib.c
@@ -601,6 +601,34 @@ int rtdm_timer_init(rtdm_timer_t *timer, rtdm_timer_handler_t handler,
 
 EXPORT_SYMBOL_GPL(rtdm_timer_init);
 
+/**
+ * @brief Initialise a timer on specified cpu
+ *
+ * @param[in,out] timer Timer handle
+ * @param[in] handler Handler to be called on timer expiry
+ * @param[in] name Optional timer name
+ * @param[in] cpu that run on
+ *
+ * @return 0 on success, otherwise negative error code
+ *
+ * @coretags{task-unrestricted}
+ */
+int rtdm_timer_init_on_cpu(rtdm_timer_t *timer, rtdm_timer_handler_t handler,
+		    const char *name, int cpu)
+{
+	struct xnsched *sched = xnsched_struct(cpu);
+
+	if (!realtime_core_enabled())
+		return -ENOSYS;
+
+	sched = xnsched_struct(cpu);
+	xntimer_init((timer), &nkclock, handler, sched, XNTIMER_IGRAVITY);
+	xntimer_set_name((timer), (name));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rtdm_timer_init_on_cpu);
+
 /**
  * @brief Destroy a timer
  *
-- 
2.17.1



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

* [PATCH 3/5] rtdm/testing: latmus: introduce latmus driver
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
  2021-02-20  5:53 ` [PATCH 2/5] cobalt/rtdm: add interface to init timer " hongzha1
@ 2021-02-20  5:53 ` hongzha1
  2021-02-20  5:53 ` [PATCH 4/5] drivers/gpio: core: introduce helper to find gpiochip hongzha1
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: hongzha1 @ 2021-02-20  5:53 UTC (permalink / raw)
  To: xenomai

Signed-off-by: hongzha1 <hongzhan.chen@intel.com>

diff --git a/include/rtdm/uapi/testing.h b/include/rtdm/uapi/testing.h
index f8207b8c7..b1723b9f8 100644
--- a/include/rtdm/uapi/testing.h
+++ b/include/rtdm/uapi/testing.h
@@ -118,6 +118,52 @@ struct rttst_heap_stathdr {
 	struct rttst_heap_stats *buf;
 };
 
+/* Latmus context types. */
+#define COBALT_LAT_IRQ   0
+#define COBALT_LAT_KERN  1
+#define COBALT_LAT_USER  2
+#define COBALT_LAT_SIRQ  3
+#define COBALT_LAT_LAST	COBALT_LAT_SIRQ
+
+struct latmus_setup {
+	__u32 type;
+	__u64 period;
+	__s32 priority;
+	__u32 cpu;
+	union {
+		struct {
+			__u32 verbosity;
+		} tune;
+		struct {
+			__u32 xfd;
+			__u32 hcells;
+		} measure;
+	} u;
+};
+
+/*
+ * The measurement record which the driver sends to userland each
+ * second through an xbuf channel.
+ */
+struct latmus_measurement {
+	__s64 sum_lat;
+	__s32 min_lat;
+	__s32 max_lat;
+	__u32 overruns;
+	__u32 samples;
+};
+
+struct latmus_measurement_result {
+	__u64 last_ptr;		/* (struct latmus_measurement __user *last) */
+	__u64 histogram_ptr;	/* (__s32 __user *histogram) */
+	__u32 len;
+};
+
+struct latmus_result {
+	__u64 data_ptr;		/* (void __user *data) */
+	__u32 len;
+};
+
 #define RTIOC_TYPE_TESTING		RTDM_CLASS_TESTING
 
 /*!
@@ -133,6 +179,8 @@ struct rttst_heap_stathdr {
 #define RTDM_SUBCLASS_RTDMTEST		3
 /** subclase name: "heapcheck" */
 #define RTDM_SUBCLASS_HEAPCHECK		4
+/** subclase name: "latmus" */
+#define RTDM_SUBCLASS_LATMUS		5
 /** @} */
 
 /*!
@@ -193,6 +241,21 @@ struct rttst_heap_stathdr {
 #define RTTST_RTIOC_HEAP_STAT_COLLECT \
 	_IOR(RTIOC_TYPE_TESTING, 0x45, int)
 
+#define RTTST_RTIOC_COBALT_LATIOC_TUNE \
+	_IOWR(RTIOC_TYPE_TESTING, 0x50, struct latmus_setup)
+
+#define RTTST_RTIOC_COBALT_LATIOC_MEASURE \
+	_IOWR(RTIOC_TYPE_TESTING, 0x51, struct latmus_setup)
+
+#define RTTST_RTIOC_COBALT_LATIOC_RUN \
+	_IOR(RTIOC_TYPE_TESTING, 0x52, struct latmus_result)
+
+#define RTTST_RTIOC_COBALT_LATIOC_PULSE \
+	_IOW(RTIOC_TYPE_TESTING, 0x53, __u64)
+
+#define RTTST_RTIOC_COBALT_LATIOC_RESET	\
+	_IO(RTIOC_TYPE_TESTING, 0x54)
+
 /** @} */
 
 #endif /* !_RTDM_UAPI_TESTING_H */
diff --git a/kernel/drivers/testing/Kconfig b/kernel/drivers/testing/Kconfig
index 88c043c82..5478dfe97 100644
--- a/kernel/drivers/testing/Kconfig
+++ b/kernel/drivers/testing/Kconfig
@@ -26,4 +26,14 @@ config XENO_DRIVERS_RTDMTEST
 	help
 	Kernel driver for performing RTDM unit tests.
 
+config COBALT_LATMUS
+	bool "Timer latency calibration and measurement"
+	depends on XENOMAI
+	default y
+	help
+	  This driver supports the latmus application for
+	  determining the best gravity values for the cobalt core
+	  clock, and measuring the response time to timer events.
+	  If in doubt, say 'Y'.
+
 endmenu
diff --git a/kernel/drivers/testing/Makefile b/kernel/drivers/testing/Makefile
index 09b076348..cad4ad50d 100644
--- a/kernel/drivers/testing/Makefile
+++ b/kernel/drivers/testing/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_XENO_DRIVERS_TIMERBENCH) += xeno_timerbench.o
 obj-$(CONFIG_XENO_DRIVERS_SWITCHTEST) += xeno_switchtest.o
 obj-$(CONFIG_XENO_DRIVERS_RTDMTEST)   += xeno_rtdmtest.o
 obj-$(CONFIG_XENO_DRIVERS_HEAPCHECK)   += xeno_heapcheck.o
+obj-$(CONFIG_COBALT_LATMUS)		+= latmus.o
 
 xeno_timerbench-y := timerbench.o
 
diff --git a/kernel/drivers/testing/latmus.c b/kernel/drivers/testing/latmus.c
new file mode 100644
index 000000000..bef662260
--- /dev/null
+++ b/kernel/drivers/testing/latmus.c
@@ -0,0 +1,1237 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * Derived from Xenomai Cobalt's autotune driver, https://xenomai.org/
+ * Copyright (C) 2014, 2018 Philippe Gerum  <rpm@xenomai.org>
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/sort.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <cobalt/kernel/pipe.h>
+#include <cobalt/kernel/sched.h>
+#include <rtdm/ipc.h>
+#include <rtdm/testing.h>
+#include <rtdm/driver.h>
+#include <rtdm/compat.h>
+
+#define ONE_BILLION  1000000000
+#define TUNER_SAMPLING_TIME	500000000UL
+#define TUNER_WARMUP_STEPS	10
+#define TUNER_RESULT_STEPS	40
+
+#define progress(__runner, __fmt, __args...)				\
+	do {								\
+		if ((__runner)->verbosity > 1)				\
+			printk(XENO_INFO "latmus(%s) " __fmt "\n",	\
+			       (__runner)->name, ##__args);		\
+	} while (0)
+
+#define cobalt_init_xntimer_on_cpu(__timer, __cpu, __handler)		\
+		rtdm_timer_init_on_cpu(__timer, __handler,		\
+			#__handler, __cpu)
+
+struct latmus_message {
+	struct xnpipe_mh mh;
+	char data[];
+};
+
+struct tuning_score {
+	int pmean;
+	int stddev;
+	int minlat;
+	unsigned int step;
+	unsigned int gravity;
+};
+
+struct runner_state {
+	ktime_t ideal;
+	int offset;
+	int min_lat;
+	int max_lat;
+	int allmax_lat;
+	int prev_mean;
+	int prev_sqs;
+	int cur_sqs;
+	int sum;
+	unsigned int overruns;
+	unsigned int cur_samples;
+	unsigned int max_samples;
+};
+
+struct latmus_runner {
+	const char *name;
+	unsigned int (*get_gravity)(struct latmus_runner *runner);
+	void (*set_gravity)(struct latmus_runner *runner, unsigned int gravity);
+	unsigned int (*adjust_gravity)(struct latmus_runner *runner, int adjust);
+	int (*start)(struct latmus_runner *runner, ktime_t start_time);
+	void (*stop)(struct latmus_runner *runner);
+	void (*destroy)(struct latmus_runner *runner);
+	int (*add_sample)(struct latmus_runner *runner, ktime_t timestamp);
+	int (*run)(struct rtdm_fd *fd, struct latmus_runner *runner,
+			struct latmus_result *result);
+	void (*cleanup)(struct latmus_runner *runner);
+	struct runner_state state;
+	struct rtdm_event done;
+	int status;
+	int verbosity;
+	ktime_t period;
+	union {
+		struct {
+			struct tuning_score scores[TUNER_RESULT_STEPS];
+			int nscores;
+		};
+		struct {
+			unsigned int warmup_samples;
+			unsigned int warmup_limit;
+			int socketfd;
+			void *xbuf;
+			u32 hcells;
+			s32 *histogram;
+		};
+	};
+};
+
+struct irq_runner {
+	rtdm_timer_t timer;
+	struct latmus_runner runner;
+};
+
+struct kthread_runner {
+	rtdm_task_t kthread;
+	struct rtdm_event barrier;
+	ktime_t start_time;
+	struct latmus_runner runner;
+};
+
+struct uthread_runner {
+	rtdm_timer_t timer;
+	struct rtdm_event pulse;
+	struct latmus_runner runner;
+};
+
+struct sirq_runner {
+	int sirq;
+	struct sirq_runner * __percpu *sirq_percpu;
+	rtdm_timer_t timer;
+	struct latmus_runner runner;
+};
+
+struct latmus_state {
+	struct latmus_runner *runner;
+	rtdm_lock_t latmus_lock;
+};
+
+static inline void init_runner_base(struct latmus_runner *runner)
+{
+	rtdm_event_init(&runner->done, 0);
+	runner->status = 0;
+	runner->socketfd = -1;
+}
+
+static inline void destroy_runner_base(struct latmus_runner *runner)
+{
+	rtdm_event_destroy(&runner->done);
+	if (runner->cleanup)
+		runner->cleanup(runner);
+}
+
+static inline void done_sampling(struct latmus_runner *runner,
+				 int status)
+{
+	runner->status = status;
+	rtdm_event_signal(&runner->done);
+}
+
+static void send_measurement(struct latmus_runner *runner)
+{
+	struct runner_state *state = &runner->state;
+	struct latmus_measurement *meas;
+	struct latmus_message *mbuf;
+	int len;
+
+	len = sizeof(*meas) + sizeof(*mbuf);
+	mbuf = xnmalloc(len);
+	if (mbuf == NULL)
+		return;
+
+	meas = (struct latmus_measurement *)mbuf->data;
+	meas->min_lat = state->min_lat;
+	meas->max_lat = state->max_lat;
+	meas->sum_lat = state->sum;
+	meas->overruns = state->overruns;
+	meas->samples = state->cur_samples;
+
+	runner->xbuf = mbuf;
+	xnpipe_send(runner->socketfd, &mbuf->mh, len, XNPIPE_NORMAL);
+
+	/* Reset counters for next round. */
+	state->min_lat = INT_MAX;
+	state->max_lat = INT_MIN;
+	state->sum = 0;
+	state->overruns = 0;
+	state->cur_samples = 0;
+}
+
+static int add_measurement_sample(struct latmus_runner *runner,
+				  ktime_t timestamp)
+{
+	struct runner_state *state = &runner->state;
+	ktime_t period = runner->period;
+	int delta, cell, offset_delta;
+
+	/* Skip samples in warmup time. */
+	if (runner->warmup_samples < runner->warmup_limit) {
+		runner->warmup_samples++;
+		state->ideal = ktime_add(state->ideal, period);
+		return 0;
+	}
+
+	delta = (int)ktime_to_ns(ktime_sub(timestamp, state->ideal));
+	offset_delta = delta - state->offset;
+	if (offset_delta < state->min_lat)
+		state->min_lat = offset_delta;
+	if (offset_delta > state->max_lat)
+		state->max_lat = offset_delta;
+	if (offset_delta > state->allmax_lat) {
+		state->allmax_lat = offset_delta;
+		//trace_evl_latspot(offset_delta);
+		//trace_evl_trigger("latmus");
+	}
+
+	if (runner->histogram) {
+		cell = (offset_delta < 0 ? -offset_delta : offset_delta) / 1000; /* us */
+		if (cell >= runner->hcells)
+			cell = runner->hcells - 1;
+		runner->histogram[cell]++;
+	}
+
+	state->sum += offset_delta;
+	state->ideal = ktime_add(state->ideal, period);
+
+	while (delta > 0 &&
+		(unsigned int)delta > ktime_to_ns(period)) { /* period > 0 */
+		state->overruns++;
+		state->ideal = ktime_add(state->ideal, period);
+		delta -= ktime_to_ns(period);
+	}
+
+	if (++state->cur_samples >= state->max_samples)
+		send_measurement(runner);
+
+	return 0;	/* Always keep going. */
+}
+
+static int add_tuning_sample(struct latmus_runner *runner,
+			     ktime_t timestamp)
+{
+	struct runner_state *state = &runner->state;
+	int n, delta, cur_mean;
+
+	delta = (int)ktime_to_ns(ktime_sub(timestamp, state->ideal));
+	if (delta < state->min_lat)
+		state->min_lat = delta;
+	if (delta > state->max_lat)
+		state->max_lat = delta;
+	if (delta < 0)
+		delta = 0;
+
+	state->sum += delta;
+	state->ideal = ktime_add(state->ideal, runner->period);
+	n = ++state->cur_samples;
+
+	/* TAOCP (Vol 2), single-pass computation of variance. */
+	if (n == 1)
+		state->prev_mean = delta;
+	else {
+		cur_mean = state->prev_mean + (delta - state->prev_mean) / n;
+		state->cur_sqs = state->prev_sqs + (delta - state->prev_mean)
+			* (delta - cur_mean);
+		state->prev_mean = cur_mean;
+		state->prev_sqs = state->cur_sqs;
+	}
+
+	if (n >= state->max_samples) {
+		done_sampling(runner, 0);
+		return 1;	/* Finished. */
+	}
+
+	return 0;	/* Keep going. */
+}
+
+static void latmus_irq_handler(rtdm_timer_t *timer) /* hard irqs off */
+{
+	struct irq_runner *irq_runner;
+	ktime_t now;
+
+	irq_runner = container_of(timer, struct irq_runner, timer);
+	now = xnclock_read_raw(&nkclock);
+	if (irq_runner->runner.add_sample(&irq_runner->runner, now))
+		rtdm_timer_stop(timer);
+}
+
+static void destroy_irq_runner(struct latmus_runner *runner)
+{
+	struct irq_runner *irq_runner;
+
+	irq_runner = container_of(runner, struct irq_runner, runner);
+	rtdm_timer_destroy(&irq_runner->timer);
+	destroy_runner_base(runner);
+	kfree(irq_runner);
+}
+
+static unsigned int get_irq_gravity(struct latmus_runner *runner)
+{
+	return nkclock.gravity.irq;
+}
+
+static void set_irq_gravity(struct latmus_runner *runner, unsigned int gravity)
+{
+	nkclock.gravity.irq = gravity;
+}
+
+static unsigned int adjust_irq_gravity(struct latmus_runner *runner, int adjust)
+{
+	return nkclock.gravity.irq += adjust;
+}
+
+static int start_irq_runner(struct latmus_runner *runner,
+			    ktime_t start_time)
+{
+	struct irq_runner *irq_runner;
+
+	irq_runner = container_of(runner, struct irq_runner, runner);
+
+	rtdm_timer_start(&irq_runner->timer, start_time,
+				runner->period, RTDM_TIMERMODE_ABSOLUTE);
+
+	return 0;
+}
+
+static void stop_irq_runner(struct latmus_runner *runner)
+{
+	struct irq_runner *irq_runner;
+
+	irq_runner = container_of(runner, struct irq_runner, runner);
+
+	rtdm_timer_stop(&irq_runner->timer);
+}
+
+static struct latmus_runner *create_irq_runner(int cpu)
+{
+	struct irq_runner *irq_runner;
+
+	irq_runner = kzalloc(sizeof(*irq_runner), GFP_KERNEL);
+	if (irq_runner == NULL)
+		return NULL;
+
+	irq_runner->runner = (struct latmus_runner){
+		.name = "irqhand",
+		.destroy = destroy_irq_runner,
+		.get_gravity = get_irq_gravity,
+		.set_gravity = set_irq_gravity,
+		.adjust_gravity = adjust_irq_gravity,
+		.start = start_irq_runner,
+		.stop = stop_irq_runner,
+	};
+
+	init_runner_base(&irq_runner->runner);
+	cobalt_init_xntimer_on_cpu(&irq_runner->timer, cpu, latmus_irq_handler);
+
+	return &irq_runner->runner;
+}
+
+static irqreturn_t latmus_sirq_handler(int sirq, void *dev_id)
+{
+	struct sirq_runner * __percpu *self_percpu = dev_id;
+	struct sirq_runner *sirq_runner = *self_percpu;
+	ktime_t now;
+
+	now = xnclock_read_raw(&nkclock);
+	if (sirq_runner->runner.add_sample(&sirq_runner->runner, now))
+		rtdm_timer_stop(&sirq_runner->timer);
+
+	return IRQ_HANDLED;
+}
+
+static void latmus_sirq_timer_handler(rtdm_timer_t *timer) /* hard irqs off */
+{
+	struct sirq_runner *sirq_runner;
+	struct runner_state *state;
+	ktime_t now;
+
+	now = xnclock_read_raw(&nkclock);
+	sirq_runner = container_of(timer, struct sirq_runner, timer);
+	state = &sirq_runner->runner.state;
+	state->offset = (int)ktime_to_ns(ktime_sub(now, state->ideal));
+	irq_post_inband(sirq_runner->sirq);
+}
+
+static void destroy_sirq_runner(struct latmus_runner *runner)
+{
+	struct sirq_runner *sirq_runner;
+
+	sirq_runner = container_of(runner, struct sirq_runner, runner);
+	rtdm_timer_destroy(&sirq_runner->timer);
+	free_percpu_irq(sirq_runner->sirq, sirq_runner->sirq_percpu);
+	free_percpu(sirq_runner->sirq_percpu);
+	irq_dispose_mapping(sirq_runner->sirq);
+	destroy_runner_base(runner);
+	kfree(sirq_runner);
+}
+
+static int start_sirq_runner(struct latmus_runner *runner,
+			ktime_t start_time)
+{
+	struct sirq_runner *sirq_runner;
+
+	sirq_runner = container_of(runner, struct sirq_runner, runner);
+
+	rtdm_timer_start(&sirq_runner->timer, start_time,
+				runner->period, RTDM_TIMERMODE_ABSOLUTE);
+
+	return 0;
+}
+
+static void stop_sirq_runner(struct latmus_runner *runner)
+{
+	struct sirq_runner *sirq_runner;
+
+	sirq_runner = container_of(runner, struct sirq_runner, runner);
+
+	rtdm_timer_stop(&sirq_runner->timer);
+}
+
+static struct latmus_runner *create_sirq_runner(int cpu)
+{
+	struct sirq_runner * __percpu *sirq_percpu;
+	struct sirq_runner *sirq_runner;
+	int sirq, ret, _cpu;
+
+	sirq_percpu = alloc_percpu(struct sirq_runner *);
+	if (sirq_percpu == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	sirq_runner = kzalloc(sizeof(*sirq_runner), GFP_KERNEL);
+	if (sirq_runner == NULL) {
+		free_percpu(sirq_percpu);
+		return NULL;
+	}
+
+	sirq = irq_create_direct_mapping(synthetic_irq_domain);
+	if (sirq == 0) {
+		free_percpu(sirq_percpu);
+		kfree(sirq_runner);
+		return ERR_PTR(-EAGAIN);
+	}
+
+	sirq_runner->runner = (struct latmus_runner){
+		.name = "sirqhand",
+		.destroy = destroy_sirq_runner,
+		.start = start_sirq_runner,
+		.stop = stop_sirq_runner,
+	};
+	sirq_runner->sirq = sirq;
+	sirq_runner->sirq_percpu = sirq_percpu;
+	init_runner_base(&sirq_runner->runner);
+	cobalt_init_xntimer_on_cpu(&sirq_runner->timer, cpu,
+			latmus_sirq_timer_handler);
+
+	for_each_possible_cpu(_cpu)
+		*per_cpu_ptr(sirq_percpu, _cpu) = sirq_runner;
+
+	ret = __request_percpu_irq(sirq, latmus_sirq_handler,
+				IRQF_NO_THREAD,
+				"latmus sirq",
+				sirq_percpu);
+	if (ret) {
+		rtdm_timer_destroy(&sirq_runner->timer);
+		irq_dispose_mapping(sirq);
+		free_percpu(sirq_percpu);
+		kfree(sirq_runner);
+		return ERR_PTR(ret);
+	}
+
+	return &sirq_runner->runner;
+}
+
+void kthread_handler(void *arg)
+{
+	struct kthread_runner *k_runner = arg;
+	ktime_t now;
+	int ret = 0;
+
+	for (;;) {
+		if (rtdm_task_should_stop())
+			break;
+
+		ret = rtdm_event_wait(&k_runner->barrier);
+		if (ret)
+			break;
+
+		ret = rtdm_task_set_period(&k_runner->kthread,
+					k_runner->start_time,
+					k_runner->runner.period);
+		if (ret)
+			break;
+
+		for (;;) {
+			ret = rtdm_task_wait_period(NULL);
+			if (ret && ret != -ETIMEDOUT)
+				goto out;
+
+			now = xnclock_read_raw(&nkclock);
+			if (k_runner->runner.add_sample(&k_runner->runner, now)) {
+				rtdm_task_set_period(&k_runner->kthread, 0, 0);
+				break;
+			}
+		}
+	}
+out:
+	done_sampling(&k_runner->runner, ret);
+	rtdm_task_destroy(&k_runner->kthread);
+}
+
+static void destroy_kthread_runner(struct latmus_runner *runner)
+{
+	struct kthread_runner *k_runner;
+
+	k_runner = container_of(runner, struct kthread_runner, runner);
+	rtdm_task_destroy(&k_runner->kthread);
+	rtdm_event_destroy(&k_runner->barrier);
+	destroy_runner_base(runner);
+	kfree(k_runner);
+}
+
+static unsigned int get_kthread_gravity(struct latmus_runner *runner)
+{
+	return nkclock.gravity.kernel;
+}
+
+static void
+set_kthread_gravity(struct latmus_runner *runner, unsigned int gravity)
+{
+	nkclock.gravity.kernel = gravity;
+}
+
+static unsigned int
+adjust_kthread_gravity(struct latmus_runner *runner, int adjust)
+{
+	return nkclock.gravity.kernel += adjust;
+}
+
+static int start_kthread_runner(struct latmus_runner *runner,
+				ktime_t start_time)
+{
+	struct kthread_runner *k_runner;
+
+	k_runner = container_of(runner, struct kthread_runner, runner);
+
+	k_runner->start_time = start_time;
+	rtdm_event_signal(&k_runner->barrier);
+
+	return 0;
+}
+
+static struct latmus_runner *
+create_kthread_runner(int priority, int cpu)
+{
+	struct kthread_runner *k_runner;
+	int ret;
+	char *taskname;
+
+	k_runner = kzalloc(sizeof(*k_runner), GFP_KERNEL);
+	if (k_runner == NULL)
+		return NULL;
+
+	k_runner->runner = (struct latmus_runner){
+		.name = "kthread",
+		.destroy = destroy_kthread_runner,
+		.get_gravity = get_kthread_gravity,
+		.set_gravity = set_kthread_gravity,
+		.adjust_gravity = adjust_kthread_gravity,
+		.start = start_kthread_runner,
+	};
+
+	init_runner_base(&k_runner->runner);
+	rtdm_event_init(&k_runner->barrier, 0);
+
+	taskname = kasprintf(GFP_KERNEL, "latmus-klat:%d",
+			task_pid_nr(current));
+	ret = rtdm_task_init_on_cpu(&k_runner->kthread, cpu,
+				taskname,
+				kthread_handler,
+				k_runner,
+				priority,
+				0);
+	kfree(taskname);
+	if (ret) {
+		kfree(k_runner);
+		return ERR_PTR(ret);
+	}
+
+	return &k_runner->runner;
+}
+
+static void latmus_pulse_handler(rtdm_timer_t *timer)
+{
+	struct uthread_runner *u_runner;
+
+	u_runner = container_of(timer, struct uthread_runner, timer);
+	rtdm_event_signal(&u_runner->pulse);
+}
+
+static void destroy_uthread_runner(struct latmus_runner *runner)
+{
+	struct uthread_runner *u_runner;
+
+	u_runner = container_of(runner, struct uthread_runner, runner);
+	rtdm_timer_destroy(&u_runner->timer);
+	rtdm_event_destroy(&u_runner->pulse);
+	destroy_runner_base(runner);
+	kfree(u_runner);
+}
+
+static unsigned int get_uthread_gravity(struct latmus_runner *runner)
+{
+	return nkclock.gravity.user;
+}
+
+static void set_uthread_gravity(struct latmus_runner *runner,
+				unsigned int gravity)
+{
+	nkclock.gravity.user = gravity;
+}
+
+static unsigned int
+adjust_uthread_gravity(struct latmus_runner *runner, int adjust)
+{
+	return nkclock.gravity.user += adjust;
+}
+
+static int start_uthread_runner(struct latmus_runner *runner,
+				ktime_t start_time)
+{
+	struct uthread_runner *u_runner;
+
+	u_runner = container_of(runner, struct uthread_runner, runner);
+
+	rtdm_timer_start(&u_runner->timer, start_time,
+				runner->period, RTDM_TIMERMODE_ABSOLUTE);
+
+	return 0;
+}
+
+static void stop_uthread_runner(struct latmus_runner *runner)
+{
+	struct uthread_runner *u_runner;
+
+	u_runner = container_of(runner, struct uthread_runner, runner);
+
+	rtdm_timer_stop(&u_runner->timer);
+}
+
+static int add_uthread_sample(struct latmus_runner *runner,
+			      ktime_t user_timestamp)
+{
+	struct uthread_runner *u_runner;
+	int ret;
+
+	u_runner = container_of(runner, struct uthread_runner, runner);
+
+	if (user_timestamp &&
+	    u_runner->runner.add_sample(runner, user_timestamp)) {
+		rtdm_timer_stop(&u_runner->timer);
+		/* Tell the caller to park until next round. */
+		ret = -EPIPE;
+	} else {
+		ret = rtdm_event_wait(&u_runner->pulse);
+	}
+
+	return ret;
+}
+
+static struct latmus_runner *create_uthread_runner(int cpu)
+{
+	struct uthread_runner *u_runner;
+
+	u_runner = kzalloc(sizeof(*u_runner), GFP_KERNEL);
+	if (u_runner == NULL)
+		return NULL;
+
+	u_runner->runner = (struct latmus_runner){
+		.name = "uthread",
+		.destroy = destroy_uthread_runner,
+		.get_gravity = get_uthread_gravity,
+		.set_gravity = set_uthread_gravity,
+		.adjust_gravity = adjust_uthread_gravity,
+		.start = start_uthread_runner,
+		.stop = stop_uthread_runner,
+	};
+
+	init_runner_base(&u_runner->runner);
+	cobalt_init_xntimer_on_cpu(&u_runner->timer, cpu, latmus_pulse_handler);
+	xntimer_set_gravity(&u_runner->timer, XNTIMER_UGRAVITY);
+	rtdm_event_init(&u_runner->pulse, 0);
+
+	return &u_runner->runner;
+}
+
+static inline void build_score(struct latmus_runner *runner, int step)
+{
+	struct runner_state *state = &runner->state;
+	unsigned int variance, n;
+
+	n = state->cur_samples;
+	runner->scores[step].pmean = state->sum / n;
+	variance = n > 1 ? state->cur_sqs / (n - 1) : 0;
+	runner->scores[step].stddev = int_sqrt(variance);
+	runner->scores[step].minlat = state->min_lat;
+	runner->scores[step].gravity = runner->get_gravity(runner);
+	runner->scores[step].step = step;
+	runner->nscores++;
+}
+
+static int cmp_score_mean(const void *c, const void *r)
+{
+	const struct tuning_score *sc = c, *sr = r;
+
+	return sc->pmean - sr->pmean;
+}
+
+static int cmp_score_stddev(const void *c, const void *r)
+{
+	const struct tuning_score *sc = c, *sr = r;
+
+	return sc->stddev - sr->stddev;
+}
+
+static int cmp_score_minlat(const void *c, const void *r)
+{
+	const struct tuning_score *sc = c, *sr = r;
+
+	return sc->minlat - sr->minlat;
+}
+
+static int cmp_score_gravity(const void *c, const void *r)
+{
+	const struct tuning_score *sc = c, *sr = r;
+
+	return sc->gravity - sr->gravity;
+}
+
+static int filter_mean(struct latmus_runner *runner)
+{
+	sort(runner->scores, runner->nscores,
+	     sizeof(struct tuning_score),
+	     cmp_score_mean, NULL);
+
+	/* Top half of the best pondered means. */
+
+	return (runner->nscores + 1) / 2;
+}
+
+static int filter_stddev(struct latmus_runner *runner)
+{
+	sort(runner->scores, runner->nscores,
+	     sizeof(struct tuning_score),
+	     cmp_score_stddev, NULL);
+
+	/* Top half of the best standard deviations. */
+
+	return (runner->nscores + 1) / 2;
+}
+
+static int filter_minlat(struct latmus_runner *runner)
+{
+	sort(runner->scores, runner->nscores,
+	     sizeof(struct tuning_score),
+	     cmp_score_minlat, NULL);
+
+	/* Top half of the minimum latencies. */
+
+	return (runner->nscores + 1) / 2;
+}
+
+static int filter_gravity(struct latmus_runner *runner)
+{
+	sort(runner->scores, runner->nscores,
+	     sizeof(struct tuning_score),
+	     cmp_score_gravity, NULL);
+
+	/* Smallest gravity required among the shortest latencies. */
+
+	return runner->nscores;
+}
+
+static void dump_scores(struct latmus_runner *runner)
+{
+	int n;
+
+	if (runner->verbosity < 2)
+		return;
+
+	for (n = 0; n < runner->nscores; n++)
+		printk(XENO_INFO
+		       ".. S%.2d pmean=%d stddev=%d minlat=%d gravity=%u\n",
+		       runner->scores[n].step,
+		       runner->scores[n].pmean,
+		       runner->scores[n].stddev,
+		       runner->scores[n].minlat,
+		       runner->scores[n].gravity);
+}
+
+static inline void filter_score(struct latmus_runner *runner,
+				int (*filter)(struct latmus_runner *runner))
+{
+	runner->nscores = filter(runner);
+	dump_scores(runner);
+}
+
+static void __latmus_free_handler(void *buf, void *skarg) /* nklock free */
+{
+	struct latmus_runner *runner = skarg;
+
+	xnfree(runner->xbuf);
+}
+
+static int measure_continously(struct latmus_runner *runner)
+{
+	struct runner_state *state = &runner->state;
+	ktime_t period = runner->period;
+	int ret;
+
+	state->max_samples = ONE_BILLION / (int)ktime_to_ns(period);
+	runner->add_sample = add_measurement_sample;
+	state->min_lat = INT_MAX;
+	state->max_lat = INT_MIN;
+	state->allmax_lat = INT_MIN;
+	state->sum = 0;
+	state->overruns = 0;
+	state->cur_samples = 0;
+	state->offset = 0;	/* for SIRQ latency only. */
+	state->ideal = ktime_add(xnclock_read_raw(&nkclock), period);
+
+	ret = runner->start(runner, state->ideal);
+	if (ret)
+		goto out;
+
+	ret = rtdm_event_wait(&runner->done) ?: runner->status;
+
+	if (runner->stop)
+		runner->stop(runner);
+out:
+	return ret;
+}
+
+static int tune_gravity(struct latmus_runner *runner)
+{
+	struct runner_state *state = &runner->state;
+	int ret, step, gravity_limit, adjust;
+	ktime_t period = runner->period;
+	unsigned int orig_gravity;
+
+	state->max_samples = TUNER_SAMPLING_TIME / (int)ktime_to_ns(period);
+	orig_gravity = runner->get_gravity(runner);
+	runner->add_sample = add_tuning_sample;
+	runner->set_gravity(runner, 0);
+	runner->nscores = 0;
+	adjust = 500; /* Gravity adjustment step */
+	gravity_limit = 0;
+	progress(runner, "warming up...");
+
+	for (step = 0; step < TUNER_WARMUP_STEPS + TUNER_RESULT_STEPS; step++) {
+		state->ideal = ktime_add_ns(xnclock_read_raw(&nkclock),
+			    ktime_to_ns(period) * TUNER_WARMUP_STEPS);
+		state->min_lat = INT_MAX;
+		state->max_lat = INT_MIN;
+		state->prev_mean = 0;
+		state->prev_sqs = 0;
+		state->cur_sqs = 0;
+		state->sum = 0;
+		state->cur_samples = 0;
+
+		ret = runner->start(runner, state->ideal);
+		if (ret)
+			goto fail;
+
+		/* Runner stops when posting. */
+		ret = rtdm_event_wait(&runner->done);
+		if (ret)
+			goto fail;
+
+		ret = runner->status;
+		if (ret)
+			goto fail;
+
+		if (step < TUNER_WARMUP_STEPS) {
+			if (state->min_lat > gravity_limit) {
+				gravity_limit = state->min_lat;
+				progress(runner, "gravity limit set to %u ns (%d)",
+					 gravity_limit, state->min_lat);
+			}
+			continue;
+		}
+
+		if (state->min_lat < 0) {
+			if (runner->get_gravity(runner) < -state->min_lat) {
+				printk(XENO_WARNING
+				       "latmus(%s) failed with early shot (%d ns)\n",
+				       runner->name,
+				       -(runner->get_gravity(runner) + state->min_lat));
+				ret = -EAGAIN;
+				goto fail;
+			}
+			break;
+		}
+
+		if (((step - TUNER_WARMUP_STEPS) % 5) == 0)
+			progress(runner, "calibrating... (slice %d)",
+				 (step - TUNER_WARMUP_STEPS) / 5 + 1);
+
+		build_score(runner, step - TUNER_WARMUP_STEPS);
+
+		/*
+		 * Anticipating by more than the minimum latency
+		 * detected at warmup would make no sense: cap the
+		 * gravity we may try.
+		 */
+		if (runner->adjust_gravity(runner, adjust) > gravity_limit) {
+			progress(runner, "beyond gravity limit at %u ns",
+				 runner->get_gravity(runner));
+			break;
+		}
+	}
+
+	progress(runner, "calibration scores");
+	dump_scores(runner);
+	progress(runner, "pondered mean filter");
+	filter_score(runner, filter_mean);
+	progress(runner, "standard deviation filter");
+	filter_score(runner, filter_stddev);
+	progress(runner, "minimum latency filter");
+	filter_score(runner, filter_minlat);
+	progress(runner, "gravity filter");
+	filter_score(runner, filter_gravity);
+	runner->set_gravity(runner, runner->scores[0].gravity);
+
+	return 0;
+fail:
+	runner->set_gravity(runner, orig_gravity);
+
+	return ret;
+}
+
+static int setup_tuning(struct latmus_runner *runner,
+			struct latmus_setup *setup)
+{
+	runner->verbosity = setup->u.tune.verbosity;
+	runner->period = setup->period;
+
+	return 0;
+}
+
+static int run_tuning(struct rtdm_fd *fd,
+		struct latmus_runner *runner,
+		struct latmus_result *result)
+{
+	__u32 gravity;
+	int ret;
+
+	ret = tune_gravity(runner);
+	if (ret)
+		return ret;
+
+	gravity = runner->get_gravity(runner);
+
+	if (rtdm_safe_copy_to_user(fd, (void *)(long)result->data_ptr,
+				&gravity, sizeof(gravity)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int setup_measurement(struct latmus_runner *runner,
+			     struct latmus_setup *setup)
+{
+	struct xnpipe_operations ops;
+	int xnpipefd;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.free_obuf = &__latmus_free_handler;
+	xnpipefd = xnpipe_connect(-1, &ops, runner);
+	if (xnpipefd < 0)
+		return xnpipefd;
+	runner->socketfd = xnpipefd;
+	runner->period = setup->period;
+	runner->warmup_limit = ONE_BILLION / (int)ktime_to_ns(setup->period); /* 1s warmup */
+	runner->histogram = NULL;
+	runner->hcells = setup->u.measure.hcells;
+	if (runner->hcells == 0)
+		return 0;
+
+	if (runner->hcells > 1000) /* LART */
+		return -EINVAL;
+
+	runner->histogram = kzalloc(runner->hcells * sizeof(s32),
+				    GFP_KERNEL);
+
+	return runner->histogram ? 0 : -ENOMEM;
+}
+
+static int run_measurement(struct rtdm_fd *fd,
+			struct latmus_runner *runner,
+			struct latmus_result *result)
+{
+	struct runner_state *state = &runner->state;
+	struct latmus_measurement_result mr;
+	struct latmus_measurement last;
+	size_t len;
+	int ret;
+
+	if (result->len != sizeof(mr))
+		return -EINVAL;
+
+	if (rtdm_safe_copy_from_user(fd, &mr, (void *)(long)result->data_ptr,
+				sizeof(mr)))
+		return -EFAULT;
+
+	ret = measure_continously(runner);
+	if (ret != -EINTR)
+		return ret;
+
+	/*
+	 * Copy the last bulk of consolidated measurements and the
+	 * histogram distribution data back to userland.
+	 */
+	last.min_lat = state->min_lat;
+	last.max_lat = state->max_lat;
+	last.sum_lat = state->sum;
+	last.overruns = state->overruns;
+	last.samples = state->cur_samples;
+	if (rtdm_safe_copy_to_user(fd, (void *)(long)mr.last_ptr,
+				&last, sizeof(last)))
+		return -EFAULT;
+
+	if (runner->histogram) {
+		len = runner->hcells * sizeof(s32);
+		if (len > mr.len)
+			len = result->len;
+		if (len > 0 &&
+		    rtdm_safe_copy_to_user(fd, (void *)(long)mr.histogram_ptr,
+					   runner->histogram, len))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void cleanup_measurement(struct latmus_runner *runner)
+{
+	if (runner->socketfd >= 0) {
+		xnpipe_disconnect(runner->socketfd);
+		runner->socketfd = -1;
+	}
+
+	if (runner->histogram)
+		kfree(runner->histogram);
+}
+
+static int latmus_ioctl(struct rtdm_fd *fd, unsigned int cmd, void *arg)
+{
+	struct latmus_state *ls = rtdm_fd_to_private(fd);
+	int (*setup)(struct latmus_runner *runner,
+		     struct latmus_setup *setup_data);
+	int (*run)(struct rtdm_fd *fd,
+		struct latmus_runner *runner,
+		struct latmus_result *result);
+	void (*cleanup)(struct latmus_runner *runner);
+	struct latmus_setup setup_data;
+	struct latmus_runner *runner;
+	int ret;
+
+	if (cmd == RTTST_RTIOC_COBALT_LATIOC_RESET) {
+		xnclock_reset_gravity(&nkclock);
+		return 0;
+	}
+
+	/* All other cmds require a setup struct to be passed. */
+
+	if (rtdm_copy_from_user(fd, &setup_data, arg, sizeof(setup_data)))
+		return -EFAULT;
+
+	if (setup_data.type == COBALT_LAT_SIRQ &&
+			cmd != RTTST_RTIOC_COBALT_LATIOC_MEASURE)
+		return -EINVAL;
+
+	switch (cmd) {
+	case RTTST_RTIOC_COBALT_LATIOC_TUNE:
+		setup = setup_tuning;
+		run = run_tuning;
+		cleanup = NULL;
+		break;
+	case RTTST_RTIOC_COBALT_LATIOC_MEASURE:
+		setup = setup_measurement;
+		run = run_measurement;
+		cleanup = cleanup_measurement;
+		break;
+	default:
+		return -ENOSYS;
+	}
+
+	if (setup_data.period <= 0 ||
+	    setup_data.period > ONE_BILLION)
+		return -EINVAL;
+
+	if (setup_data.priority < 1 ||
+	    setup_data.priority > XNSCHED_FIFO_MAX_PRIO)
+		return -EINVAL;
+
+	if (setup_data.cpu >= num_possible_cpus() ||
+		!xnsched_supported_cpu(setup_data.cpu))
+		return -EINVAL;
+
+	/* Clear previous runner. */
+	runner = ls->runner;
+	if (runner) {
+		runner->destroy(runner);
+		ls->runner = NULL;
+	}
+
+	switch (setup_data.type) {
+	case COBALT_LAT_IRQ:
+		runner = create_irq_runner(setup_data.cpu);
+		break;
+	case COBALT_LAT_KERN:
+		runner = create_kthread_runner(setup_data.priority,
+					       setup_data.cpu);
+		break;
+	case COBALT_LAT_USER:
+		runner = create_uthread_runner(setup_data.cpu);
+		break;
+	case COBALT_LAT_SIRQ:
+		runner = create_sirq_runner(setup_data.cpu);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (IS_ERR(runner))
+		return PTR_ERR(runner);
+
+	ret = setup(runner, &setup_data);
+	if (ret) {
+		runner->destroy(runner);
+		return ret;
+	}
+
+	runner->run = run;
+	runner->cleanup = cleanup;
+	ls->runner = runner;
+
+	return ((cmd == RTTST_RTIOC_COBALT_LATIOC_MEASURE) ?
+			runner->socketfd : 0);
+}
+
+static int latmus_oob_ioctl(struct rtdm_fd *fd, unsigned int cmd, void *arg)
+{
+	struct latmus_state *ls = rtdm_fd_to_private(fd);
+	struct latmus_runner *runner;
+	struct latmus_result result;
+	__u64 timestamp;
+	int ret;
+
+	switch (cmd) {
+	case RTTST_RTIOC_COBALT_LATIOC_RUN:
+		runner = ls->runner;
+		if (runner == NULL)
+			return -EAGAIN;
+		ret = rtdm_safe_copy_from_user(fd, &result,
+				arg, sizeof(result));
+
+		if (ret)
+			return -EFAULT;
+		ret = runner->run(fd, runner, &result);
+		break;
+	case RTTST_RTIOC_COBALT_LATIOC_PULSE:
+		runner = ls->runner;
+		if (runner == NULL)
+			return -EAGAIN;
+		if (runner->start != start_uthread_runner)
+			return -EINVAL;
+		ret = rtdm_safe_copy_from_user(fd,  &timestamp,
+						arg, sizeof(timestamp));
+		if (ret)
+			return -EFAULT;
+		ret = add_uthread_sample(runner, ns_to_ktime(timestamp));
+		break;
+	default:
+		ret = -ENOSYS;
+	}
+
+	return ret;
+}
+
+static int latmus_open(struct rtdm_fd *fd, int oflags)
+{
+	struct latmus_state *ls;
+
+	ls = rtdm_fd_to_private(fd);
+	ls->runner = NULL;
+	rtdm_lock_init(&ls->latmus_lock);
+
+	return 0;
+}
+
+static void latmus_close(struct rtdm_fd *fd)
+{
+	struct latmus_state *ls = rtdm_fd_to_private(fd);
+	struct latmus_runner *runner;
+
+	runner = ls->runner;
+	if (runner)
+		runner->destroy(runner);
+
+}
+
+static struct rtdm_driver latmus_driver = {
+	.profile_info		= RTDM_PROFILE_INFO(latmus,
+						    RTDM_CLASS_TESTING,
+						    RTDM_SUBCLASS_LATMUS,
+						    RTTST_PROFILE_VER),
+	.device_flags		= RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
+	.device_count		= 1,
+	.context_size		= sizeof(struct latmus_state),
+	.ops = {
+		.open		= latmus_open,
+		.close		= latmus_close,
+		.ioctl_rt	= latmus_oob_ioctl,
+		.ioctl_nrt	= latmus_ioctl,
+	},
+};
+
+static struct rtdm_device device = {
+	.driver = &latmus_driver,
+	.label = "latmus",
+};
+
+static int __init __latmus_init(void)
+{
+	return rtdm_dev_register(&device);
+}
+
+static void __latmus_exit(void)
+{
+	rtdm_dev_unregister(&device);
+}
+
+module_init(__latmus_init);
+module_exit(__latmus_exit);
+
+MODULE_LICENSE("GPL");
-- 
2.17.1



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

* [PATCH 4/5] drivers/gpio: core: introduce helper to find gpiochip
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
  2021-02-20  5:53 ` [PATCH 2/5] cobalt/rtdm: add interface to init timer " hongzha1
  2021-02-20  5:53 ` [PATCH 3/5] rtdm/testing: latmus: introduce latmus driver hongzha1
@ 2021-02-20  5:53 ` hongzha1
  2021-02-20  5:53 ` [PATCH 5/5] testsuite/latmus: introduce latmus app hongzha1
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: hongzha1 @ 2021-02-20  5:53 UTC (permalink / raw)
  To: xenomai

To find gpiochip for non-OF platforms like x86

Signed-off-by: hongzha1 <hongzhan.chen@intel.com>

diff --git a/include/cobalt/kernel/rtdm/gpio.h b/include/cobalt/kernel/rtdm/gpio.h
index 00055ec0a..1d1e67bf7 100644
--- a/include/cobalt/kernel/rtdm/gpio.h
+++ b/include/cobalt/kernel/rtdm/gpio.h
@@ -61,6 +61,9 @@ int rtdm_gpiochip_add_by_name(struct rtdm_gpio_chip *rgc,
 int rtdm_gpiochip_post_event(struct rtdm_gpio_chip *rgc,
 			     unsigned int offset);
 
+int rtdm_gpiochip_find(struct device_node *from,
+			  void *compat, int type);
+
 #ifdef CONFIG_OF
 
 int rtdm_gpiochip_scan_of(struct device_node *from,
diff --git a/kernel/drivers/gpio/gpio-core.c b/kernel/drivers/gpio/gpio-core.c
index 600ef9789..7ab989e94 100644
--- a/kernel/drivers/gpio/gpio-core.c
+++ b/kernel/drivers/gpio/gpio-core.c
@@ -554,6 +554,34 @@ static int match_gpio_chip(struct gpio_chip *gc, void *data)
 	return 0;
 }
 
+static int chip_match_name(struct gpio_chip *chip, void *data)
+{
+	return !strncmp(chip->label, data, strlen(data));
+}
+
+int rtdm_gpiochip_find(struct device_node *from, void *compat,
+			  int type)
+{
+	struct rtdm_gpio_chip *rgc;
+	struct gpio_chip *chip;
+	int ret = -ENODEV;
+
+	if (!rtdm_available())
+		return -ENOSYS;
+
+	chip = gpiochip_find(compat, chip_match_name);
+	if (chip == NULL)
+		return ret;
+
+	ret = 0;
+	rgc = rtdm_gpiochip_alloc(chip, type);
+	if (IS_ERR(rgc))
+		ret = PTR_ERR(rgc);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rtdm_gpiochip_find);
+
 int rtdm_gpiochip_scan_of(struct device_node *from, const char *compat,
 			  int type)
 {
-- 
2.17.1



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

* [PATCH 5/5] testsuite/latmus: introduce latmus app
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
                   ` (2 preceding siblings ...)
  2021-02-20  5:53 ` [PATCH 4/5] drivers/gpio: core: introduce helper to find gpiochip hongzha1
@ 2021-02-20  5:53 ` hongzha1
  2021-02-20  9:39 ` [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu chensong
  2021-03-05 11:45 ` Philippe Gerum
  5 siblings, 0 replies; 9+ messages in thread
From: hongzha1 @ 2021-02-20  5:53 UTC (permalink / raw)
  To: xenomai

Signed-off-by: hongzha1 <hongzhan.chen@intel.com>

diff --git a/configure.ac b/configure.ac
index abe538dbd..8416bc013 100644
--- a/configure.ac
+++ b/configure.ac
@@ -954,6 +954,7 @@ AC_CONFIG_FILES([ \
 	lib/trank/Makefile \
 	testsuite/Makefile \
 	testsuite/latency/Makefile \
+	testsuite/latmus/Makefile \
 	testsuite/switchtest/Makefile \
 	testsuite/gpiotest/Makefile \
 	testsuite/gpiobench/Makefile \
diff --git a/include/rtdm/uapi/testing.h b/include/rtdm/uapi/testing.h
index b1723b9f8..d7e2707e1 100644
--- a/include/rtdm/uapi/testing.h
+++ b/include/rtdm/uapi/testing.h
@@ -256,6 +256,22 @@ struct latmus_result {
 #define RTTST_RTIOC_COBALT_LATIOC_RESET	\
 	_IO(RTIOC_TYPE_TESTING, 0x54)
 
+#define LATMON_NET_PORT 2306
+
+struct latmon_net_request {
+	uint32_t period_usecs;
+	uint32_t histogram_cells;
+} __attribute__((__packed__));
+
+struct latmon_net_data {
+	int32_t sum_lat_hi;
+	int32_t sum_lat_lo;
+	int32_t min_lat;
+	int32_t max_lat;
+	uint32_t overruns;
+	uint32_t samples;
+} __attribute__((__packed__));
+
 /** @} */
 
 #endif /* !_RTDM_UAPI_TESTING_H */
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index 4932f6d33..4baff2f19 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -1,10 +1,11 @@
 
-SUBDIRS = latency smokey gpiobench
+SUBDIRS = latency latmus smokey gpiobench
 
 if XENO_COBALT
 SUBDIRS += 		\
 	clocktest	\
 	gpiotest	\
+	latmus		\
 	spitest		\
 	switchtest	\
 	xeno-test
@@ -15,6 +16,7 @@ DIST_SUBDIRS =		\
 	gpiotest	\
 	gpiobench   \
 	latency		\
+	latmus		\
 	smokey		\
 	spitest		\
 	switchtest	\
diff --git a/testsuite/latmus/Makefile.am b/testsuite/latmus/Makefile.am
new file mode 100644
index 000000000..8eab3d740
--- /dev/null
+++ b/testsuite/latmus/Makefile.am
@@ -0,0 +1,18 @@
+testdir = @XENO_TEST_DIR@
+
+CCLD = $(top_srcdir)/scripts/wrap-link.sh $(CC)
+
+test_PROGRAMS = latmus
+
+latmus_SOURCES = latmus.c
+
+latmus_CPPFLAGS = 		\
+	$(XENO_USER_CFLAGS)	\
+	-I$(top_srcdir)/include
+
+latmus_LDFLAGS = @XENO_AUTOINIT_LDFLAGS@ $(XENO_POSIX_WRAPPERS)
+
+latmus_LDADD =			\
+	@XENO_CORE_LDADD@		\
+	@XENO_USER_LDADD@		\
+	-lpthread -lrt -lm
diff --git a/testsuite/latmus/latmus.c b/testsuite/latmus/latmus.c
new file mode 100644
index 000000000..e1abba216
--- /dev/null
+++ b/testsuite/latmus/latmus.c
@@ -0,0 +1,1444 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Derived from Xenomai Cobalt's latency & autotune utilities
+ * (http://git.xenomai.org/xenomai-3.git/)
+ * Copyright (C) 2014 Gilles Chanteperdrix <gch@xenomai.org>
+ * Copyright (C) 2018-2020 Philippe Gerum  <rpm@xenomai.org>
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <limits.h>
+#include <time.h>
+#include <string.h>
+#include <memory.h>
+#include <poll.h>
+#include <signal.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <linux/gpio.h>
+#include <rtdm/testing.h>
+#include <rtdm/gpio.h>
+
+#define COBALT_CPU_OOB      (1 << 0)
+#define COBALT_CPU_ISOL     (1 << 1)
+#define COBALT_CPU_OFFLINE  (1 << 2)
+
+#define ISOLATED_CPU_LIST "/sys/devices/system/cpu/isolated"
+#define OOB_CPU_LIST	"/sys/module/xenomai/parameters/supported_cpus"
+
+static cpu_set_t isolated_cpus;
+
+#define OOB_GPIO_LAT    1
+#define INBAND_GPIO_LAT 2
+
+#define LATMON_TIMEOUT_SECS  5
+
+static int test_irqlat, test_klat,
+	test_ulat, test_sirqlat,
+	test_gpiolat;
+
+static bool reset, background,
+	abort_on_switch = true;
+
+static int verbosity = 1,
+	abort_threshold;
+
+static bool tuning;
+
+static time_t timeout;
+
+static time_t start_time;
+
+static unsigned int period_usecs = 1000; /* 1ms */
+
+static int responder_priority = 98;
+
+static int responder_cpu = -1;
+
+static int responder_cpu_state;
+
+static struct in_addr gpio_monitor_ip;
+
+static sigset_t sigmask;
+
+static int latmus_fd = -1;
+
+static int gpio_infd = -1, gpio_outfd = -1;
+
+static int gpio_inpin, gpio_outpin;
+
+static int gpio_hdinflags = GPIOHANDLE_REQUEST_INPUT,
+	gpio_hdoutflags = GPIOHANDLE_REQUEST_OUTPUT;
+
+static int gpio_evinflags;
+
+static pthread_t responder, logger;
+
+static sem_t logger_done;
+
+static bool c_state_restricted;
+
+static bool force_cpu;
+
+#define short_optlist "ikusrqbKmtp:A:T:v::l:g::H:P:c:Z:z:I:O:C:"
+
+static const struct option options[] = {
+	{
+		.name = "irq",
+		.has_arg = no_argument,
+		.val = 'i'
+	},
+	{
+		.name = "kernel",
+		.has_arg = no_argument,
+		.val = 'k'
+	},
+	{
+		.name = "user",
+		.has_arg = no_argument,
+		.val = 'u'
+	},
+	{
+		.name = "sirq",
+		.has_arg = no_argument,
+		.val = 's'
+	},
+	{
+		.name = "reset",
+		.has_arg = no_argument,
+		.val = 'r'
+	},
+	{
+		.name = "quiet",
+		.has_arg = no_argument,
+		.val = 'q'
+	},
+	{
+		.name = "background",
+		.has_arg = no_argument,
+		.val = 'b'
+	},
+	{
+		.name = "keep-going",
+		.has_arg = no_argument,
+		.val = 'K'
+	},
+	{
+		.name = "measure",
+		.has_arg = no_argument,
+		.val = 'm',
+	},
+	{
+		.name = "tune",
+		.has_arg = no_argument,
+		.val = 't',
+	},
+	{
+		.name = "period",
+		.has_arg = required_argument,
+		.val = 'p',
+	},
+	{
+		.name = "timeout",
+		.has_arg = required_argument,
+		.val = 'T',
+	},
+	{
+		.name = "maxlat-abort",
+		.has_arg = required_argument,
+		.val = 'A',
+	},
+	{
+		.name = "verbose",
+		.has_arg = optional_argument,
+		.val = 'v',
+	},
+	{
+		.name = "lines",
+		.has_arg = required_argument,
+		.val = 'l',
+	},
+	{
+		.name = "plot",
+		.has_arg = optional_argument,
+		.val = 'g',
+	},
+	{
+		.name = "histogram",
+		.has_arg = required_argument,
+		.val = 'H',
+	},
+	{
+		.name = "priority",
+		.has_arg = required_argument,
+		.val = 'P',
+	},
+	{
+		.name = "cpu",
+		.has_arg = required_argument,
+		.val = 'c',
+	},
+	{
+		.name = "force-cpu",
+		.has_arg = required_argument,
+		.val = 'C',
+	},
+	{
+		.name = "oob-gpio",
+		.has_arg = required_argument,
+		.val = 'Z',
+	},
+	{
+		.name = "inband-gpio",
+		.has_arg = required_argument,
+		.val = 'z',
+	},
+	{
+		.name = "gpio-in",
+		.has_arg = required_argument,
+		.val = 'I',
+	},
+	{
+		.name = "gpio-out",
+		.has_arg = required_argument,
+		.val = 'O',
+	},
+	{ /* Sentinel */ }
+};
+
+static inline unsigned int stack_size(unsigned int size)
+{
+	return size > PTHREAD_STACK_MIN ? size : PTHREAD_STACK_MIN;
+}
+
+static void create_responder(pthread_t *tid, void *(*responder)(void *))
+{
+	struct sched_param param;
+	pthread_attr_t attr;
+	int ret;
+
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+	pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
+	param.sched_priority = responder_priority;
+	pthread_attr_setschedparam(&attr, &param);
+	pthread_attr_setstacksize(&attr,  stack_size(65536));
+	ret = pthread_create(tid, &attr, responder, NULL);
+	pthread_attr_destroy(&attr);
+	if (ret)
+		error(1, ret, "sampling thread");
+}
+
+#define ONE_BILLION	1000000000
+#define TEN_MILLIONS	10000000
+
+static int lat_xfd = -1;
+
+static int lat_sock = -1;
+
+static int data_lines = 21;
+
+static int32_t *histogram;
+
+static unsigned int histogram_cells = 200;
+
+static struct latmus_measurement last_bulk;
+
+static unsigned int all_overruns;
+
+static unsigned int spurious_inband_switches;
+
+static int32_t all_minlat = TEN_MILLIONS, all_maxlat = -TEN_MILLIONS;
+
+static int64_t all_sum;
+
+static int64_t all_samples;
+
+static time_t peak_time;
+
+static FILE *plot_fp;
+
+#define COBALT_LAT_OOB_GPIO      (COBALT_LAT_LAST + 1)
+#define COBALT_LAT_INBAND_GPIO   (COBALT_LAT_OOB_GPIO + 1)
+
+static int context_type = COBALT_LAT_USER;
+
+const char *context_labels[] = {
+	[COBALT_LAT_IRQ] = "irq",
+	[COBALT_LAT_SIRQ] = "sirq",
+	[COBALT_LAT_KERN] = "kernel",
+	[COBALT_LAT_USER] = "user",
+	[COBALT_LAT_OOB_GPIO] = "oob-gpio",
+	[COBALT_LAT_INBAND_GPIO] = "inband-gpio",
+};
+
+static void __log_results(struct latmus_measurement *meas)
+{
+	if (meas->min_lat < all_minlat)
+		all_minlat = meas->min_lat;
+	if (meas->max_lat > all_maxlat) {
+		peak_time = time(NULL) - start_time - 1;
+		all_maxlat = meas->max_lat;
+		if (abort_threshold && all_maxlat > abort_threshold) {
+			fprintf(stderr, "latency threshold is exceeded"
+				" (%d >= %.3f), aborting.\n",
+				abort_threshold,
+				(double)all_maxlat / 1000.0);
+			exit(102);
+		}
+	}
+
+	all_sum += meas->sum_lat;
+	all_samples += meas->samples;
+	all_overruns += meas->overruns;
+}
+
+static void log_results(struct latmus_measurement *meas,
+			unsigned int round)
+{
+	double min, avg, max, best, worst;
+	bool oops = false;
+	time_t now, dt;
+
+	if (verbosity > 0 && data_lines && (round % data_lines) == 0) {
+		time(&now);
+		dt = now - start_time - 1; /* -1s warm-up time */
+		printf("RTT|  %.2ld:%.2ld:%.2ld  (%s, %u us period,",
+			dt / 3600, (dt / 60) % 60, dt % 60,
+			context_labels[context_type], period_usecs);
+		if (context_type != COBALT_LAT_IRQ && context_type != COBALT_LAT_SIRQ)
+			printf(" priority %d,", responder_priority);
+		printf(" CPU%d%s)\n",
+			responder_cpu,
+			responder_cpu_state & COBALT_CPU_ISOL ? "" : "-noisol");
+		printf("RTH|%11s|%11s|%11s|%8s|%6s|%11s|%11s\n",
+		       "----lat min", "----lat avg",
+		       "----lat max", "-overrun", "---msw",
+		       "---lat best", "--lat worst");
+	}
+
+	__log_results(meas);
+	min = (double)meas->min_lat / 1000.0;
+	avg = (double)(meas->sum_lat / (int)meas->samples) / 1000.0;
+	max = (double)meas->max_lat / 1000.0;
+	best = (double)all_minlat / 1000.0;
+	worst = (double)all_maxlat / 1000.0;
+
+	/*
+	 * A trivial check on the reported values, so that we detect
+	 * and stop on obviously inconsistent results.
+	 */
+	if (min > max || min > avg || avg > max ||
+		min > worst || max > worst || avg > worst ||
+		best > worst || worst < best) {
+		oops = true;
+		verbosity = 1;
+	}
+
+	if (verbosity > 0)
+		printf("RTD|%11.3f|%11.3f|%11.3f|%8d|%6u|%11.3f|%11.3f\n",
+			min, avg, max,
+			all_overruns, spurious_inband_switches,
+			best, worst);
+
+	if (oops) {
+		fprintf(stderr, "results look weird, aborting.\n");
+		exit(103);
+	}
+}
+
+static inline void notify_start(void)
+{
+	if (timeout)
+		alarm(timeout + 1); /* +1 warm-up time */
+}
+
+static void create_logger(pthread_t *tid, void *(*logger)(void *), void *arg)
+{
+	struct sched_param param;
+	pthread_attr_t attr;
+	int ret;
+
+	sem_init(&logger_done, 0, 0);
+
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+	pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+	param.sched_priority = 0;
+	pthread_attr_setschedparam(&attr, &param);
+	pthread_attr_setstacksize(&attr, stack_size(65536));
+	ret = pthread_create(tid, &attr, logger, arg);
+	pthread_attr_destroy(&attr);
+	if (ret)
+		error(1, ret, "logger thread");
+}
+
+static void *xbuf_logger_thread(void *arg)
+{
+	struct latmus_measurement meas;
+	ssize_t ret, round = 0;
+
+	for (;;) {
+		ret = read(lat_xfd, &meas, sizeof(meas));
+		if (ret != sizeof(meas))
+			break;
+		log_results(&meas, round++);
+	}
+
+	/* Nobody waits for logger_done in timer mode. */
+
+	return NULL;
+}
+
+static void *timer_responder(void *arg)
+{
+	__u64 timestamp = 0;
+	struct timespec now;
+	int ret;
+
+	for (;;) {
+		ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_PULSE,
+				&timestamp);
+		if (ret) {
+			if (errno != EPIPE)
+				error(1, errno, "pulse failed");
+			timestamp = 0; /* Next period. */
+		} else {
+			clock_gettime(CLOCK_MONOTONIC, &now);
+			timestamp = (__u64)now.tv_sec * 1000000000 + now.tv_nsec;
+		}
+	}
+
+	return NULL;
+}
+
+static void *timer_test_sitter(void *arg)
+{
+	struct latmus_measurement_result mr;
+	struct latmus_result result;
+	int ret;
+
+	mr.last_ptr = (__u64)(uintptr_t)&last_bulk;
+	mr.histogram_ptr = (__u64)(uintptr_t)histogram;
+	mr.len = histogram ? histogram_cells * sizeof(int32_t) : 0;
+
+	result.data_ptr = (__u64)(uintptr_t)&mr;
+	result.len = sizeof(mr);
+
+	notify_start();
+
+	/* Run test until signal. */
+	ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_RUN, &result);
+	if (ret)
+		error(1, errno, "measurement failed");
+
+	return NULL;
+}
+
+static void setup_measurement_on_timer(void)
+{
+	struct latmus_setup setup;
+	pthread_attr_t attr;
+	pthread_t sitter;
+	int ret, sig;
+	char *rtp;
+
+	memset(&setup, 0, sizeof(setup));
+	setup.type = context_type;
+	setup.period = period_usecs * 1000ULL; /* ns */
+	setup.priority = responder_priority;
+	setup.cpu = responder_cpu;
+	setup.u.measure.hcells = histogram ? histogram_cells : 0;
+	ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_MEASURE, &setup);
+	if (ret < 0)
+		error(1, errno, "measurement setup failed");
+
+	/*open xnpipe to exchange message*/
+	if (asprintf(&rtp, "/dev/rtp%d", ret) < 0)
+		error(1, ENOMEM, "resolve rtp name");
+	lat_xfd = open(rtp, O_RDWR);
+	if (lat_xfd < 0)
+		error(1, -lat_xfd, "cannot create rtp");
+	free(rtp);
+	create_logger(&logger, xbuf_logger_thread, NULL);
+
+	if (context_type == COBALT_LAT_USER)
+		create_responder(&responder, timer_responder);
+
+	pthread_attr_init(&attr);
+	pthread_attr_setstacksize(&attr, stack_size(65536));
+	ret = pthread_create(&sitter, &attr, timer_test_sitter, NULL);
+	pthread_attr_destroy(&attr);
+	if (ret)
+		error(1, ret, "timer_test_sitter");
+
+	sigwait(&sigmask, &sig);
+	pthread_cancel(sitter);
+	pthread_join(sitter, NULL);
+
+	/*
+	 * Add results from the last incomplete bulk once the sitter
+	 * has returned to user-space from oob_ioctl(COBALT_LATIOC_RUN)
+	 * then exited, at which point such bulk contains meaningful
+	 * data.
+	 */
+	if (last_bulk.samples > 0)
+		__log_results(&last_bulk);
+}
+
+static void setup_gpio_pins(int *fds)
+{
+	int value;
+	int ret;
+
+	ret = ioctl(gpio_infd, GPIO_RTIOC_IRQEN, &gpio_evinflags);
+	if (ret)
+		error(1, errno, "ioctl(GPIO_RTIOC_IRQEN)");
+
+	value = 1;
+	ret = ioctl(gpio_infd, GPIO_RTIOC_TS, &value);
+	if (ret)
+		error(1, errno, "ioctl(GPIO_RTIOC_TS)");
+
+	value = 1;
+	ret = ioctl(gpio_outfd, GPIO_RTIOC_DIR_OUT, &value);
+	if (ret)
+		error(1, errno, "ioctl(GPIO_RTIOC_DIR_OUT)");
+
+	fds[0] = gpio_infd;
+	fds[1] = gpio_outfd;
+}
+
+static void *gpio_responder_thread(void *arg)
+{
+	int value;
+	struct rtdm_gpio_readout rdo;
+	const int ackval = 0;	/* Remote observes falling edges. */
+	int fds[2], ret;
+
+	setup_gpio_pins(fds);
+
+	for (;;) {
+		value = !ackval;
+		ret = write(fds[1], &value, sizeof(value));
+		if (ret < 0)
+			error(1, errno, "write failed");
+
+		ret = read(fds[0], &rdo,
+				sizeof(struct rtdm_gpio_readout));
+		if (ret != sizeof(struct rtdm_gpio_readout))
+			break;
+
+		value = ackval;
+		ret = write(fds[1], &value, sizeof(value));
+		if (ret < 0)
+			error(1, errno, "write failed");
+	}
+
+	return NULL;
+}
+
+static ssize_t read_net_data(void *buf, size_t len)
+{
+	ssize_t count = 0, ret;
+	struct pollfd pollfd;
+
+	pollfd.fd = lat_sock;
+	pollfd.events = POLLIN;
+	pollfd.revents = 0;
+
+	do {
+		/* Make sure to detect latmon unresponsivess. */
+		ret = poll(&pollfd, 1, LATMON_TIMEOUT_SECS * 1000);
+		if (ret <= 0)
+			return -ETIMEDOUT;
+		ret = recv(lat_sock, buf + count, len - count, 0);
+		if (ret <= 0)
+			return ret;
+		count += ret;
+	} while (count < len);
+
+	return count;
+}
+
+static void *sock_logger_thread(void *arg)
+{
+	struct latmus_measurement meas;
+	struct latmon_net_data ndata;
+	bool *no_response = arg;
+	ssize_t ret, round = 0;
+	int cell;
+
+	for (;;) {
+		ret = read_net_data(&ndata, sizeof(ndata));
+		if (ret <= 0)
+			goto unresponsive;
+
+		/*
+		 * Receiving an empty data record means that we got
+		 * the trailing data in the previous round, so
+		 * we are done with sample bulks now.
+		 */
+		if (ndata.samples == 0)
+			break;
+
+		/* This is valid sample data, log it. */
+		meas.sum_lat = ((__s64)ntohl(ndata.sum_lat_hi)) << 32 |
+			ntohl(ndata.sum_lat_lo);
+		meas.min_lat = ntohl(ndata.min_lat);
+		meas.max_lat = ntohl(ndata.max_lat);
+		meas.overruns = ntohl(ndata.overruns);
+		meas.samples = ntohl(ndata.samples);
+		log_results(&meas, round++);
+	}
+
+	if (histogram == NULL)
+		goto out;
+
+	ret = read_net_data(histogram, histogram_cells * sizeof(int32_t));
+	if (ret <= 0) {
+unresponsive:
+		*no_response = true;
+		kill(getpid(), SIGHUP);
+	} else {
+		for (cell = 0; cell < histogram_cells; cell++)
+			histogram[cell] = ntohl(histogram[cell]);
+	}
+out:
+	sem_post(&logger_done);
+
+	return NULL;
+}
+
+static void setup_measurement_on_gpio(void)
+{
+	struct latmon_net_request req;
+	struct sockaddr_in in_addr;
+	bool latmon_hung = false;
+	struct timespec timeout;
+	int ret, sig;
+
+	lat_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (lat_sock < 0)
+		error(1, errno, "socket()");
+
+	if (verbosity)
+		printf("connecting to latmon at %s:%d...\n",
+			inet_ntoa(gpio_monitor_ip), LATMON_NET_PORT);
+
+	memset(&in_addr, 0, sizeof(in_addr));
+	in_addr.sin_family = AF_INET;
+	in_addr.sin_addr = gpio_monitor_ip;
+	in_addr.sin_port = htons(LATMON_NET_PORT);
+	ret = connect(lat_sock, (struct sockaddr *)&in_addr,
+		sizeof(in_addr));
+	if (ret)
+		error(1, errno, "connect()");
+
+	if (verbosity && context_type == COBALT_LAT_INBAND_GPIO)
+		printf("CAUTION: measuring in-band response time (no COBALT there)\n");
+
+	create_responder(&responder, gpio_responder_thread);
+	create_logger(&logger, sock_logger_thread, &latmon_hung);
+
+	notify_start();
+	req.period_usecs = htonl(period_usecs); /* Non-zero, means start. */
+	req.histogram_cells = histogram ? htonl(histogram_cells) : 0;
+	ret = send(lat_sock, &req, sizeof(req), 0);
+	if (ret != sizeof(req))
+		error(1, errno, "send() start");
+
+	sigwait(&sigmask, &sig);
+
+	/*
+	 * From now on, we may wait up to LATMON_TIMEOUT_SECS
+	 * max. between messages from the remote latency monitor
+	 * before declaring it unresponsive.
+	 */
+	if (!latmon_hung) {
+		req.period_usecs = 0; /* Zero means stop. */
+		req.histogram_cells = 0;
+		ret = send(lat_sock, &req, sizeof(req), 0);
+		if (ret != sizeof(req)) {
+			error(1, errno, "send() stop");
+			latmon_hung = true;
+		} else {
+			clock_gettime(CLOCK_REALTIME, &timeout);
+			timeout.tv_sec += LATMON_TIMEOUT_SECS;
+			if (sem_timedwait(&logger_done, &timeout))
+				latmon_hung = true;
+		}
+	}
+
+	if (latmon_hung)
+		error(1, ETIMEDOUT, "latmon at %s is unresponsive",
+			inet_ntoa(gpio_monitor_ip));
+
+	close(lat_sock);
+}
+
+static void paste_file_in(const char *path, const char *header)
+{
+	char buf[BUFSIZ];
+	FILE *fp;
+
+	fp = fopen(path, "r");
+	if (fp == NULL)
+		return;
+
+	fprintf(plot_fp, "# %s", header ?: "");
+
+	while (fgets(buf, sizeof(buf), fp))
+		fputs(buf, plot_fp);
+
+	fclose(fp);
+}
+
+static void dump_gnuplot(time_t duration)
+{
+	int first, last, n;
+
+	if (all_samples == 0)
+		return;
+
+	fprintf(plot_fp, "# test started on: %s", ctime(&start_time));
+	paste_file_in("/proc/version", NULL);
+	paste_file_in("/proc/cmdline", NULL);
+	//fprintf(plot_fp, "# libevl version: %s\n", evl_get_version().version_string);
+	fprintf(plot_fp, "# sampling period: %u microseconds\n", period_usecs);
+	paste_file_in("/sys/devices/virtual/clock/monotonic/gravity",
+		"clock gravity: ");
+	paste_file_in("/sys/devices/system/clocksource/clocksource0/current_clocksource",
+		"clocksource: ");
+	paste_file_in("/sys/devices/system/clocksource/clocksource0/vdso_clocksource",
+		"vDSO access: ");
+	fprintf(plot_fp, "# context: %s\n", context_labels[context_type]);
+	if (!(test_irqlat || test_sirqlat)) {
+		fprintf(plot_fp, "# thread priority: %d\n", responder_priority);
+		fprintf(plot_fp, "# thread affinity: CPU%d%s\n",
+			responder_cpu,
+			responder_cpu_state & COBALT_CPU_ISOL ? "" : "-noisol");
+	}
+	if (c_state_restricted)
+		fprintf(plot_fp, "# C-state restricted\n");
+	fprintf(plot_fp, "# duration (hhmmss): %.2ld:%.2ld:%.2ld\n",
+		duration / 3600, (duration / 60) % 60, duration % 60);
+	fprintf(plot_fp, "# peak (hhmmss): %.2ld:%.2ld:%.2ld\n",
+		peak_time / 3600, (peak_time / 60) % 60, peak_time % 60);
+	if (all_overruns > 0)
+		fprintf(plot_fp, "# OVERRUNS: %u\n", all_overruns);
+	if (spurious_inband_switches > 0)
+		fprintf(plot_fp, "# IN-BAND SWITCHES: %u\n", spurious_inband_switches);
+	fprintf(plot_fp, "# min latency: %.3f\n",
+		(double)all_minlat / 1000.0);
+	fprintf(plot_fp, "# avg latency: %.3f\n",
+		(double)(all_sum / all_samples) / 1000.0);
+	fprintf(plot_fp, "# max latency: %.3f\n",
+		(double)all_maxlat / 1000.0);
+	fprintf(plot_fp, "# sample count: %lld\n",
+		(long long)all_samples);
+
+	for (n = 0; n < histogram_cells && histogram[n] == 0; n++)
+		;
+	first = n;
+
+	for (n = histogram_cells - 1; n >= 0 && histogram[n] == 0; n--)
+		;
+	last = n;
+
+	for (n = first; n < last; n++)
+		fprintf(plot_fp, "%d %d\n", n, histogram[n]);
+
+	/*
+	 * If we have outliers, display a '+' sign after the last cell
+	 * index.
+	 */
+	fprintf(plot_fp, "%d%s %d\n", last,
+		all_maxlat / 1000 >= histogram_cells ? "+" : "",
+		histogram[last]);
+}
+
+static void do_measurement(int type)
+{
+	const char *cpu_s = "";
+	time_t duration;
+
+	context_type = type;
+
+	if (plot_fp) {
+		histogram = malloc(histogram_cells * sizeof(int32_t));
+		if (histogram == NULL)
+			error(1, ENOMEM, "cannot get memory");
+	}
+
+	if (!(responder_cpu_state & COBALT_CPU_ISOL))
+		cpu_s = " (not isolated)";
+
+	if (verbosity > 0)
+		fprintf(stderr, "warming up on CPU%d%s...\n", responder_cpu, cpu_s);
+	else
+		fprintf(stderr, "running quietly for %ld seconds on CPU%d%s\n",
+			timeout, responder_cpu, cpu_s);
+
+	switch (type) {
+	case COBALT_LAT_OOB_GPIO:
+	case COBALT_LAT_INBAND_GPIO:
+		setup_measurement_on_gpio();
+		break;
+	default:
+		setup_measurement_on_timer();
+	}
+
+	duration = time(NULL) - start_time - 1; /* -1s warm-up time */
+	if (plot_fp) {
+		dump_gnuplot(duration);
+		if (plot_fp != stdout)
+			fclose(plot_fp);
+		free(histogram);
+	}
+
+	if (!timeout)
+		timeout = duration;
+
+	if (all_samples > 0)
+		printf("---|-----------|-----------|-----------|--------"
+			"|------|-------------------------\n"
+			"RTS|%11.3f|%11.3f|%11.3f|%8d|%6u|    "
+			"%.2ld:%.2ld:%.2ld/%.2ld:%.2ld:%.2ld\n",
+			(double)all_minlat / 1000.0,
+			(double)(all_sum / all_samples) / 1000.0,
+			(double)all_maxlat / 1000.0,
+			all_overruns, spurious_inband_switches,
+			duration / 3600, (duration / 60) % 60,
+			duration % 60, duration / 3600,
+			(timeout / 60) % 60, timeout % 60);
+
+	if (spurious_inband_switches > 0) {
+		if (all_samples > 0)
+			fputc('\n', stderr);
+		fprintf(stderr, "*** WARNING: unexpected switches to in-band mode detected,\n"
+		       "             latency figures displayed are NOT reliable.\n"
+		       "             Please submit a bug report upstream.\n");
+		if (abort_on_switch) {
+			abort_on_switch = false;
+			fprintf(stderr, "*** OOPS: aborting upon spurious switch to in-band mode.\n");
+		}
+	}
+
+	if (responder)
+		pthread_cancel(responder);
+
+	if (logger)
+		pthread_cancel(logger);
+}
+
+static void do_tuning(int type)
+{
+	struct latmus_result result;
+	struct latmus_setup setup;
+	pthread_t responder;
+	__s32 gravity;
+	int ret;
+
+	if (verbosity) {
+		printf("%s gravity...", context_labels[type]);
+		fflush(stdout);
+	}
+
+	memset(&setup, 0, sizeof(setup));
+	setup.type = type;
+	setup.period = period_usecs * 1000ULL; /* ns */
+	setup.priority = responder_priority;
+	setup.cpu = responder_cpu;
+	setup.u.tune.verbosity = verbosity;
+	ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_TUNE, &setup);
+	if (ret)
+		error(1, errno, "tuning setup failed (%s)", context_labels[type]);
+
+	if (type == COBALT_LAT_USER)
+		create_responder(&responder, timer_responder);
+
+	pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL);
+
+	notify_start();
+
+	result.data_ptr = (__u64)(uintptr_t)&gravity;
+	result.len = sizeof(gravity);
+	ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_RUN, &result);
+	if (ret)
+		error(1, errno, "measurement failed");
+
+	if (type == COBALT_LAT_USER)
+		pthread_cancel(responder);
+
+	if (verbosity)
+		printf("%u ns\n", gravity);
+}
+
+#ifdef CONFIG_XENO_COBALT
+
+#include <cobalt/uapi/syscall.h>
+
+static void sigdebug_handler(int sig, siginfo_t *si, void *context)
+{
+	unsigned int reason = sigdebug_reason(si);
+
+	if (reason > SIGDEBUG_WATCHDOG)
+		reason = SIGDEBUG_UNDEFINED;
+
+	switch (reason) {
+	case SIGDEBUG_UNDEFINED:
+	case SIGDEBUG_NOMLOCK:
+	case SIGDEBUG_WATCHDOG:
+		exit(99);
+	}
+
+	spurious_inband_switches++;
+	if (abort_on_switch)
+		kill(getpid(), SIGHUP);
+}
+#endif /* CONFIG_XENO_COBALT */
+
+static void set_cpu_affinity(void)
+{
+	cpu_set_t cpu_set;
+	int ret;
+
+	CPU_ZERO(&cpu_set);
+	CPU_SET(responder_cpu, &cpu_set);
+	ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
+	if (ret)
+		error(1, errno, "cannot set affinity to CPU%d",
+		      responder_cpu);
+}
+
+static void restrict_c_state(void)
+{
+	__s32 val = 0;
+	int fd;
+
+	fd = open("/dev/cpu_dma_latency", O_WRONLY);
+	if (fd < 0)
+		return;
+
+	if (write(fd, &val, sizeof(val) == sizeof(val)))
+		c_state_restricted = true;
+}
+
+static void parse_host_spec(const char *host, struct in_addr *in_addr)
+{
+	struct addrinfo hints, *res;
+	int ret;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_INET;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_ADDRCONFIG;
+	ret = getaddrinfo(host, NULL, &hints, &res);
+	if (ret)
+		error(1, ret == EAI_SYSTEM ? errno : ESRCH,
+			"getaddrinfo(%s)", host);
+
+	*in_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr;
+}
+
+static int parse_gpio_spec(const char *spec, int *pin,
+			int *hdflags, int *evflags)
+{
+	char *s, *dev, *p, *endptr, *devname;
+	int fd, ret;
+
+	s = strdup(spec);
+	dev = strtok(s, ",");
+	if (dev == NULL)
+		error(1, EINVAL, "no GPIO device in spec: %s", spec);
+
+	p = strtok(NULL, ",");
+	if (p == NULL)
+		error(1, EINVAL, "no GPIO pin in spec: %s", spec);
+
+	*pin = (int)strtol(p, &endptr, 10);
+	if (*pin < 0 || endptr == p)
+		error(1, EINVAL, "invalid GPIO pin number in spec: %s",
+			spec);
+
+	ret = asprintf(&devname, "/dev/rtdm/%s/gpio%d", dev, *pin);
+	if (ret < 0)
+		error(1, ENOMEM, "asprintf()");
+
+	p = strtok(NULL, ",");
+	if (evflags) {
+		if (p) {
+			if (!strcmp(p, "rising-edge"))
+				*evflags = GPIO_TRIGGER_EDGE_FALLING;
+			else if  (!strcmp(p, "falling-edge"))
+				*evflags = GPIO_TRIGGER_EDGE_RISING;
+			else
+				error(1, EINVAL, "invalid edge type in spec: %s",
+					spec);
+		} else	/* Default is rising edge. */
+			*evflags = GPIO_TRIGGER_EDGE_RISING;
+	} else if (p)
+		error(1, EINVAL, "trailing garbage in spec: %s",
+			spec);
+
+	fd = open(devname, O_RDONLY);
+	if (fd < 0)
+		error(1, errno, "open(%s)", devname);
+
+	free(devname);
+	free(s);
+
+	return fd;
+}
+
+static int get_realtime_cpu_set(cpu_set_t *cpuset)
+{
+	unsigned long long cpumask;
+	char buf[BUFSIZ], *p;
+	FILE *fp;
+	int cpu;
+
+	fp = fopen("/sys/module/xenomai/parameters/supported_cpus", "r");
+	if (fp == NULL)
+		return -ENOENT;
+
+	p = fgets(buf, sizeof(buf), fp);
+	fclose(fp);
+	if (p == NULL)
+		return -EBADF;
+
+	errno = 0;
+	cpumask = strtoll(p, NULL, 10);
+	if (cpumask == LLONG_MAX && errno == ERANGE)
+		cpumask = ULLONG_MAX;
+	for (cpu = 0; cpumask != 0; cpu++, cpumask >>= 1) {
+		if (cpumask & 1ULL)
+			CPU_SET(cpu, cpuset);
+	}
+
+	return 0;
+}
+
+static void parse_cpu_list(const char *path, cpu_set_t *cpuset)
+{
+	char *p, *range, *range_p = NULL, *id, *id_r;
+	int start, end, cpu;
+	char buf[BUFSIZ];
+	FILE *fp;
+
+	CPU_ZERO(cpuset);
+
+	fp = fopen(path, "r");
+	if (fp == NULL)
+		return;
+
+	if (!fgets(buf, sizeof(buf), fp))
+		goto out;
+
+	p = buf;
+	while ((range = strtok_r(p, ",", &range_p)) != NULL) {
+		if (*range == '\0' || *range == '\n')
+			goto next;
+		end = -1;
+		id = strtok_r(range, "-", &id_r);
+		if (id) {
+			start = atoi(id);
+			id = strtok_r(NULL, "-", &id_r);
+			if (id)
+				end = atoi(id);
+			else if (end < 0)
+				end = start;
+			for (cpu = start; cpu <= end; cpu++)
+				CPU_SET(cpu, cpuset);
+		}
+next:
+		p = NULL;
+	}
+out:
+	fclose(fp);
+}
+
+static void determine_responder_cpu(bool inband_test)
+{
+	cpu_set_t oob_cpus, best_cpus;
+	int cpu;
+
+	parse_cpu_list(ISOLATED_CPU_LIST, &isolated_cpus);
+	get_realtime_cpu_set(&oob_cpus);
+	if (responder_cpu >= 0) {
+		if (!inband_test && !CPU_ISSET(responder_cpu, &oob_cpus)) {
+			if (verbosity)
+				printf("CPU%d is not OOB-capable, "
+					"picking a better one\n",
+					responder_cpu);
+			goto pick_oob;
+		}
+		goto finish;
+	}
+
+	if (force_cpu)
+		goto finish;
+
+	if (inband_test)
+		goto pick_isolated;
+
+	/*
+	 * Pick a default CPU among the ones which are both
+	 * OOB-capable and isolated. If COBALT is not enabled, oob_cpus
+	 * is empty so there is no best choice.
+	 */
+	CPU_AND(&best_cpus, &isolated_cpus, &oob_cpus);
+	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+		if (CPU_ISSET(cpu, &best_cpus)) {
+			responder_cpu = cpu;
+			goto finish;
+		}
+	}
+
+	/*
+	 * If no best choice, pick the first OOB-capable CPU we can
+	 * find (if any).
+	 */
+pick_oob:
+	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+		if (CPU_ISSET(cpu, &oob_cpus)) {
+			responder_cpu = cpu;
+			goto finish;
+		}
+	}
+
+pick_isolated:
+	/*
+	 * This must be a kernel with no COBALT support or we
+	 * specifically need an isolated CPU.
+	 */
+	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+		if (CPU_ISSET(cpu, &isolated_cpus)) {
+			responder_cpu = cpu;
+			goto finish;
+		}
+	}
+
+	/* Out of luck, run on the current CPU. */
+	if (responder_cpu < 0)
+		responder_cpu = sched_getcpu();
+finish:
+	if (CPU_ISSET(responder_cpu, &isolated_cpus))
+		responder_cpu_state = COBALT_CPU_ISOL;
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "usage: latmus [options]:\n");
+	fprintf(stderr, "-m --measure            measure latency on timer event [default]\n");
+	fprintf(stderr, "-t --tune               tune the COBALT core timer\n");
+	fprintf(stderr, "-i --irq                measure/tune interrupt latency\n");
+	fprintf(stderr, "-k --kernel             measure/tune kernel scheduling latency\n");
+	fprintf(stderr, "-u --user               measure/tune user scheduling latency\n");
+	fprintf(stderr, "    [ if none of --irq, --kernel or --user is given,\n"
+			"      tune for all contexts ]\n");
+	fprintf(stderr, "-s --sirq               measure in-band response time to synthetic irq\n");
+	fprintf(stderr, "-p --period=<us>        sampling period\n");
+	fprintf(stderr, "-P --priority=<prio>    responder thread priority [=90]\n");
+	fprintf(stderr, "-c --cpu=<n>            pin responder thread to CPU [=current]\n");
+	fprintf(stderr, "-C --force-cpu=<n>      similar to -c, accept non-isolated CPU\n");
+	fprintf(stderr, "-r --reset              reset core timer gravity to factory default\n");
+	fprintf(stderr, "-b --background         run in the background (daemon mode)\n");
+	fprintf(stderr, "-K --keep-going         keep going on unexpected switch to in-band mode\n");
+	fprintf(stderr, "-A --max-abort=<us>     abort if maximum latency exceeds threshold\n");
+	fprintf(stderr, "-T --timeout=<t>[dhms]  stop measurement after <t> [d(ays)|h(ours)|m(inutes)|s(econds)]\n");
+	fprintf(stderr, "-v --verbose[=level]    set verbosity level [=1]\n");
+	fprintf(stderr, "-q --quiet              quiet mode (i.e. --verbose=0)\n");
+	fprintf(stderr, "-l --lines=<num>        result lines per page, 0 = no pagination [=21]\n");
+	fprintf(stderr, "-H --histogram[=<nr>]   set histogram size to <nr> cells [=200]\n");
+	fprintf(stderr, "-g --plot=<filename>    dump histogram data to file (gnuplot format)\n");
+	fprintf(stderr, "-Z --oob-gpio=<host>    measure COBALT response time to GPIO event via <host>\n");
+	fprintf(stderr, "-z --inband-gpio=<host> measure in-band response time to GPIO event via <host>\n");
+	fprintf(stderr, "-I --gpio-in=<spec>     input GPIO line configuration\n");
+	fprintf(stderr, "   with <spec> = gpiochip-devname,pin-number[,rising-edge|falling-edge]\n");
+	fprintf(stderr, "-O --gpio-out=<spec>    output GPIO line configuration\n");
+	fprintf(stderr, "   with <spec> = gpiochip-devname,pin-number\n");
+}
+
+static void bad_usage(int argc, char *const argv[])
+{
+	int last = optind < argc ? optind : argc - 1;
+
+	printf("** Uh, you lost me somewhere near '%s' (arg #%d)\n", argv[last], last);
+	usage();
+}
+
+int main(int argc, char *const argv[])
+{
+	int ret, c, spec, type, max_prio, lindex;
+	const char *plot_filename = NULL;
+	struct sigaction sa;
+	char *endptr;
+
+	opterr = 0;
+
+	for (;;) {
+		c = getopt_long(argc, argv, short_optlist, options, &lindex);
+		if (c == EOF)
+			break;
+
+		switch (c) {
+		case 0:
+			break;
+		case 'i':
+			test_irqlat = 1;
+			break;
+		case 'k':
+			test_klat = 1;
+			break;
+		case 'u':
+			test_ulat = 1;
+			break;
+		case 's':
+			test_sirqlat = 1;
+			break;
+		case 'r':
+			reset = true;
+			break;
+		case 'q':
+			verbosity = 0;
+			break;
+		case 'b':
+			background = true;
+			break;
+		case 'K':
+			abort_on_switch = false;
+			break;
+		case 'm':
+			tuning = false;
+			break;
+		case 't':
+			tuning = true;
+			break;
+		case 'p':
+			period_usecs = atoi(optarg);
+			if (period_usecs <= 0 || period_usecs > 1000000)
+				error(1, EINVAL, "invalid sampling period "
+				      "(0 < period < 1000000)");
+			break;
+		case 'A':
+			abort_threshold = atoi(optarg) * 1000; /* ns */
+			if (abort_threshold <= 0)
+				error(1, EINVAL, "invalid timeout");
+			break;
+		case 'T':
+			timeout = (int)strtol(optarg, &endptr, 10);
+			if (timeout < 0 || endptr == optarg)
+				error(1, EINVAL, "invalid timeout");
+			switch (*endptr) {
+			case 'd':
+				timeout *= 24;
+				/* Falldown wanted. */
+			case 'h':
+				timeout *= 60;
+				/* Falldown wanted. */
+			case 'm':
+				timeout *= 60;
+				break;
+			case 's':
+			case '\0':
+				break;
+			default:
+				error(1, EINVAL, "invalid time modifier: '%c'",
+					*endptr);
+			}
+			break;
+		case 'v':
+			verbosity = optarg ? atoi(optarg) : 1;
+			break;
+		case 'l':
+			data_lines = atoi(optarg);
+			break;
+		case 'g':
+			if (optarg && strcmp(optarg, "-"))
+				plot_filename = optarg;
+			else
+				plot_fp = stdout;
+			break;
+		case 'H':
+			histogram_cells = atoi(optarg);
+			if (histogram_cells < 1 || histogram_cells > 1000)
+				error(1, EINVAL, "invalid number of histogram cells "
+				      "(0 < cells <= 1000)");
+			break;
+		case 'P':
+			max_prio = sched_get_priority_max(SCHED_FIFO);
+			responder_priority = atoi(optarg);
+			if (responder_priority < 0 || responder_priority > max_prio)
+				error(1, EINVAL, "invalid thread priority "
+				      "(0 < priority < %d)", max_prio);
+			break;
+		case 'C':
+			force_cpu = true;
+			/* fallthrough */
+		case 'c':
+			responder_cpu = atoi(optarg);
+			if (responder_cpu < 0 || responder_cpu >= CPU_SETSIZE)
+				error(1, EINVAL, "invalid CPU number");
+			break;
+		case 'z':
+		case 'Z':
+			test_gpiolat = (c == 'z') + 1;
+			parse_host_spec(optarg, &gpio_monitor_ip);
+			break;
+		case 'I':
+			gpio_infd = parse_gpio_spec(optarg, &gpio_inpin,
+					&gpio_hdinflags, &gpio_evinflags);
+			break;
+		case 'O':
+			gpio_outfd = parse_gpio_spec(optarg, &gpio_outpin,
+					&gpio_hdoutflags, NULL);
+			break;
+		case '?':
+		default:
+			bad_usage(argc, argv);
+			return 1;
+		}
+	}
+
+	if (optind < argc) {
+		bad_usage(argc, argv);
+		return 1;
+	}
+
+	determine_responder_cpu(test_gpiolat == INBAND_GPIO_LAT);
+
+	setlinebuf(stdout);
+	setlinebuf(stderr);
+
+	if (!tuning && !timeout && !verbosity) {
+		fprintf(stderr, "--quiet requires --timeout, ignoring --quiet\n");
+		verbosity = 1;
+	}
+
+	if (background && verbosity) {
+		fprintf(stderr, "--background requires --quiet, taming verbosity down\n");
+		verbosity = 0;
+	}
+
+	if (tuning && (plot_filename || plot_fp)) {
+		fprintf(stderr, "--plot implies --measure, ignoring --plot\n");
+		plot_filename = NULL;
+		plot_fp = NULL;
+	}
+
+	if (background) {
+		signal(SIGHUP, SIG_IGN);
+		ret = daemon(0, 0);
+		if (ret)
+			error(1, errno, "cannot daemonize");
+	}
+
+	set_cpu_affinity();
+	restrict_c_state();
+
+	sigaddset(&sigmask, SIGINT);
+	sigaddset(&sigmask, SIGTERM);
+	sigaddset(&sigmask, SIGHUP);
+	sigaddset(&sigmask, SIGALRM);
+	pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
+
+#ifdef CONFIG_XENO_COBALT
+	sigemptyset(&sa.sa_mask);
+	sa.sa_sigaction = sigdebug_handler;
+	sa.sa_flags = SA_SIGINFO | SA_RESTART;
+	sigaction(SIGDEBUG, &sa, NULL);
+#endif
+
+	spec = test_irqlat || test_klat || test_ulat || test_sirqlat || test_gpiolat;
+	if (!tuning) {
+		if (!spec)
+			test_ulat = 1;
+		else if (test_irqlat + test_klat + test_ulat + test_sirqlat +
+			(!!test_gpiolat) > 1)
+			error(1, EINVAL, "only one of -u, -k, -i, -s, -z or -Z "
+			      "in measurement mode");
+	} else {
+		/* Default to tune for all contexts. */
+		if (!spec)
+			test_irqlat = test_klat = test_ulat = 1;
+		else if (test_sirqlat || test_gpiolat)
+			error(1, EINVAL, "-s/-z and -t are mutually exclusive");
+	}
+
+	if (!test_gpiolat) {
+		latmus_fd = open("/dev/rtdm/latmus", O_RDWR);
+		if (latmus_fd < 0)
+			error(1, errno, "cannot open latmus device");
+
+		if (reset) {
+			ret = ioctl(latmus_fd, RTTST_RTIOC_COBALT_LATIOC_RESET);
+			if (ret)
+				error(1, errno, "reset failed");
+		}
+	} else {
+		if (gpio_infd < 0 || gpio_outfd < 0)
+			error(1, EINVAL, "-[zZ] require -I, -O for GPIO settings");
+		if (test_gpiolat == OOB_GPIO_LAT) {
+			//gpio_hdinflags |= GPIOHANDLE_REQUEST_OOB;
+			//gpio_hdoutflags |= GPIOHANDLE_REQUEST_OOB;
+		}
+	}
+
+	time(&start_time);
+
+	if (!tuning) {
+		if (plot_filename) {
+			if (!access(plot_filename, F_OK))
+				error(1, EINVAL, "declining to overwrite %s",
+				      plot_filename);
+			plot_fp = fopen(plot_filename, "w");
+			if (plot_fp == NULL)
+				error(1, errno, "cannot open %s for writing",
+				      plot_filename);
+		}
+		type = test_irqlat ? COBALT_LAT_IRQ : test_klat ?
+			COBALT_LAT_KERN : test_sirqlat ? COBALT_LAT_SIRQ :
+			test_ulat ? COBALT_LAT_USER :
+			COBALT_LAT_LAST + test_gpiolat;
+		do_measurement(type);
+	} else {
+		if (verbosity)
+			printf("== latmus started for core tuning, "
+			       "period=%d microseconds (may take a while)\n",
+			       period_usecs);
+
+		if (test_irqlat)
+			do_tuning(COBALT_LAT_IRQ);
+
+		if (test_klat)
+			do_tuning(COBALT_LAT_KERN);
+
+		if (test_ulat)
+			do_tuning(COBALT_LAT_USER);
+
+		if (verbosity)
+			printf("== tuning completed after %ds\n",
+			       (int)(time(NULL) - start_time));
+	}
+
+	return 0;
+}
-- 
2.17.1



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

* Re: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
                   ` (3 preceding siblings ...)
  2021-02-20  5:53 ` [PATCH 5/5] testsuite/latmus: introduce latmus app hongzha1
@ 2021-02-20  9:39 ` chensong
  2021-02-22  0:51   ` Chen, Hongzhan
  2021-03-05 11:45 ` Philippe Gerum
  5 siblings, 1 reply; 9+ messages in thread
From: chensong @ 2021-02-20  9:39 UTC (permalink / raw)
  To: hongzha1, xenomai

why do we need a new interface instead of adding a new parameter(cpu) in 
old interface, in this case rtdm_task_init.

For backward compatibility?

On 2021年02月20日 13:53, hongzha1 via Xenomai wrote:
> Initialise and start a real-time task on specified cpu
>
> Signed-off-by: hongzha1 <hongzhan.chen@intel.com>
>
> diff --git a/include/cobalt/kernel/rtdm/driver.h b/include/cobalt/kernel/rtdm/driver.h
> index 733c49df8..6cdace324 100644
> --- a/include/cobalt/kernel/rtdm/driver.h
> +++ b/include/cobalt/kernel/rtdm/driver.h
> @@ -1024,6 +1024,9 @@ typedef void (*rtdm_task_proc_t)(void *arg);
>   int rtdm_task_init(rtdm_task_t *task, const char *name,
>   		   rtdm_task_proc_t task_proc, void *arg,
>   		   int priority, nanosecs_rel_t period);
> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
> +		rtdm_task_proc_t task_proc, void *arg,
> +		int priority, nanosecs_rel_t period);
>   int __rtdm_task_sleep(xnticks_t timeout, xntmode_t mode);
>   void rtdm_task_busy_sleep(nanosecs_rel_t delay);
>
> diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
> index b914fa312..7452850b7 100644
> --- a/kernel/cobalt/rtdm/drvlib.c
> +++ b/kernel/cobalt/rtdm/drvlib.c
> @@ -157,6 +157,79 @@ int rtdm_task_init(rtdm_task_t *task, const char *name,
>
>   EXPORT_SYMBOL_GPL(rtdm_task_init);
>
> +/**
> + * @brief Initialise and start a real-time task on specified cpu
> + *
> + * After initialising a task, the task handle remains valid and can be
> + * passed to RTDM services until either rtdm_task_destroy() or
> + * rtdm_task_join() was invoked.
> + *
> + * @param[in,out] task Task handle
> + * @param[in] cpu that task want to run on
> + * @param[in] name Optional task name
> + * @param[in] task_proc Procedure to be executed by the task
> + * @param[in] arg Custom argument passed to @c task_proc() on entry
> + * @param[in] priority Priority of the task, see also
> + * @ref rtdmtaskprio "Task Priority Range"
> + * @param[in] period Period in nanoseconds of a cyclic task, 0 for non-cyclic
> + * mode. Waiting for the first and subsequent periodic events is
> + * done using rtdm_task_wait_period().
> + *
> + * @return 0 on success, otherwise negative error code
> + *
> + * @coretags{secondary-only, might-switch}
> + */
> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
> +		rtdm_task_proc_t task_proc, void *arg,
> +		int priority, nanosecs_rel_t period)
> +{
> +	union xnsched_policy_param param;
> +	struct xnthread_start_attr sattr;
> +	struct xnthread_init_attr iattr;
> +	int err;
> +
> +	if (!realtime_core_enabled())
> +		return -ENOSYS;
> +
> +	iattr.name = name;
> +	iattr.flags = 0;
> +	iattr.personality = &xenomai_personality;
> +	iattr.affinity = *cpumask_of(cpu);
> +	param.rt.prio = priority;
> +
> +	err = xnthread_init(task, &iattr, &xnsched_class_rt, &param);
> +	if (err)
> +		return err;
> +
> +	/* We need an anonymous registry entry to obtain a handle for fast
> +	 * mutex locking.
> +	 */
> +	err = xnthread_register(task, "");
> +	if (err)
> +		goto cleanup_out;
> +
> +	if (period > 0) {
> +		err = xnthread_set_periodic(task, XN_INFINITE,
> +					    XN_RELATIVE, period);
> +		if (err)
> +			goto cleanup_out;
> +	}
> +
> +	sattr.mode = 0;
> +	sattr.entry = task_proc;
> +	sattr.cookie = arg;
> +	err = xnthread_start(task, &sattr);
> +	if (err)
> +		goto cleanup_out;
> +
> +	return 0;
> +
> +cleanup_out:
> +	xnthread_cancel(task);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(rtdm_task_init_on_cpu);
> +
>   #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
>   /**
>    * @brief Destroy a real-time task
>


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

* RE: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
  2021-02-20  9:39 ` [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu chensong
@ 2021-02-22  0:51   ` Chen, Hongzhan
  2021-02-22  9:16     ` Philippe Gerum
  0 siblings, 1 reply; 9+ messages in thread
From: Chen, Hongzhan @ 2021-02-22  0:51 UTC (permalink / raw)
  To: chensong, xenomai


>-----Original Message-----
>From: chensong <chensong@kylinos.cn> 
>Sent: Saturday, February 20, 2021 5:40 PM
>To: Chen, Hongzhan <hongzhan.chen@intel.com>; xenomai@xenomai.org
>Subject: Re: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
>
>why do we need a new interface instead of adding a new parameter(cpu) in 
>old interface, in this case rtdm_task_init.
>
>For backward compatibility?

We discussed it about introducing rtdm_task_init_on_cpu in [1]. Actually most of contents of
rtdm_task_init_on_cpu is really same with rtdm_task_init except cpu affinity. But rtdm_task_init 
was already called by several drivers such as net and testing. If we modify definition of 
rtdm_task_init , we have to modify all of these drivers that calling rtdm_task_init. 

[1]: https://xenomai.org/pipermail/xenomai/2021-February/044250.html
>
>On 2021?02?20? 13:53, hongzha1 via Xenomai wrote:
>> Initialise and start a real-time task on specified cpu
>>
>> Signed-off-by: hongzha1 <hongzhan.chen@intel.com>
>>
>> diff --git a/include/cobalt/kernel/rtdm/driver.h b/include/cobalt/kernel/rtdm/driver.h
>> index 733c49df8..6cdace324 100644
>> --- a/include/cobalt/kernel/rtdm/driver.h
>> +++ b/include/cobalt/kernel/rtdm/driver.h
>> @@ -1024,6 +1024,9 @@ typedef void (*rtdm_task_proc_t)(void *arg);
>>   int rtdm_task_init(rtdm_task_t *task, const char *name,
>>   		   rtdm_task_proc_t task_proc, void *arg,
>>   		   int priority, nanosecs_rel_t period);
>> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
>> +		rtdm_task_proc_t task_proc, void *arg,
>> +		int priority, nanosecs_rel_t period);
>>   int __rtdm_task_sleep(xnticks_t timeout, xntmode_t mode);
>>   void rtdm_task_busy_sleep(nanosecs_rel_t delay);
>>
>> diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
>> index b914fa312..7452850b7 100644
>> --- a/kernel/cobalt/rtdm/drvlib.c
>> +++ b/kernel/cobalt/rtdm/drvlib.c
>> @@ -157,6 +157,79 @@ int rtdm_task_init(rtdm_task_t *task, const char *name,
>>
>>   EXPORT_SYMBOL_GPL(rtdm_task_init);
>>
>> +/**
>> + * @brief Initialise and start a real-time task on specified cpu
>> + *
>> + * After initialising a task, the task handle remains valid and can be
>> + * passed to RTDM services until either rtdm_task_destroy() or
>> + * rtdm_task_join() was invoked.
>> + *
>> + * @param[in,out] task Task handle
>> + * @param[in] cpu that task want to run on
>> + * @param[in] name Optional task name
>> + * @param[in] task_proc Procedure to be executed by the task
>> + * @param[in] arg Custom argument passed to @c task_proc() on entry
>> + * @param[in] priority Priority of the task, see also
>> + * @ref rtdmtaskprio "Task Priority Range"
>> + * @param[in] period Period in nanoseconds of a cyclic task, 0 for non-cyclic
>> + * mode. Waiting for the first and subsequent periodic events is
>> + * done using rtdm_task_wait_period().
>> + *
>> + * @return 0 on success, otherwise negative error code
>> + *
>> + * @coretags{secondary-only, might-switch}
>> + */
>> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
>> +		rtdm_task_proc_t task_proc, void *arg,
>> +		int priority, nanosecs_rel_t period)
>> +{
>> +	union xnsched_policy_param param;
>> +	struct xnthread_start_attr sattr;
>> +	struct xnthread_init_attr iattr;
>> +	int err;
>> +
>> +	if (!realtime_core_enabled())
>> +		return -ENOSYS;
>> +
>> +	iattr.name = name;
>> +	iattr.flags = 0;
>> +	iattr.personality = &xenomai_personality;
>> +	iattr.affinity = *cpumask_of(cpu);
>> +	param.rt.prio = priority;
>> +
>> +	err = xnthread_init(task, &iattr, &xnsched_class_rt, &param);
>> +	if (err)
>> +		return err;
>> +
>> +	/* We need an anonymous registry entry to obtain a handle for fast
>> +	 * mutex locking.
>> +	 */
>> +	err = xnthread_register(task, "");
>> +	if (err)
>> +		goto cleanup_out;
>> +
>> +	if (period > 0) {
>> +		err = xnthread_set_periodic(task, XN_INFINITE,
>> +					    XN_RELATIVE, period);
>> +		if (err)
>> +			goto cleanup_out;
>> +	}
>> +
>> +	sattr.mode = 0;
>> +	sattr.entry = task_proc;
>> +	sattr.cookie = arg;
>> +	err = xnthread_start(task, &sattr);
>> +	if (err)
>> +		goto cleanup_out;
>> +
>> +	return 0;
>> +
>> +cleanup_out:
>> +	xnthread_cancel(task);
>> +	return err;
>> +}
>> +EXPORT_SYMBOL_GPL(rtdm_task_init_on_cpu);
>> +
>>   #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
>>   /**
>>    * @brief Destroy a real-time task
>>

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

* Re: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
  2021-02-22  0:51   ` Chen, Hongzhan
@ 2021-02-22  9:16     ` Philippe Gerum
  0 siblings, 0 replies; 9+ messages in thread
From: Philippe Gerum @ 2021-02-22  9:16 UTC (permalink / raw)
  To: Chen, Hongzhan; +Cc: chensong, xenomai


Chen, Hongzhan via Xenomai <xenomai@xenomai.org> writes:

>>-----Original Message-----
>>From: chensong <chensong@kylinos.cn> 
>>Sent: Saturday, February 20, 2021 5:40 PM
>>To: Chen, Hongzhan <hongzhan.chen@intel.com>; xenomai@xenomai.org
>>Subject: Re: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
>>
>>why do we need a new interface instead of adding a new parameter(cpu) in 
>>old interface, in this case rtdm_task_init.
>>
>>For backward compatibility?
>
> We discussed it about introducing rtdm_task_init_on_cpu in [1]. Actually most of contents of
> rtdm_task_init_on_cpu is really same with rtdm_task_init except cpu affinity. But rtdm_task_init 
> was already called by several drivers such as net and testing. If we modify definition of 
> rtdm_task_init , we have to modify all of these drivers that calling rtdm_task_init. 
>
> [1]: https://xenomai.org/pipermail/xenomai/2021-February/044250.html

Agreed. Not to speak of the custom drivers in the field, which would
break at build due to a new prototype of rtdm_task_init().

Besides, adding such a variant matches the mainline kernel naming scheme
(kthread_create vs kthread_create_on_node vs
kthread_create_on_cpu).

-- 
Philippe.


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

* Re: [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu
  2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
                   ` (4 preceding siblings ...)
  2021-02-20  9:39 ` [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu chensong
@ 2021-03-05 11:45 ` Philippe Gerum
  5 siblings, 0 replies; 9+ messages in thread
From: Philippe Gerum @ 2021-03-05 11:45 UTC (permalink / raw)
  To: hongzha1; +Cc: xenomai


hongzha1 via Xenomai <xenomai@xenomai.org> writes:

> Initialise and start a real-time task on specified cpu
>
> Signed-off-by: hongzha1 <hongzhan.chen@intel.com>
>
> diff --git a/include/cobalt/kernel/rtdm/driver.h b/include/cobalt/kernel/rtdm/driver.h
> index 733c49df8..6cdace324 100644
> --- a/include/cobalt/kernel/rtdm/driver.h
> +++ b/include/cobalt/kernel/rtdm/driver.h
> @@ -1024,6 +1024,9 @@ typedef void (*rtdm_task_proc_t)(void *arg);
>  int rtdm_task_init(rtdm_task_t *task, const char *name,
>  		   rtdm_task_proc_t task_proc, void *arg,
>  		   int priority, nanosecs_rel_t period);
> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
> +		rtdm_task_proc_t task_proc, void *arg,
> +		int priority, nanosecs_rel_t period);
>  int __rtdm_task_sleep(xnticks_t timeout, xntmode_t mode);
>  void rtdm_task_busy_sleep(nanosecs_rel_t delay);
>  
> diff --git a/kernel/cobalt/rtdm/drvlib.c b/kernel/cobalt/rtdm/drvlib.c
> index b914fa312..7452850b7 100644
> --- a/kernel/cobalt/rtdm/drvlib.c
> +++ b/kernel/cobalt/rtdm/drvlib.c
> @@ -157,6 +157,79 @@ int rtdm_task_init(rtdm_task_t *task, const char *name,
>  
>  EXPORT_SYMBOL_GPL(rtdm_task_init);
>  
> +/**
> + * @brief Initialise and start a real-time task on specified cpu
> + *
> + * After initialising a task, the task handle remains valid and can be
> + * passed to RTDM services until either rtdm_task_destroy() or
> + * rtdm_task_join() was invoked.
> + *
> + * @param[in,out] task Task handle
> + * @param[in] cpu that task want to run on
> + * @param[in] name Optional task name
> + * @param[in] task_proc Procedure to be executed by the task
> + * @param[in] arg Custom argument passed to @c task_proc() on entry
> + * @param[in] priority Priority of the task, see also
> + * @ref rtdmtaskprio "Task Priority Range"
> + * @param[in] period Period in nanoseconds of a cyclic task, 0 for non-cyclic
> + * mode. Waiting for the first and subsequent periodic events is
> + * done using rtdm_task_wait_period().
> + *
> + * @return 0 on success, otherwise negative error code
> + *
> + * @coretags{secondary-only, might-switch}
> + */
> +int rtdm_task_init_on_cpu(rtdm_task_t *task, int cpu, const char *name,
> +		rtdm_task_proc_t task_proc, void *arg,
> +		int priority, nanosecs_rel_t period)
> +{
> +	union xnsched_policy_param param;
> +	struct xnthread_start_attr sattr;
> +	struct xnthread_init_attr iattr;
> +	int err;
> +
> +	if (!realtime_core_enabled())
> +		return -ENOSYS;
> +
> +	iattr.name = name;
> +	iattr.flags = 0;
> +	iattr.personality = &xenomai_personality;
> +	iattr.affinity = *cpumask_of(cpu);

We should probably check cpu for validity first, I don't think
cpumask_of() does it (e.g. cpu_online(cpu) would ensure both validity
and online presence).

> +	param.rt.prio = priority;
> +
> +	err = xnthread_init(task, &iattr, &xnsched_class_rt, &param);
> +	if (err)
> +		return err;
> +
> +	/* We need an anonymous registry entry to obtain a handle for fast
> +	 * mutex locking.
> +	 */
> +	err = xnthread_register(task, "");
> +	if (err)
> +		goto cleanup_out;
> +
> +	if (period > 0) {
> +		err = xnthread_set_periodic(task, XN_INFINITE,
> +					    XN_RELATIVE, period);
> +		if (err)
> +			goto cleanup_out;
> +	}
> +
> +	sattr.mode = 0;
> +	sattr.entry = task_proc;
> +	sattr.cookie = arg;
> +	err = xnthread_start(task, &sattr);
> +	if (err)
> +		goto cleanup_out;
> +
> +	return 0;
> +
> +cleanup_out:
> +	xnthread_cancel(task);
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(rtdm_task_init_on_cpu);
> +
>  #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */
>  /**
>   * @brief Destroy a real-time task

Looks good. We may want to implement rtdm_task_init_cpu() as an inline
variant calling rtdm_task_init_on_cpu() with the current CPU number, to
prevent code duplication.

-- 
Philippe.


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

end of thread, other threads:[~2021-03-05 11:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-20  5:53 [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu hongzha1
2021-02-20  5:53 ` [PATCH 2/5] cobalt/rtdm: add interface to init timer " hongzha1
2021-02-20  5:53 ` [PATCH 3/5] rtdm/testing: latmus: introduce latmus driver hongzha1
2021-02-20  5:53 ` [PATCH 4/5] drivers/gpio: core: introduce helper to find gpiochip hongzha1
2021-02-20  5:53 ` [PATCH 5/5] testsuite/latmus: introduce latmus app hongzha1
2021-02-20  9:39 ` [PATCH 1/5] cobalt/rtdm: add new interface to init task on specified cpu chensong
2021-02-22  0:51   ` Chen, Hongzhan
2021-02-22  9:16     ` Philippe Gerum
2021-03-05 11:45 ` Philippe Gerum

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.