[PATCH] serial inter-character timeout usage with async io

From: Harvey Chapman
Date: Tue Aug 07 2007 - 19:25:36 EST


I'm looking for comments, thanks.

This patch adds a timeout mode to n_tty that will only send SIGIO
signals (for input) when the UART's IIR indicates that a character
timeout has occurred. This reduces overhead by reducing the number of
SIGIO signals sent and consequently the unnecessary wake-ups of a
sleeping process waiting for serial input asynchronously.

two boolean values "timeout_enabled" and "timeout" are added to
tty_struct which enable/disable the mode and record whether an
inter-character timeout occurred during the last interrupt, respectively.

A new ioctl, TIOCSTIMEOUT, is added to set/clear "timeout_enabled." This
seemed to be the cleanest way to do that (that I saw, at least).

In n_tty_receive_buf(), some additional tests were added before the
kill_async() call to accommodate the old behavior and the newer timeout
behavior as well as adding a check to make sure the read buffer doesn't
overflow without a timeout received.

This should be 100% backwards compatible since the timeout mode is
disabled by default.

Currently, I only have this code enabled for i386 and arm, and only for
the 8250 and pxa serial drivers. If no one thinks this is a terrible
idea, I'll modify all of the other ioctl.h files and resubmit the patch.

Thanks,

Harvey


diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/drivers/char/n_tty.c linux-2.6.22.1/drivers/char/n_tty.c
--- linux-2.6.22.1.orig/drivers/char/n_tty.c 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/drivers/char/n_tty.c 2007-08-07 18:23:44.000000000 -0400
@@ -957,7 +957,17 @@ static void n_tty_receive_buf(struct tty

n_tty_set_room(tty);

- if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+ /*
+ * Make sure we're not in canonical mode. If not in timeout mode,
+ * use vmin setting as usual. If in timeout mode, only send if the
+ * last interrupt was a timeout. As a last resort, check to make
+ * sure the read buffer doesn't get too close to full. The factor of
+ * 4 used is an arbitrary number.
+ */
+ if (!tty->icanon &&
+ ((!tty->timeout_enabled && (tty->read_cnt >= tty->minimum_to_wake)) ||
+ tty->timeout ||
+ tty->receive_room < (4 * TTY_THRESHOLD_THROTTLE))) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/drivers/char/tty_ioctl.c
linux-2.6.22.1/drivers/char/tty_ioctl.c
--- linux-2.6.22.1.orig/drivers/char/tty_ioctl.c 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/drivers/char/tty_ioctl.c 2007-08-07
15:11:23.000000000 -0400
@@ -852,6 +852,15 @@ int n_tty_ioctl(struct tty_struct * tty,
(arg ? CLOCAL : 0));
mutex_unlock(&tty->termios_mutex);
return 0;
+
+ case TIOCSTIMEOUT:
+ if (get_user(arg, (unsigned int __user *) arg))
+ return -EFAULT;
+ tty->timeout_enabled = (arg ? 1 : 0);
+ tty->minimum_to_wake = 0;
+ MIN_CHAR(tty) = 0;
+ return 0;
+
default:
return -ENOIOCTLCMD;
}
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/drivers/serial/8250.c
linux-2.6.22.1/drivers/serial/8250.c
--- linux-2.6.22.1.orig/drivers/serial/8250.c 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/drivers/serial/8250.c 2007-08-07 17:29:24.000000000 -0400
@@ -1466,6 +1466,12 @@ static irqreturn_t serial8250_interrupt(

iir = serial_in(up, UART_IIR);
if (!(iir & UART_IIR_NO_INT)) {
+ /* Flag an inter-character timeout */
+ if (up->port.info->tty->timeout_enabled &&
+ (iir & UART_IIR_TOD) == UART_IIR_TOD)
+ up->port.info->tty->timeout = 1;
+ else
+ up->port.info->tty->timeout = 0;
serial8250_handle_port(up);

handled = 1;
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/drivers/serial/pxa.c linux-2.6.22.1/drivers/serial/pxa.c
--- linux-2.6.22.1.orig/drivers/serial/pxa.c 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/drivers/serial/pxa.c 2007-08-07 17:29:59.000000000 -0400
@@ -238,6 +238,12 @@ static inline irqreturn_t serial_pxa_irq
iir = serial_in(up, UART_IIR);
if (iir & UART_IIR_NO_INT)
return IRQ_NONE;
+ /* Flag an inter-character timeout */
+ if (up->port.info->tty->timeout_enabled &&
+ (iir & UART_IIR_TOD) == UART_IIR_TOD)
+ up->port.info->tty->timeout = 1;
+ else
+ up->port.info->tty->timeout = 0;
lsr = serial_in(up, UART_LSR);
if (lsr & UART_LSR_DR)
receive_chars(up, &lsr);
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/include/asm-arm/ioctls.h
linux-2.6.22.1/include/asm-arm/ioctls.h
--- linux-2.6.22.1.orig/include/asm-arm/ioctls.h 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/include/asm-arm/ioctls.h 2007-08-07
15:11:23.000000000 -0400
@@ -69,6 +69,7 @@
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
#define FIOQSIZE 0x545E
+#define TIOCSTIMEOUT 0x545F

/* Used for packet mode */
#define TIOCPKT_DATA 0
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/include/asm-i386/ioctls.h
linux-2.6.22.1/include/asm-i386/ioctls.h
--- linux-2.6.22.1.orig/include/asm-i386/ioctls.h 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/include/asm-i386/ioctls.h 2007-08-07
18:33:49.000000000 -0400
@@ -72,6 +72,7 @@
#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
+#define TIOCSTIMEOUT 0x5461

/* Used for packet mode */
#define TIOCPKT_DATA 0
diff -urp -x '*.orig' -x '*.rej' -x '*~'
linux-2.6.22.1.orig/include/linux/tty.h linux-2.6.22.1/include/linux/tty.h
--- linux-2.6.22.1.orig/include/linux/tty.h 2007-07-10
14:56:30.000000000 -0400
+++ linux-2.6.22.1/include/linux/tty.h 2007-08-07 15:11:23.000000000 -0400
@@ -227,6 +227,7 @@ struct tty_struct {
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
+ unsigned char timeout:1, timeout_enabled:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/