ttySAC0 issue on 2.6.31 after real time patch

mgalemin
Hi all, 

I applied 'patch-2.6.31.4-rt14' patch from www.kernel.org on 2.6.31 kernel
from buserror repo and I got an issue with serial port. When I try to
connect to my mini2440 using serial cable linux works very slowly and there
is a process [irq/71-s3c2440-] (serial port irq) which eats about 80-90% of
CPU. Moreover, when I work on the board over SSH everything is fine and I
can work quit comfortably. Could please anybody say how can I resolve this
issue? Thanks!

Maksim Galemin
I got some free time and dug into problem with ttySAC0 console. I applied
patch-2.6.31.5 and patch-2.6.31.5-rt18 on 2.6.31 linux kernel from Buserror
repo and after tracing kernel console function calls I realised that the
reason why linux works so slowly on console is busy loop on 71 interrupt
rising ([irq/71-s3c2440-] process). Loop was on s3c24xx_serial_tx_chars
function calling with empty output buffer (uart_circ_empty(xmit) == 1) that
caused calling s3c24xx_serial_stop_tx function with tx_enabled(port)
parameter equals to 0 (without calling disable_irq_nosync). This function
is a handler of tx_irq for ttySAC0 uart. Thus, at some moment IRQ rised and
never cleaned that caused busy loop. My solution was replacing
spin_lock_irqsave and spin_unlock_irqrestore functions in
drivers/serial/samsung.c and /drivers/serial/serial_core.c files with their
'atomic' analogs (atomic_spin_lock_irqsave and
atomic_spin_unlock_irqrestore). Below I posted patch as temporary
workaround for this issue:

--- a/drivers/serial/samsung.c  2009-10-07 19:48:09.000000000 +0300
+++ b/drivers/serial/samsung.c  2009-11-09 11:11:57.000000000 +0200
@@ -90,7 +90,7 @@
   unsigned int ucon, ufcon;
   int count = 10000;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
 
   while (--count && !s3c24xx_serial_txempty_nofifo(port))
     udelay(100);
@@ -104,7 +104,7 @@
   wr_regl(port, S3C2410_UCON, ucon);
 
   rx_enabled(port) = 1;
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void s3c24xx_serial_rx_disable(struct uart_port *port)
@@ -112,14 +112,14 @@
   unsigned long flags;
   unsigned int ucon;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
 
   ucon = rd_regl(port, S3C2410_UCON);
   ucon &= ~S3C2410_UCON_RXIRQMODE;
   wr_regl(port, S3C2410_UCON, ucon);
 
   rx_enabled(port) = 0;
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void s3c24xx_serial_stop_tx(struct uart_port *port)
@@ -359,7 +359,7 @@
   unsigned long flags;
   unsigned int ucon;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
 
   ucon = rd_regl(port, S3C2410_UCON);
 
@@ -370,7 +370,7 @@
 
   wr_regl(port, S3C2410_UCON, ucon);
 
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void s3c24xx_serial_shutdown(struct uart_port *port)
@@ -746,7 +746,7 @@
     ulcon |= S3C2410_LCON_PNONE;
   }
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
 
   dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
       ulcon, quot, udivslot);
@@ -790,7 +790,7 @@
   if ((termios->c_cflag & CREAD) == 0)
     port->ignore_status_mask |= RXSTAT_DUMMY_READ;
 
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static const char *s3c24xx_serial_type(struct uart_port *port)
--- a/drivers/serial/serial_core.c  2009-10-07 19:48:09.000000000 +0300
+++ b/drivers/serial/serial_core.c  2009-11-09 11:16:56.000000000 +0200
@@ -86,9 +86,9 @@
   struct uart_port *port = state->port;
   unsigned long flags;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   port->ops->stop_tx(port);
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void __uart_start(struct tty_struct *tty)
@@ -107,9 +107,9 @@
   struct uart_port *port = state->port;
   unsigned long flags;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   __uart_start(tty);
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void uart_tasklet_action(unsigned long data)
@@ -124,12 +124,12 @@
   unsigned long flags;
   unsigned int old;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   old = port->mctrl;
   port->mctrl = (old & ~clear) | set;
   if (old != port->mctrl)
     port->ops->set_mctrl(port, port->mctrl);
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 }
 
 #define uart_set_mctrl(port, set)  uart_update_mctrl(port, set, 0)
@@ -190,10 +190,10 @@
     }
 
     if (info->flags & UIF_CTS_FLOW) {
-      spin_lock_irq(&port->lock);
+      atomic_spin_lock_irq(&port->lock);
       if (!(port->ops->get_mctrl(port) & TIOCM_CTS))
         info->port.tty->hw_stopped = 1;
-      spin_unlock_irq(&port->lock);
+      atomic_spin_unlock_irq(&port->lock);
     }
 
     info->flags |= UIF_INITIALIZED;
@@ -468,13 +468,13 @@
   if (!circ->buf)
     return 0;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   if (uart_circ_chars_free(circ) != 0) {
     circ->buf[circ->head] = c;
     circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
     ret = 1;
   }
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
   return ret;
 }
 
