From b3132168b6fd183a577ca9e4242951f7359f3a3f Mon Sep 17 00:00:00 2001 From: Will Sowerbutts Date: Fri, 20 Feb 2015 21:21:29 +0000 Subject: [PATCH] Kernel: Fix tty race condition, introduce new tty_sleeping() function. There are a few cases where a process goes to sleep while waiting for the UART to be ready to transmit. We want to be able to use interrupts to alert us to the UART becoming ready again (for example once the UART has drained or the flow control pins have changed). There was a race to put the process to sleep after tty_writeready() reports that it the UART is busy but before psleep() is called; the waking interrupt may arrive in that interval, wakeup() will not find any sleeping process, and no further interrupts will arrive after psleep() returns. The solution adopted here is to normally run with these waking interrupts disabled. When we put the process to sleep we enable the waking interrupts and call psleep() atomically. A new platform tty_sleeping() function is required to do the work of enabling the relevant interrupts for the given tty. The platform interrupt handler then disables these interrupts when calling tty_outproc() to awaken the process. --- Kernel/include/tty.h | 1 + Kernel/tty.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Kernel/include/tty.h b/Kernel/include/tty.h index 56e5e9fa..5c747aa2 100644 --- a/Kernel/include/tty.h +++ b/Kernel/include/tty.h @@ -202,6 +202,7 @@ typedef enum { /* provided by platform */ extern struct s_queue ttyinq[NUM_DEV_TTY + 1]; extern ttyready_t tty_writeready(uint8_t minor); +extern void tty_sleeping(uint8_t minor); extern void tty_putc(uint8_t minor, unsigned char c); extern void tty_setup(uint8_t minor); extern int tty_carrier(uint8_t minor); diff --git a/Kernel/tty.c b/Kernel/tty.c index 5317fed9..c0958684 100644 --- a/Kernel/tty.c +++ b/Kernel/tty.c @@ -390,8 +390,12 @@ void tty_putc_wait(uint8_t minor, unsigned char c) -1 (TTY_READY_LATER) -- blocked, don't spin (eg flow controlled) */ if (!udata.u_ininterrupt) { while ((t = tty_writeready(minor)) != TTY_READY_NOW) - if (t != TTY_READY_SOON || need_resched()) + if (t != TTY_READY_SOON || need_resched()){ + irqflags_t irq = di(); + tty_sleeping(minor); psleep(&ttydata[minor]); + irqrestore(irq); + } } tty_putc(minor, c); } -- 2.34.1