Hm, I just did, and it seems to work for me. I ran a bunch of different
usb-serial drivers through some tests, but these worked for me without
this patch. I don't know what stress tests Alan was running to show up
a problem.
Anyway, here's a patch with your new lock, if you want to apply it.
thanks,
greg k-h
diff -Nru a/drivers/char/tty_io.c b/drivers/char/tty_io.c
--- a/drivers/char/tty_io.c Sun Jan 12 22:27:42 2003
+++ b/drivers/char/tty_io.c Sun Jan 12 22:27:42 2003
@@ -159,6 +159,58 @@
extern void tx3912_rs_init(void);
extern void hvc_console_init(void);
+
+/*
+* This isn't even _trying_ to be fast!
+*/
+struct recursive_spinlock {
+ spinlock_t lock;
+ int lock_count;
+ struct task_struct *lock_owner;
+};
+
+static struct recursive_spinlock tty_lock = {
+ .lock = SPIN_LOCK_UNLOCKED,
+ .lock_count = 0,
+ .lock_owner = NULL
+};
+
+unsigned long tty_spin_lock(void)
+{
+ unsigned long flags;
+ struct task_struct *tsk;
+
+ local_irq_save(flags);
+ preempt_disable();
+ tsk = current;
+ if (spin_trylock(&tty_lock.lock))
+ goto got_lock;
+ if (tsk == tty_lock.lock_owner) {
+ WARN_ON(!tty_lock.lock_count);
+ tty_lock.lock_count++;
+ return flags;
+ }
+ spin_lock(&tty_lock.lock);
+got_lock:
+ WARN_ON(tty_lock.lock_owner);
+ WARN_ON(tty_lock.lock_count);
+ tty_lock.lock_owner = tsk;
+ tty_lock.lock_count = 1;
+ return flags;
+}
+
+void tty_spin_unlock(unsigned long flags)
+{
+ WARN_ON(tty_lock.lock_owner != current);
+ WARN_ON(!tty_lock.lock_count);
+ if (!--tty_lock.lock_count) {
+ tty_lock.lock_owner = NULL;
+ spin_unlock(&tty_lock.lock);
+ }
+ preempt_enable();
+ local_irq_restore(flags);
+}
+
static struct tty_struct *alloc_tty_struct(void)
{
struct tty_struct *tty;
@@ -460,7 +512,7 @@
{
unsigned long flags;
- local_irq_save(flags); // FIXME: is this safe?
+ flags = tty_spin_lock();
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
if (tty->driver.flush_buffer)
@@ -468,7 +520,7 @@
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
- local_irq_restore(flags); // FIXME: is this safe?
+ tty_spin_unlock(flags);
}
wake_up_interruptible(&tty->write_wait);
@@ -1926,7 +1978,7 @@
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
tty->flip.buf_num = 0;
- local_irq_save(flags); // FIXME: is this safe?
+ flags = tty_spin_lock();
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
} else {
@@ -1934,13 +1986,13 @@
fp = tty->flip.flag_buf;
tty->flip.buf_num = 1;
- local_irq_save(flags); // FIXME: is this safe?
+ flags = tty_spin_lock();
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
}
count = tty->flip.count;
tty->flip.count = 0;
- local_irq_restore(flags); // FIXME: is this safe?
+ tty_spin_unlock(flags);
tty->ldisc.receive_buf(tty, cp, fp, count);
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/