Appendix A. Appendix

A.1. Kernel Patch to support Latency Timing

Here is the diff between linux-2.6.14.2-rt12 and what I need to perform these latency timings.

diff -ur linux-2.6.14.2-rt12/drivers/serial/8250.c linux-2.6.14.2-rt12-latency/drivers/serial/8250.c
--- linux-2.6.14.2-rt12/drivers/serial/8250.c	2005-11-11 00:33:12.000000000 -0500
+++ linux-2.6.14.2-rt12-latency/drivers/serial/8250.c	2005-11-15 18:04:59.000000000 -0500
@@ -251,8 +251,91 @@
 	},
 };
-static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
+#ifdef CONFIG_SERIAL_LATENCY_TIMING
+
+// loadavg stuff borrowed from kernel/timer.c and fs/proc/proc_misc.c
+#include <linux/sched.h>
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+extern unsigned long avenrun[3];
+
+extern unsigned int cpu_khz;
+static u64 startTimer_G;
+
+#ifdef CONFIG_RCU_STATS
+struct rcu_ctrlblk {
+	raw_spinlock_t fliplock;
+	long completed;
+};
+extern struct rcu_ctrlblk rcu_ctrlblk;
+#endif
+
+static void
+latency_serial_out (struct uart_8250_port *up, int offset, int value)
 {
+	size_t i, len;
+	char timerBuf[100];
+	int a;
+	u64 endTimer;
+	struct timespec uptime;
+	struct timespec idle;
+	cputime_t idletime;
+
+	rdtscll (endTimer);
+#ifdef CONFIG_PREEMPT_RT
+	user_trace_stop ();
+#endif
+	a = avenrun[0] + (FIXED_1/200);
+	do_posix_clock_monotonic_gettime (&uptime);
+	idletime = cputime_add (init_task.utime, init_task.stime);
+	cputime_to_timespec (idletime, &idle);
+#ifdef CONFIG_RCU_STATS
+	sprintf (timerBuf, "**%llu %u %d.%02d %lu.%02lu %lu.%02lu %ld**\n",
+			endTimer - startTimer_G, cpu_khz,
+			LOAD_INT(a), LOAD_FRAC(a),
+			(unsigned long) uptime.tv_sec,
+			(uptime.tv_nsec / (NSEC_PER_SEC / 100)),
+			(unsigned long) idle.tv_sec,
+			(idle.tv_nsec / (NSEC_PER_SEC / 100)),
+			rcu_ctrlblk.completed);
+#else
+	sprintf (timerBuf, "**%llu %u %d.%02d %lu.%02lu %lu.%02lu**\n",
+			endTimer - startTimer_G, cpu_khz,
+			LOAD_INT(a), LOAD_FRAC(a),
+			(unsigned long) uptime.tv_sec,
+			(uptime.tv_nsec / (NSEC_PER_SEC / 100)),
+			(unsigned long) idle.tv_sec,
+			(idle.tv_nsec / (NSEC_PER_SEC / 100)));
+#endif
+	len = strlen (timerBuf);
+	for (i=0; i<len; ++i) {
+		switch (up->port.iotype) {
+			case UPIO_MEM:
+				writeb (timerBuf[i], up->port.membase + offset);
+				break;
+			case UPIO_MEM32:
+				writel (timerBuf[i], up->port.membase + offset);
+				break;
+			default:
+				outb (timerBuf[i], up->port.iobase + offset);
+				break;
+		}
+		/*
+		 * give the port enough time to cope. Note
+		 * that the timer has been stopped already,
+		 * so this does not increase the latency:
+		 */
+		udelay(100);
+	}
+}
+#endif
+
+static unsigned int
+serial_in(struct uart_8250_port *up, int offset)
+{
+	u8 ch8;
+	u32 ch32;
+
 	offset <<= up->port.regshift;
 	switch (up->port.iotype) {
@@ -261,19 +344,53 @@
 		return inb(up->port.iobase + 1);
 	case UPIO_MEM:
-		return readb(up->port.membase + offset);
+		ch8 = readb (up->port.membase + offset);
+#ifdef CONFIG_SERIAL_LATENCY_TIMING
+		if ((offset == UART_RX) && (ch8 == '\n')) {
+#ifdef CONFIG_PREEMPT_RT
+			user_trace_start ();
+#endif
+			rdtscll (startTimer_G);
+		}
+#endif
+		return ch8;
 	case UPIO_MEM32:
-		return readl(up->port.membase + offset);
+		ch32 = readl (up->port.membase + offset);
+#ifdef CONFIG_SERIAL_LATENCY_TIMING
+		if ((offset == UART_RX) && (ch32 == '\n')) {
+#ifdef CONFIG_PREEMPT_RT
+			user_trace_start ();
+#endif
+			rdtscll (startTimer_G);
+		}
+#endif
+		return ch32;
 	default:
-		return inb(up->port.iobase + offset);
+		ch8 = inb (up->port.iobase + offset);
+#ifdef CONFIG_SERIAL_LATENCY_TIMING
+		if ((offset == UART_RX) && (ch8 == '\n')) {
+#ifdef CONFIG_PREEMPT_RT
+			user_trace_start ();
+#endif
+			rdtscll (startTimer_G);
+		}
+#endif
+		return ch8;
 	}
 }
