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

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)