patch-2.2.15 linux/drivers/char/n_tty.c
Next file: linux/drivers/char/pcxx.c
Previous file: linux/drivers/char/n_hdlc.c
Back to the patch index
Back to the overall index
- Lines: 274
- Date:
Fri Apr 21 12:46:04 2000
- Orig file:
v2.2.14/drivers/char/n_tty.c
- Orig date:
Wed Oct 20 01:12:35 1999
diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/char/n_tty.c linux/drivers/char/n_tty.c
@@ -17,6 +17,10 @@
*
* This file may be redistributed under the terms of the GNU Public
* License.
+ *
+ * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of
+ * the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
+ * who actually finally proved there really was a race.
*/
#include <linux/types.h>
@@ -38,6 +42,7 @@
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
+#include <asm/spinlock.h>
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
@@ -59,11 +64,18 @@
static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
+ unsigned long flags;
+ /*
+ * The problem of stomping on the buffers ends here.
+ * Why didn't anyone see this one comming? --AJK
+ */
+ spin_lock_irqsave(&tty->read_lock, flags);
if (tty->read_cnt < N_TTY_BUF_SIZE) {
tty->read_buf[tty->read_head] = c;
tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
tty->read_cnt++;
}
+ spin_unlock_irqrestore(&tty->read_lock, flags);
}
/*
@@ -86,7 +98,11 @@
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
check_unthrottle(tty);
@@ -106,6 +122,7 @@
if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->read_wait);
+ wake_up_interruptible(&tty->link->poll_wait);
}
}
@@ -114,14 +131,19 @@
*/
ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
- if (tty->icanon) {
- if (!tty->canon_data) return 0;
+ unsigned long flags;
+ ssize_t n = 0;
- return (tty->canon_head > tty->read_tail) ?
+ spin_lock_irqsave(&tty->read_lock, flags);
+ if (!tty->icanon) {
+ n = tty->read_cnt;
+ } else if (tty->canon_data) {
+ n = (tty->canon_head > tty->read_tail) ?
tty->canon_head - tty->read_tail :
tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
}
- return tty->read_cnt;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
+ return n;
}
/*
@@ -283,6 +305,7 @@
{
enum { ERASE, WERASE, KILL } kill_type;
int head, seen_alnums;
+ unsigned long flags;
if (tty->read_head == tty->canon_head) {
/* opost('\a', tty); */ /* what do you think? */
@@ -294,15 +317,19 @@
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
finish_erasing(tty);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
@@ -324,8 +351,10 @@
else if (seen_alnums)
break;
}
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_head = head;
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!tty->erasing) {
@@ -413,6 +442,7 @@
}
put_tty_queue('\0', tty);
wake_up_interruptible(&tty->read_wait);
+ wake_up_interruptible(&tty->poll_wait);
}
static inline void n_tty_receive_overrun(struct tty_struct *tty)
@@ -443,6 +473,7 @@
else
put_tty_queue(c, tty);
wake_up_interruptible(&tty->read_wait);
+ wake_up_interruptible(&tty->poll_wait);
}
static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
@@ -605,8 +636,11 @@
tty->canon_data++;
if (tty->fasync)
kill_fasync(tty->fasync, SIGIO);
- if (tty->read_wait)
+ if (tty->read_wait || tty->poll_wait)
+ {
wake_up_interruptible(&tty->read_wait);
+ wake_up_interruptible(&tty->poll_wait);
+ }
return;
}
}
@@ -658,11 +692,13 @@
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
+ unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
+ spin_lock_irqsave(&tty->read_lock, cpuflags);
i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head));
memcpy(tty->read_buf + tty->read_head, cp, i);
@@ -676,6 +712,7 @@
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
+ spin_unlock_irqrestore(&tty->read_lock, cpuflags);
} else {
for (i=count, p = cp, f = fp; i; i--, p++) {
if (f)
@@ -707,8 +744,11 @@
if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
if (tty->fasync)
kill_fasync(tty->fasync, SIGIO);
- if (tty->read_wait)
+ if (tty->read_wait||tty->poll_wait)
+ {
wake_up_interruptible(&tty->read_wait);
+ wake_up_interruptible(&tty->poll_wait);
+ }
}
/*
@@ -850,15 +890,20 @@
{
int retval;
ssize_t n;
+ unsigned long flags;
retval = 0;
+ spin_lock_irqsave(&tty->read_lock, flags);
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
mb();
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
@@ -875,6 +920,7 @@
ssize_t retval = 0;
ssize_t size;
long timeout;
+ unsigned long flags;
do_it_again:
@@ -910,7 +956,7 @@
if (minimum) {
if (time)
tty->minimum_to_wake = 1;
- else if (!waitqueue_active(&tty->read_wait) ||
+ else if ((!waitqueue_active(&tty->read_wait) && !waitqueue_active(&tty->poll_wait)) ||
(tty->minimum_to_wake > minimum))
tty->minimum_to_wake = minimum;
} else {
@@ -993,9 +1039,11 @@
eol = test_and_clear_bit(tty->read_tail,
&tty->read_flags);
c = tty->read_buf[tty->read_tail];
+ spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
+ spin_unlock_irqrestore(&tty->read_lock, flags);
if (!eol || (c != __DISABLED_CHAR)) {
put_user(c, b++);
@@ -1040,7 +1088,7 @@
up(&tty->atomic_read);
remove_wait_queue(&tty->read_wait, &wait);
- if (!waitqueue_active(&tty->read_wait))
+ if (!waitqueue_active(&tty->read_wait) && !waitqueue_active(&tty->poll_wait))
tty->minimum_to_wake = minimum;
current->state = TASK_RUNNING;
@@ -1094,7 +1142,9 @@
nr -= num;
if (nr == 0)
break;
+ current->state = TASK_RUNNING;
get_user(c, b);
+ current->state = TASK_INTERRUPTIBLE;
if (opost(c, tty) < 0)
break;
b++; nr--;
@@ -1102,7 +1152,9 @@
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
} else {
+ current->state = TASK_RUNNING;
c = tty->driver.write(tty, 1, b, nr);
+ current->state = TASK_INTERRUPTIBLE;
if (c < 0) {
retval = c;
goto break_out;
@@ -1128,8 +1180,7 @@
{
unsigned int mask = 0;
- poll_wait(file, &tty->read_wait, wait);
- poll_wait(file, &tty->write_wait, wait);
+ poll_wait(file, &tty->poll_wait, wait);
if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty)))
mask |= POLLIN | POLLRDNORM;
if (tty->packet && tty->link->ctrl_status)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)