* 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