-static _INLINE_ void
+
+static void
 serial_out(struct uart_8250_port *up, int offset, int value)
 {
+#ifdef CONFIG_SERIAL_LATENCY_TIMING
+	if ((offset == UART_TX) && (value == '{')) {
+		latency_serial_out (up, offset, value);
+	}
+#endif
+
 	offset <<= up->port.regshift;
 	switch (up->port.iotype) {
diff -ur linux-2.6.14.2-rt12/drivers/serial/Kconfig linux-2.6.14.2-rt12-latency/drivers/serial/Kconfig
--- linux-2.6.14.2-rt12/drivers/serial/Kconfig	2005-11-11 00:33:12.000000000 -0500
+++ linux-2.6.14.2-rt12-latency/drivers/serial/Kconfig	2005-11-15 18:04:59.000000000 -0500
@@ -39,9 +39,16 @@
 	  Most people will say Y or M here, so that they can use serial mice,
 	  modems and similar devices connecting to the standard serial ports.
+config SERIAL_LATENCY_TIMING
+	tristate "serial latency timing"
+	depends on SERIAL_8250
+	---help---
+	  This modifies the 8250 serial driver for serial <-> user-space
+	  latency testing.
+
 config SERIAL_8250_CONSOLE
 	bool "Console on 8250/16550 and compatible serial port"
-	depends on SERIAL_8250=y
+	depends on SERIAL_8250=y && !SERIAL_LATENCY_TIMING
 	select SERIAL_CORE_CONSOLE
 	---help---
 	  If you say Y here, it will be possible to use a serial port as the
Only in linux-2.6.14.2-rt12/fs: exec.c.orig
diff -ur linux-2.6.14.2-rt12/kernel/rcupdate.c linux-2.6.14.2-rt12-latency/kernel/rcupdate.c
--- linux-2.6.14.2-rt12/kernel/rcupdate.c	2005-11-15 19:04:47.000000000 -0500
+++ linux-2.6.14.2-rt12-latency/kernel/rcupdate.c	2005-11-15 18:54:25.000000000 -0500
@@ -566,10 +566,11 @@
 	long		completed;	/* Number of last completed batch. */
 };
 static struct rcu_data rcu_data;
-static struct rcu_ctrlblk rcu_ctrlblk = {
+struct rcu_ctrlblk rcu_ctrlblk = {
 	.fliplock = RAW_SPIN_LOCK_UNLOCKED,
 	.completed = 0,
 };
+EXPORT_SYMBOL(rcu_ctrlblk);
 static DEFINE_PER_CPU(atomic_t [2], rcu_flipctr) =
 	{ ATOMIC_INIT(0), ATOMIC_INIT(0) };