patch-2.3.99-pre1 linux/drivers/char/n_tty.c
Next file: linux/drivers/char/ppdev.c
Previous file: linux/drivers/char/mixcomwd.c
Back to the patch index
Back to the overall index
- Lines: 188
- Date:
Sun Mar 12 19:18:55 2000
- Orig file:
v2.3.51/linux/drivers/char/n_tty.c
- Orig date:
Fri Oct 22 13:21:48 1999
diff -u --recursive --new-file v2.3.51/linux/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>
@@ -59,11 +63,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 +97,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);
@@ -114,14 +129,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 +303,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 +315,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 +349,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) {
@@ -658,11 +685,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 +705,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)
@@ -850,15 +880,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 +910,7 @@
ssize_t retval = 0;
ssize_t size;
long timeout;
+ unsigned long flags;
do_it_again:
@@ -993,9 +1029,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++);
@@ -1094,7 +1132,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 +1142,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;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)