@@ -514,7 +514,7 @@
   if (!circ->buf)
     return 0;
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   while (1) {
     c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
     if (count < c)
@@ -527,7 +527,7 @@
     count -= c;
     ret += c;
   }
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
 
   uart_start(tty);
   return ret;
@@ -539,9 +539,9 @@
   unsigned long flags;
   int ret;
 
-  spin_lock_irqsave(&state->port->lock, flags);
+  atomic_spin_lock_irqsave(&state->port->lock, flags);
   ret = uart_circ_chars_free(&state->info.xmit);
-  spin_unlock_irqrestore(&state->port->lock, flags);
+  atomic_spin_unlock_irqrestore(&state->port->lock, flags);
   return ret;
 }
 
@@ -551,9 +551,9 @@
   unsigned long flags;
   int ret;
 
-  spin_lock_irqsave(&state->port->lock, flags);
+  atomic_spin_lock_irqsave(&state->port->lock, flags);
   ret = uart_circ_chars_pending(&state->info.xmit);
-  spin_unlock_irqrestore(&state->port->lock, flags);
+  atomic_spin_unlock_irqrestore(&state->port->lock, flags);
   return ret;
 }
 
@@ -575,11 +575,11 @@
   port = state->port;
   pr_debug("uart_flush_buffer(%d) called\n", tty->index);
 
-  spin_lock_irqsave(&port->lock, flags);
+  atomic_spin_lock_irqsave(&port->lock, flags);
   uart_circ_clear(&state->info.xmit);
   if (port->ops->flush_buffer)
     port->ops->flush_buffer(port);
-  spin_unlock_irqrestore(&port->lock, flags);
+  atomic_spin_unlock_irqrestore(&port->lock, flags);
   tty_wakeup(tty);
 }
 
@@ -598,9 +598,9 @@
   else {
     port->x_char = ch;
     if (ch) {
-      spin_lock_irqsave(&port->lock, flags);
+      atomic_spin_lock_irqsave(&port->lock, flags);
       port->ops->start_tx(port);
-      spin_unlock_irqrestore(&port->lock, flags);
+      atomic_spin_unlock_irqrestore(&port->lock, flags);
     }
   }
 }
@@ -910,9 +910,9 @@
       !(tty->flags & (1 << TTY_IO_ERROR))) {
     result = port->mctrl;
 
-    spin_lock_irq(&port->lock);
+    atomic_spin_lock_irq(&port->lock);
     result |= port->ops->get_mctrl(port);
-    spin_unlock_irq(&port->lock);
+    atomic_spin_unlock_irq(&port->lock);
   }
   mutex_unlock(&state->mutex);
 
@@ -1011,20 +1011,20 @@
   /*
    * note the counters on entry
    */
-  spin_lock_irq(&port->lock);
+  atomic_spin_lock_irq(&port->lock);
   memcpy(&cprev, &port->icount, sizeof(struct uart_icount));
 
   /*
    * Force modem status interrupts on
    */
   port->ops->enable_ms(port);
-  spin_unlock_irq(&port->lock);
+  atomic_spin_unlock_irq(&port->lock);
 
   add_wait_queue(&state->info.delta_msr_wait, &wait);
   for (;;) {
-    spin_lock_irq(&port->lock);
+    atomic_spin_lock_irq(&port->lock);
     memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
-    spin_unlock_irq(&port->lock);
+    atomic_spin_unlock_irq(&port->lock);
 
     set_current_state(TASK_INTERRUPTIBLE);
 
@@ -1066,9 +1066,9 @@
   struct uart_icount cnow;
   struct uart_port *port = state->port;
 
-  spin_lock_irq(&port->lock);
+  atomic_spin_lock_irq(&port->lock);
   memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
-  spin_unlock_irq(&port->lock);
+  atomic_spin_unlock_irq(&port->lock);
 
   icount.cts         = cnow.cts;
   icount.dsr         = cnow.dsr;
@@ -1220,20 +1220,20 @@
 
   /* Handle turning off CRTSCTS */
   if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
-    spin_lock_irqsave(&state->port->lock, flags);
+    atomic_spin_lock_irqsave(&state->port->lock, flags);
     tty->hw_stopped = 0;
     __uart_start(tty);
-    spin_unlock_irqrestore(&state->port->lock, flags);
+    atomic_spin_unlock_irqrestore(&state->port->lock, flags);
   }
 
   /* Handle turning on CRTSCTS */
   if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
-    spin_lock_irqsave(&state->port->lock, flags);
+    atomic_spin_lock_irqsave(&state->po...stripped-down

Maksim Galemin
I also configured kernel to support high-resolution timers with 1 ms
period. If somebody interested in this stuff I'll post here patches.

Andreas Watterott
Hi Maksim,

Forum posts are limited to 10k characters. That is why your previous post
is stripped down. But I have now added an attachment function for
registered/logged-in users.

Regards,
Andreas

mini2440_enthus
Hi, Maksim,

could you post the high resolution timer patches for 2.6.31, as well as the
ones that solve the ttySAC0 IRQ problem?

Also, I noticed that S3C2440 has three resolution timers that can go as low
as a few microseconds. Is there a way to lower the resolution in your
patches?

Thanks,
R.

fatbrain
Hi Maksim,

Could you please post the kernel patch for high-resolution timers 
for 2.6.32 ....

thanks