a patch for serial port problem of SMP

From: Go Taniguchi (taniguchi@turbolinux.co.jp)
Date: Wed May 10 2000 - 01:53:55 EST


* Problem
The kernel can freeze when accessing the disks at the same time as doing
disk I/O
under stress situations.

* Conditions
The problem is most easily reproduced with SMP systems using IDE disks
and
transmitting to device /dev/ttyS*. The problem doesn't occur as often
with SCSI disks
but it will eventually occur given about 24 hours. There are no problems
if you
_receive_ data. Set to 9600bps, hardware CTS/RTS handshake, with the
receiver echoing
data.

* Send side Shell Script
#!/bin/sh
setserial /dev/ttyS0 baud_base 9600
stty -F /dev/ttyS0 crtscts
while :
do
    echo "123456789012345678901234567890" > /dev/ttyS0
done

* Receive side Shell Script
#!/bin/sh
setserial /dev/ttyS0 baud_base 9600
stty -F /dev/ttyS0 crtscts
while :
do
    cat /dev/ttyS0
    echo /dev/null > /dev/cua0
done

* Countermeasure
I added spin_lock code to the interrupt handler in serial.c. Within
free_irq I added a
function so that it doesn't assume the spin_lock has been released and
call
spin_unlock, which stops the kernel.

* patch for 2.2.15 include

--- linux/drivers/char/serial.c.orig Mon Apr 17 14:14:50 2000
+++ linux/drivers/char/serial.c Tue Apr 18 11:15:04 2000
@@ -149,6 +149,7 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/serial.h>
+#include <asm/spinlock.h>
 
 #ifdef SERIAL_INLINE
 #define _INLINE_ inline
@@ -576,6 +577,7 @@
         int status;
         struct async_struct * info;
         int pass_counter = 0;
+ unsigned long flags;
         struct async_struct *end_mark = 0;
 #ifdef CONFIG_SERIAL_MULTIPORT
         int first_multi = 0;
@@ -611,11 +613,13 @@
 #ifdef SERIAL_DEBUG_INTR
                 printk("status = %x...", status);
 #endif
+ spin_lock_irqsave(&info->tty->read_lock, flags);
                 if (status & UART_LSR_DR)
                         receive_chars(info, &status);
                 check_modem_status(info);
                 if (status & UART_LSR_THRE)
                         transmit_chars(info, 0);
+ spin_unlock_irqrestore(&info->tty->read_lock, flags);
 
         next:
                 info = info->next_port;
@@ -650,6 +654,7 @@
 {
         int status;
         int pass_counter = 0;
+ unsigned long flags;
         struct async_struct * info;
 #ifdef CONFIG_SERIAL_MULTIPORT
         int first_multi = 0;
@@ -675,11 +680,13 @@
 #ifdef SERIAL_DEBUG_INTR
                 printk("status = %x...", status);
 #endif
+ spin_lock_irqsave(&info->tty->read_lock, flags);
                 if (status & UART_LSR_DR)
                         receive_chars(info, &status);
                 check_modem_status(info);
                 if (status & UART_LSR_THRE)
                         transmit_chars(info, 0);
+ spin_unlock_irqrestore(&info->tty->read_lock, flags);
                 if (pass_counter++ > RS_ISR_PASS_LIMIT) {
 #if 0
                         printk("rs_single loop break.\n");
@@ -708,6 +715,7 @@
         int status;
         struct async_struct * info;
         int pass_counter = 0;
+ unsigned long flags;
         int first_multi= 0;
         struct rs_multiport_struct *multi;
 
@@ -738,11 +746,13 @@
 #ifdef SERIAL_DEBUG_INTR
                 printk("status = %x...", status);
 #endif
+ spin_lock_irqsave(&info->tty->read_lock, flags);
                 if (status & UART_LSR_DR)
                         receive_chars(info, &status);
                 check_modem_status(info);
                 if (status & UART_LSR_THRE)
                         transmit_chars(info, 0);
+ spin_unlock_irqrestore(&info->tty->read_lock, flags);
 
         next:
                 info = info->next_port;
@@ -1168,7 +1178,7 @@
         if (state->irq && (!IRQ_ports[state->irq] ||
                           !IRQ_ports[state->irq]->next_port)) {
                 if (IRQ_ports[state->irq]) {
- free_irq(state->irq, NULL);
+ free_irq2(state->irq, NULL);
                         retval = request_irq(state->irq, rs_interrupt_single,
                                              IRQ_T(state), "serial", NULL);
                         
@@ -1176,7 +1186,7 @@
                                 printk("serial shutdown: request_irq: error %d"
                                        " Couldn't reacquire IRQ.\n", retval);
                 } else
- free_irq(state->irq, NULL);
+ free_irq2(state->irq, NULL);
         }
 
         if (info->xmit_buf) {
--- linux/arch/i386/kernel/irq.c.orig Mon Apr 17 14:14:50 2000
+++ linux/arch/i386/kernel/irq.c Mon Apr 17 16:10:20 2000
@@ -958,6 +958,36 @@
         spin_unlock_irqrestore(&irq_controller_lock,flags);
 }
 
+void free_irq2(unsigned int irq, void *dev_id)
+{
+ struct irqaction * action, **p;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS)
+ return;
+
+ spin_lock_irqsave(&irq_controller_lock,flags);
+
+ for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now free it */
+ *p = action->next;
+ kfree(action);
+ if (!irq_desc[irq].action) {
+ irq_desc[irq].status |= IRQ_DISABLED;
+ irq_desc[irq].handler->shutdown(irq);
+ }
+ goto out;
+ }
+ printk("Trying to free free IRQ%d\n",irq);
+out:
+ spin_unlock_irqrestore(&irq_controller_lock,flags);
+}
+
+
+
 /*
  * IRQ autodetection code..
  *
--- linux/kernel/ksyms.c.orig Mon Apr 17 14:15:00 2000
+++ linux/kernel/ksyms.c Mon Apr 17 17:19:51 2000
@@ -280,6 +280,7 @@
 /* interrupt handling */
 EXPORT_SYMBOL(request_irq);
 EXPORT_SYMBOL(free_irq);
+EXPORT_SYMBOL(free_irq2);
 EXPORT_SYMBOL(bh_active);
 EXPORT_SYMBOL(bh_mask);
 EXPORT_SYMBOL(bh_mask_count);
--- linux/include/linux/sched.h.orig Mon Apr 17 18:16:48 2000
+++ linux/include/linux/sched.h Mon Apr 17 17:15:22 2000
@@ -581,6 +581,7 @@
                        const char *device,
                        void *dev_id);
 extern void free_irq(unsigned int irq, void *dev_id);
+extern void free_irq2(unsigned int irq, void *dev_id);
 
 /*
  * This has now become a routine instead of a macro, it sets a flag if

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon May 15 2000 - 21:00:15 EST