diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/n_tty.c linux-2.2.15-smp/drivers/char/n_tty.c --- linux-2.2.15-pre14/drivers/char/n_tty.c Wed Mar 15 14:29:36 2000 +++ linux-2.2.15-smp/drivers/char/n_tty.c Mon Mar 20 10:23:32 2000 @@ -1,6 +1,6 @@ /* * n_tty.c --- implements the N_TTY line discipline. - * + * * This code used to be in tty_io.c, but things are getting hairy * enough that it made sense to split things off. (The N_TTY * processing has changed so much that it's hardly recognizable, @@ -8,19 +8,22 @@ * * Note that the open routine for N_TTY is guaranteed never to return * an error. This is because Linux will fall back to setting a line - * to N_TTY if it can not switch to any other line discipline. + * to N_TTY if it can not switch to any other line discipline. * * Written by Theodore Ts'o, Copyright 1994. - * + * * This file also contains code originally written by Linus Torvalds, * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. - * + * * 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 + * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of * the patch by Andrew J. Kroll * who actually finally proved there really was a race. + * + * 3/00: Rid circular buffer of redundant read_cnt. Fix a few more races. + * Alan Modra */ #include @@ -47,9 +50,6 @@ #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif /* number of characters left in xmit buffer before select has we have room */ #define WAKEUP_CHARS 256 @@ -68,17 +68,16 @@ /* * 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_lock_irqsave(&tty->head_lock, flags); + if (CIRC_SPACE(tty->read.head, tty->read.tail, N_TTY_BUF_SIZE) != 0) { + tty->read.buf[tty->read.head] = c; + tty->read.head = (tty->read.head + 1) & (N_TTY_BUF_SIZE-1); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&tty->head_lock, flags); } -/* +/* * Check whether to call the driver.unthrottle function. * We test the TTY_THROTTLED bit first so that it always * indicates the current state. @@ -86,13 +85,13 @@ static void check_unthrottle(struct tty_struct * tty) { if (tty->count && - test_and_clear_bit(TTY_THROTTLED, &tty->flags) && + test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver.unthrottle) tty->driver.unthrottle(tty); } /* - * Reset the read buffer counters, clear the flags, + * Reset the read buffer counters, clear the flags, * and make sure the driver is unthrottled. Called * from n_tty_open() and n_tty_flush_buffer(). */ @@ -100,9 +99,11 @@ { 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); + spin_lock_irqsave(&tty->head_lock, flags); + spin_lock(&tty->tail_lock); + tty->read.head = tty->read.tail = 0; + spin_unlock(&tty->tail_lock); + spin_unlock_irqrestore(&tty->head_lock, flags); tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); check_unthrottle(tty); @@ -115,7 +116,7 @@ { /* clear everything and unthrottle the driver */ reset_buffer_flags(tty); - + if (!tty->link) return; @@ -130,18 +131,13 @@ */ ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { - unsigned long flags; ssize_t n = 0; - spin_lock_irqsave(&tty->read_lock, flags); if (!tty->icanon) { - n = tty->read_cnt; + n = CIRC_CNT(tty->read.head, tty->read.tail, N_TTY_BUF_SIZE); } 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); + n = CIRC_CNT(tty->canon_head, tty->read.tail, N_TTY_BUF_SIZE); } - spin_unlock_irqrestore(&tty->read_lock, flags); return n; } @@ -213,7 +209,7 @@ * things. */ static ssize_t opost_block(struct tty_struct * tty, - const unsigned char * inbuf, unsigned int nr) + const unsigned char * inbuf, unsigned int nr) { char buf[80]; int space; @@ -226,11 +222,11 @@ if (nr > space) nr = space; if (nr > sizeof(buf)) - nr = sizeof(buf); + nr = sizeof(buf); nr -= copy_from_user(buf, inbuf, nr); if (!nr) return 0; - + for (i = 0, cp = buf; i < nr; i++, cp++) { switch (*cp) { case '\n': @@ -268,7 +264,7 @@ break_out: if (tty->driver.flush_chars) tty->driver.flush_chars(tty); - i = tty->driver.write(tty, 0, buf, i); + i = tty->driver.write(tty, 0, buf, i); return i; } @@ -306,7 +302,7 @@ int head, seen_alnums; unsigned long flags; - if (tty->read_head == tty->canon_head) { + if (tty->read.head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; } @@ -316,19 +312,15 @@ 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); + spin_lock_irqsave(&tty->head_lock, flags); + tty->read.head = tty->canon_head; + spin_unlock_irqrestore(&tty->head_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); + spin_lock_irqsave(&tty->head_lock, flags); + tty->read.head = tty->canon_head; + spin_unlock_irqrestore(&tty->head_lock, flags); finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ @@ -340,9 +332,9 @@ } seen_alnums = 0; - while (tty->read_head != tty->canon_head) { - head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[head]; + while (tty->read.head != tty->canon_head) { + head = (tty->read.head - 1) & (N_TTY_BUF_SIZE-1); + c = tty->read.buf[head]; if (kill_type == WERASE) { /* Equivalent to BSD's ALTWERASE. */ if (isalnum(c) || c == '_') @@ -350,10 +342,9 @@ 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); + spin_lock_irqsave(&tty->head_lock, flags); + tty->read.head = head; + spin_unlock_irqrestore(&tty->head_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!tty->erasing) { @@ -366,11 +357,11 @@ echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { unsigned int col = tty->canon_column; - unsigned long tail = tty->canon_head; + int tail = tty->canon_head; /* Find the column of the last char. */ - while (tail != tty->read_head) { - c = tty->read_buf[tail]; + while (tail != tty->read.head) { + c = tty->read.buf[tail]; if (c == '\t') col = (col | 7) + 1; else if (iscntrl(c)) { @@ -383,7 +374,7 @@ /* should never happen */ if (tty->column > 0x80000000) - tty->column = 0; + tty->column = 0; /* Now backup to that column. */ while (tty->column > col) { @@ -412,7 +403,7 @@ if (kill_type == ERASE) break; } - if (tty->read_head == tty->canon_head) + if (tty->read.head == tty->canon_head) finish_erasing(tty); } @@ -479,13 +470,13 @@ put_tty_queue(c, tty); return; } - + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { start_tty(tty); return; } - + if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) @@ -511,12 +502,14 @@ finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { + if (CIRC_SPACE(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) < 2) { put_char('\a', tty); /* beep if no space */ return; } /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (tty->canon_head == tty->read.head) tty->canon_column = tty->column; echo_char(c, tty); } @@ -525,7 +518,7 @@ put_tty_queue(c, tty); return; } - + if (c == '\r') { if (I_IGNCR(tty)) return; @@ -577,20 +570,22 @@ } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { - unsigned long tail = tty->canon_head; + int tail = tty->canon_head; finish_erasing(tty); echo_char(c, tty); opost('\n', tty); - while (tail != tty->read_head) { - echo_char(tty->read_buf[tail], tty); + while (tail != tty->read.head) { + echo_char(tty->read.buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } return; } if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { + if (CIRC_SPACE(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) < 2) { put_char('\a', tty); return; } @@ -599,8 +594,8 @@ goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (tty->canon_head != tty->read_head) - set_bit(TTY_PUSH, &tty->flags); + if (tty->canon_head != tty->read.head) + set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } @@ -610,12 +605,14 @@ * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ if (L_ECHO(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { + if (CIRC_SPACE(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) < 2) { put_char('\a', tty); return; } /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (tty->canon_head == tty->read.head) tty->canon_column = tty->column; echo_char(c, tty); } @@ -627,9 +624,9 @@ put_tty_queue(c, tty); handle_newline: - set_bit(tty->read_head, &tty->read_flags); + set_bit(tty->read.head, &tty->read_flags); put_tty_queue(c, tty); - tty->canon_head = tty->read_head; + tty->canon_head = tty->read.head; tty->canon_data++; if (tty->fasync) kill_fasync(tty->fasync, SIGIO); @@ -638,10 +635,12 @@ return; } } - + finish_erasing(tty); if (L_ECHO(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { + if (CIRC_SPACE(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) < 2) { put_char('\a', tty); /* beep if no space */ return; } @@ -649,7 +648,7 @@ opost('\n', tty); else { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (tty->canon_head == tty->read.head) tty->canon_column = tty->column; echo_char(c, tty); } @@ -659,12 +658,10 @@ put_tty_queue(c, tty); put_tty_queue(c, tty); -} +} static int n_tty_receive_room(struct tty_struct *tty) { - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - /* * If we are doing input canonicalization, and there are no * pending newlines, let characters through without limit, so @@ -672,43 +669,48 @@ * characters will be beeped. */ if (tty->icanon && !tty->canon_data) - return N_TTY_BUF_SIZE; + return N_TTY_BUF_SIZE - 1; - if (left > 0) - return left; - return 0; + return CIRC_SPACE(tty->read.head, tty->read.tail, N_TTY_BUF_SIZE); } static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { - const unsigned char *p; - char *f, flags = TTY_NORMAL; - int i; - char buf[64]; - unsigned long cpuflags; - - if (!tty->read_buf) + 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); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; + int i; + unsigned long cpuflags; + + spin_lock_irqsave(&tty->head_lock, cpuflags); + i = CIRC_SPACE_TO_END(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE); + if (count < i) + i = count; + memcpy(tty->read.buf + tty->read.head, cp, i); + tty->read.head = (tty->read.head + i) & (N_TTY_BUF_SIZE-1); cp += i; count -= i; - 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); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; - spin_unlock_irqrestore(&tty->read_lock, cpuflags); + if (count) { + /* know tty->read.head == 0 */ + i = CIRC_SPACE(0, tty->read.tail, N_TTY_BUF_SIZE); + if (count < i) + i = count; + memcpy(tty->read.buf, cp, i); + tty->read.head = i; + } + spin_unlock_irqrestore(&tty->head_lock, cpuflags); } else { - for (i=count, p = cp, f = fp; i; i--, p++) { + int i; + const unsigned char *p; + char *f, flags = TTY_NORMAL; + char buf[64]; + + for (i = count, p = cp, f = fp; i; i--, p++) { if (f) flags = *f++; switch (flags) { @@ -735,7 +737,10 @@ tty->driver.flush_chars(tty); } - if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { + if (!tty->icanon + && CIRC_CNT(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) >= tty->minimum_to_wake) { if (tty->fasync) kill_fasync(tty->fasync, SIGIO); if (tty->read_wait) @@ -765,7 +770,7 @@ { if (!tty) return; - + tty->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; @@ -829,9 +834,10 @@ static void n_tty_close(struct tty_struct *tty) { n_tty_flush_buffer(tty); - if (tty->read_buf) { - free_page((unsigned long) tty->read_buf); - tty->read_buf = 0; + if (tty->read.buf) { + unsigned long page = (unsigned long) tty->read.buf; + tty->read.buf = 0; + free_page(page); } } @@ -840,13 +846,13 @@ if (!tty) return -EINVAL; - if (!tty->read_buf) { - tty->read_buf = (unsigned char *) + if (!tty->read.buf) { + tty->read.buf = (unsigned char *) get_free_page(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); - if (!tty->read_buf) + if (!tty->read.buf) return -ENOMEM; } - memset(tty->read_buf, 0, N_TTY_BUF_SIZE); + memset(tty->read.buf, 0, N_TTY_BUF_SIZE); reset_buffer_flags(tty); tty->column = 0; n_tty_set_termios(tty, 0); @@ -860,7 +866,9 @@ if (tty->icanon) { if (tty->canon_data) return 1; - } else if (tty->read_cnt >= (amt ? amt : 1)) + } else if (CIRC_CNT(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE) >= (amt ? amt : 1)) return 1; return 0; @@ -875,29 +883,42 @@ * the buffer to head pointer. */ static inline int copy_from_read_buf(struct tty_struct *tty, - unsigned char **b, - size_t *nr) + unsigned char **b, + size_t *nr) { int retval; - ssize_t n; + int n; unsigned long flags; + int tail; - 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; + /* This block is repeated in the case someone else removed + chars from the buffer while we were in copy_to_user. (SMP) + We can't hold tail_lock during copy_to_user, because + copy_to_user may fault, and end up enabling interrupts. + Note that CIRC_CNT_TO_END is carefully written so that we + don't need the lock when calculating count in buffer. + */ + while (1) { + tail = tty->read.tail; + n = CIRC_CNT_TO_END(tty->read.head, tail, N_TTY_BUF_SIZE); + if (*nr < n) + n = *nr; + if (n == 0) + return 0; + + retval = copy_to_user(*b, &tty->read.buf[tail], n); + spin_lock_irqsave(&tty->tail_lock, flags); + if (tty->read.tail == tail) + break; + spin_unlock_irqrestore(&tty->tail_lock, flags); } + + n -= retval; + tty->read.tail = (tty->read.tail + n) & (N_TTY_BUF_SIZE-1); + spin_unlock_irqrestore(&tty->tail_lock, flags); + *b += n; + *nr -= n; return retval; } @@ -915,8 +936,8 @@ do_it_again: - if (!tty->read_buf) { - printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); + if (!tty->read.buf) { + printk("n_tty_read_chan: called with read.buf == NULL?!?\n"); return -EIO; } @@ -987,11 +1008,11 @@ so that any interrupt will set the state back to TASK_RUNNING. */ current->state = TASK_INTERRUPTIBLE; - + if (((minimum - (b - buf)) < tty->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) tty->minimum_to_wake = (minimum - (b - buf)); - + if (!input_available_p(tty, 0)) { if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; @@ -1024,18 +1045,22 @@ if (tty->icanon) { /* N.B. avoid overrun if nr == 0 */ - while (nr && tty->read_cnt) { + while (nr != 0) { int eol; - eol = test_and_clear_bit(tty->read_tail, + spin_lock_irqsave(&tty->tail_lock, flags); + if (tty->read.head == tty->read.tail) { + spin_unlock_irqrestore(&tty->tail_lock, + flags); + break; + } + 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) & + c = tty->read.buf[tty->read.tail]; + tty->read.tail = ((tty->read.tail+1) & (N_TTY_BUF_SIZE-1)); - tty->read_cnt--; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&tty->tail_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { put_user(c, b++); nr--; @@ -1087,9 +1112,9 @@ if (size) { retval = size; if (nr) - clear_bit(TTY_PUSH, &tty->flags); + clear_bit(TTY_PUSH, &tty->flags); } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; + goto do_it_again; return retval; } @@ -1103,7 +1128,7 @@ ssize_t retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && + if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) { retval = tty_check_change(tty); diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/pty.c linux-2.2.15-smp/drivers/char/pty.c --- linux-2.2.15-pre14/drivers/char/pty.c Wed Mar 15 14:29:36 2000 +++ linux-2.2.15-smp/drivers/char/pty.c Thu Mar 16 11:58:40 2000 @@ -61,8 +61,6 @@ static struct pty_struct ptm_state[UNIX98_NR_MAJORS][NR_PTYS]; #endif -#define MIN(a,b) ((a) < (b) ? (a) : (b)) - static void pty_close(struct tty_struct * tty, struct file * filp) { if (!tty) @@ -136,21 +134,25 @@ const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c=0, n; - char *temp_buffer; + int c = 0; if (!to || tty->stopped) return 0; if (from_user) { + char *temp_buffer; down(&tty->flip.pty_sem); temp_buffer = &tty->flip.char_buf[0]; while (count > 0) { + int n, n2; /* check space so we don't copy needlessly */ - n = MIN(count, to->ldisc.receive_room(to)); + n = to->ldisc.receive_room(to); + if (count < n) + n = count; if (!n) break; - n = MIN(n, PTY_BUF_SIZE); + if (PTY_BUF_SIZE < n) + n = PTY_BUF_SIZE; n -= copy_from_user(temp_buffer, buf, n); if (!n) { if (!c) @@ -159,7 +161,9 @@ } /* check again in case the buffer filled up */ - n = MIN(n, to->ldisc.receive_room(to)); + n2 = to->ldisc.receive_room(to); + if (n2 < n) + n = n2; if (!n) break; buf += n; c += n; diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/selection.c linux-2.2.15-smp/drivers/char/selection.c --- linux-2.2.15-pre14/drivers/char/selection.c Mon Sep 28 20:59:42 1998 +++ linux-2.2.15-smp/drivers/char/selection.c Thu Mar 16 12:03:45 2000 @@ -25,10 +25,6 @@ #include #include -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /* Don't take this from : 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -295,7 +291,7 @@ int paste_selection(struct tty_struct *tty) { struct vt_struct *vt = (struct vt_struct *) tty->driver_data; - int pasted = 0, count; + int pasted = 0, count, n; struct wait_queue wait = { current, NULL }; poke_blanked_console(); @@ -307,7 +303,9 @@ continue; } count = sel_buffer_lth - pasted; - count = MIN(count, tty->ldisc.receive_room(tty)); + n = tty->ldisc.receive_room(tty); + if (n < count) + count = n; tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count); pasted += count; } diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/serial.c linux-2.2.15-smp/drivers/char/serial.c --- linux-2.2.15-pre14/drivers/char/serial.c Wed Mar 15 13:09:23 2000 +++ linux-2.2.15-smp/drivers/char/serial.c Tue Mar 21 00:13:45 2000 @@ -32,6 +32,11 @@ * 4/98: Added changes to support the ARM architecture proposed by * Russell King * + * 3/00: Rid circular buffer of redundant xmit_cnt and use SMP + * friendly spin_lock_irqsave rather than global cli. Fix a + * few races on freeing buffers too. + * Alan Modra + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -155,7 +160,7 @@ #endif static char *serial_name = "Serial driver"; -static char *serial_version = "4.27"; +static char *serial_version = "4.28"; static DECLARE_TASK_QUEUE(tq_serial); @@ -171,6 +176,17 @@ */ static struct async_struct *IRQ_ports[NR_IRQS]; +/* scr_info is for serial_{in,out}, and locking */ +static struct async_struct scr_info; +#ifdef SEPARATE_SERIAL_LOCKING +/* This is the way to go eventually, with a separate lock for each + serial port. */ +#define SERIAL_LOCK &info->xmit_lock +#else +/* I'm chicken. Just use one lock. */ +#define SERIAL_LOCK &scr_info.xmit_lock +#endif + #ifdef CONFIG_SERIAL_MULTIPORT static struct rs_multiport_struct rs_multiport[NR_IRQS]; #endif @@ -180,7 +196,7 @@ #endif static unsigned detect_uart_irq (struct serial_state * state); -static void autoconfig(struct serial_state * info); +static void autoconfig(struct serial_state * state); static void change_speed(struct async_struct *info, struct termios *old); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); @@ -212,9 +228,6 @@ static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif /* * tmp_buf is used as a temporary buffer by serial_write. We need to @@ -318,12 +331,12 @@ if (serial_paranoia_check(info, tty->device, "rs_stop")) return; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } static void rs_start(struct tty_struct *tty) @@ -334,12 +347,14 @@ if (serial_paranoia_check(info, tty->device, "rs_start")) return; - save_flags(flags); cli(); - if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) { + spin_lock_irqsave(SERIAL_LOCK, flags); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf + && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* @@ -459,7 +474,7 @@ static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) { int count; - + if (info->x_char) { serial_outp(info, UART_TX, info->x_char); info->state->icount.tx++; @@ -468,8 +483,9 @@ *intr_done = 0; return; } - if ((info->xmit_cnt <= 0) || info->tty->stopped || - info->tty->hw_stopped) { + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); return; @@ -477,14 +493,16 @@ count = info->xmit_fifo_size; do { - serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]); - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); info->state->icount.tx++; - if (--info->xmit_cnt <= 0) + if (info->xmit.head == info->xmit.tail) break; } while (--count > 0); - if (info->xmit_cnt < WAKEUP_CHARS) + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); #ifdef SERIAL_DEBUG_INTR @@ -493,7 +511,7 @@ if (intr_done) *intr_done = 0; - if (info->xmit_cnt <= 0) { + if (info->xmit.head == info->xmit.tail) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } @@ -585,11 +603,13 @@ #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt(%d)...", irq); #endif - + info = IRQ_ports[irq]; if (!info) return; - + + spin_lock(SERIAL_LOCK); + #ifdef CONFIG_SERIAL_MULTIPORT multi = &rs_multiport[irq]; if (multi->port_monitor) @@ -636,6 +656,7 @@ info->state->irq, first_multi, inb(multi->port_monitor)); #endif + spin_unlock(SERIAL_LOCK); #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif @@ -659,11 +680,13 @@ #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq); #endif - + info = IRQ_ports[irq]; if (!info || !info->tty) return; + spin_lock(SERIAL_LOCK); + #ifdef CONFIG_SERIAL_MULTIPORT multi = &rs_multiport[irq]; if (multi->port_monitor) @@ -694,6 +717,7 @@ info->state->irq, first_multi, inb(multi->port_monitor)); #endif + spin_unlock(SERIAL_LOCK); #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif @@ -714,15 +738,18 @@ #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_multi(%d)...", irq); #endif - + info = IRQ_ports[irq]; if (!info) return; + + spin_lock(SERIAL_LOCK); + multi = &rs_multiport[irq]; if (!multi->port1) { /* Should never happen */ printk("rs_interrupt_multi: NULL port1!\n"); - return; + goto out; } if (multi->port_monitor) first_multi = inb(multi->port_monitor); @@ -776,6 +803,8 @@ continue; break; } + out: + spin_unlock(SERIAL_LOCK); #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif @@ -838,16 +867,18 @@ info = IRQ_ports[i]; if (!info) continue; - save_flags(flags); cli(); + __save_flags(flags); __cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ if (info->next_port) { + spin_lock(SERIAL_LOCK); do { serial_out(info, UART_IER, 0); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); info = info->next_port; } while (info); -#ifdef CONFIG_SERIAL_MULTIPORT + spin_unlock(SERIAL_LOCK); +#ifdef CONFIG_SERIAL_MULTIPORT if (rs_multiport[i].port1) rs_interrupt_multi(i, NULL, NULL); else @@ -856,7 +887,7 @@ } else #endif /* CONFIG_SERIAL_SHARE_IRQ */ rs_interrupt_single(i, NULL, NULL); - restore_flags(flags); + __restore_flags(flags); } } last_strobe = jiffies; @@ -864,13 +895,13 @@ timer_active |= 1 << RS_TIMER; if (IRQ_ports[0]) { - save_flags(flags); cli(); + __save_flags(flags); __cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ rs_interrupt(0, NULL, NULL); #else rs_interrupt_single(0, NULL, NULL); #endif - restore_flags(flags); + __restore_flags(flags); timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2; } @@ -925,7 +956,7 @@ if (!page) return -ENOMEM; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); @@ -938,10 +969,10 @@ free_page(page); goto errout; } - if (info->xmit_buf) + if (info->xmit.buf) free_page(page); else - info->xmit_buf = (unsigned char *) page; + info->xmit.buf = (unsigned char *) page; #ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %d)...", info->line, state->irq); @@ -1086,7 +1117,7 @@ if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + info->xmit.head = info->xmit.tail = 0; /* * Set up serial timers... @@ -1107,6 +1138,7 @@ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } + spin_unlock_irqrestore(SERIAL_LOCK, flags); /* * and set the speed of the serial port @@ -1114,11 +1146,10 @@ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); return 0; errout: - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); return retval; } @@ -1142,7 +1173,7 @@ state->irq); #endif - save_flags(flags); cli(); /* Disable interrupts */ + spin_lock_irqsave(SERIAL_LOCK, flags); /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq @@ -1178,9 +1209,10 @@ free_irq(state->irq, NULL); } - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); } info->IER = 0; @@ -1227,7 +1259,7 @@ serial_outp(info, UART_IER, UART_IERX_SLEEP); } info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* @@ -1371,7 +1403,7 @@ */ if ((cflag & CREAD) == 0) info->ignore_status_mask |= UART_LSR_DR; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (uart_config[info->state->type].flags & UART_STARTECH) { serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, @@ -1390,7 +1422,7 @@ } serial_outp(info, UART_FCR, fcr); /* set fcr */ } - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } static void rs_put_char(struct tty_struct *tty, unsigned char ch) @@ -1401,19 +1433,19 @@ if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; - if (!tty || !info->xmit_buf) + if (!tty || !info->xmit.buf) return; - save_flags(flags); cli(); - if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - restore_flags(flags); - return; - } - - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE-1; - info->xmit_cnt++; - restore_flags(flags); + spin_lock_irqsave(SERIAL_LOCK, flags); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) + goto out; + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + out: + spin_unlock_irqrestore(SERIAL_LOCK, flags); } static void rs_flush_chars(struct tty_struct *tty) @@ -1424,14 +1456,16 @@ if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) return; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } static int rs_write(struct tty_struct * tty, int from_user, @@ -1444,16 +1478,18 @@ if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; - if (!tty || !info->xmit_buf || !tmp_buf) + if (!tty || !info->xmit.buf || !tmp_buf) return 0; - save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { - c = MIN(count, - MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; if (c <= 0) break; @@ -1463,41 +1499,45 @@ ret = -EFAULT; break; } - cli(); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - info->xmit_head = ((info->xmit_head + c) & + spin_lock_irqsave(SERIAL_LOCK, flags); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & (SERIAL_XMIT_SIZE-1)); - info->xmit_cnt += c; - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { + spin_lock_irqsave(SERIAL_LOCK, flags); while (1) { - cli(); - c = MIN(count, - MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; if (c <= 0) { - restore_flags(flags); break; } - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & (SERIAL_XMIT_SIZE-1)); - info->xmit_cnt += c; - restore_flags(flags); buf += c; count -= c; ret += c; } + spin_unlock_irqrestore(SERIAL_LOCK, flags); } - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(info->IER & UART_IER_THRI)) { + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } @@ -1507,14 +1547,10 @@ static int rs_write_room(struct tty_struct *tty) { struct async_struct *info = (struct async_struct *)tty->driver_data; - int ret; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static int rs_chars_in_buffer(struct tty_struct *tty) @@ -1523,7 +1559,7 @@ if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; - return info->xmit_cnt; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); } static void rs_flush_buffer(struct tty_struct *tty) @@ -1533,9 +1569,9 @@ if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); + spin_lock_irqsave(SERIAL_LOCK, flags); + info->xmit.head = info->xmit.tail = 0; + spin_unlock_irqrestore(SERIAL_LOCK, flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1589,9 +1625,9 @@ if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } static void rs_unthrottle(struct tty_struct * tty) @@ -1616,9 +1652,9 @@ } if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* @@ -1784,9 +1820,9 @@ unsigned int result; unsigned long flags; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); status = serial_in(info, UART_LSR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value); } @@ -1799,9 +1835,9 @@ unsigned long flags; control = info->MCR; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); status = serial_in(info, UART_MSR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) #ifdef TIOCM_OUT1 @@ -1867,16 +1903,14 @@ default: return -EINVAL; } - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); return 0; } static int do_autoconfig(struct async_struct * info) { - int retval; - if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1891,10 +1925,7 @@ (info->state->type != PORT_UNKNOWN)) info->state->irq = detect_uart_irq(info->state); - retval = startup(info); - if (retval) - return retval; - return 0; + return startup(info); } /* @@ -1910,14 +1941,14 @@ if (!info->port) return; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (break_state == -1) serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); else serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } #ifdef CONFIG_SERIAL_MULTIPORT @@ -2094,18 +2125,18 @@ * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); /* note the counters on entry */ cprev = info->state->icount; - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); cnow = info->state->icount; /* atomic copy */ - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -2126,9 +2157,9 @@ * RI where only 0->1 is counted. */ case TIOCGICOUNT: - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); cnow = info->state->icount; - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); p_cuser = (struct serial_icounter_struct *) arg; error = put_user(cnow.cts, &p_cuser->cts); if (error) return error; @@ -2183,9 +2214,9 @@ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* Handle transition away from B0 status */ @@ -2196,9 +2227,9 @@ !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= UART_MCR_RTS; } - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); serial_out(info, UART_MCR, info->MCR); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* Handle turning off CRTSCTS */ @@ -2242,13 +2273,12 @@ state = info->state; - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + goto errout; } #ifdef SERIAL_DEBUG_OPEN @@ -2274,8 +2304,7 @@ if (state->count) { DBG_CNT("before DEC-2"); MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + goto errout; } info->flags |= ASYNC_CLOSING; /* @@ -2310,6 +2339,8 @@ */ rs_wait_until_sent(tty, info->timeout); } + spin_unlock_irqrestore(SERIAL_LOCK, flags); + shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); @@ -2329,7 +2360,10 @@ ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); MOD_DEC_USE_COUNT; - restore_flags(flags); + return; + + errout: + spin_unlock_irqrestore(SERIAL_LOCK, flags); } /* @@ -2363,8 +2397,8 @@ char_time = char_time / 5; if (char_time == 0) char_time = 1; - if (timeout) - char_time = MIN(char_time, timeout); + if (timeout && timeout < char_time) + char_time = timeout; /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't @@ -2502,21 +2536,21 @@ printk("block_til_ready before block: ttys%d, count = %d\n", state->line, state->count); #endif - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); info->blocked_open++; while (1) { - save_flags(flags); cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS)); - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { @@ -2585,6 +2619,7 @@ info->tqueue.routine = do_softint; info->tqueue.data = info; info->state = sstate; + spin_lock_init(&info->xmit_lock); if (sstate->info) { kfree_s(info, sizeof(struct async_struct)); *ret_info = sstate->info; @@ -2707,7 +2742,7 @@ static inline int line_info(char *buf, struct serial_state *state) { - struct async_struct *info = state->info, scr_info; + struct async_struct *info = state->info; char stat_buf[30], control, status; int ret; unsigned long flags; @@ -2724,20 +2759,21 @@ /* * Figure out the current RS-232 lines */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ + if (!info) + info = &scr_info; + + spin_lock_irqsave(SERIAL_LOCK, flags); - info->magic = SERIAL_MAGIC; + if (info == &scr_info) { info->port = state->port; info->flags = state->flags; - info->quot = 0; - info->tty = 0; } - save_flags(flags); cli(); + status = serial_in(info, UART_MSR); - control = info ? info->MCR : serial_in(info, UART_MCR); - restore_flags(flags); - + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + + spin_unlock_irqrestore(SERIAL_LOCK, flags); + stat_buf[0] = 0; stat_buf[1] = 0; if (control & UART_MCR_RTS) @@ -2861,12 +2897,26 @@ int irq; unsigned long irqs; unsigned char save_mcr, save_ier; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ - + struct async_struct *info = state->info; #ifdef CONFIG_SERIAL_MANY_PORTS unsigned char save_ICP=0; /* no warning */ unsigned short ICP=0; +#endif + + if (!info) + info = &scr_info; + spin_lock(SERIAL_LOCK); + + if (info == &scr_info) { + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + } + +#ifdef CONFIG_SERIAL_MANY_PORTS if (state->flags & ASYNC_FOURPORT) { ICP = (state->port & 0xFE0) | 0x01F; save_ICP = inb_p(ICP); @@ -2874,44 +2924,38 @@ (void) inb_p(ICP); } #endif - scr_info.magic = SERIAL_MAGIC; - scr_info.port = state->port; - scr_info.flags = state->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif - /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); - save_mcr = serial_inp(&scr_info, UART_MCR); - save_ier = serial_inp(&scr_info, UART_IER); - serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + save_mcr = serial_inp(info, UART_MCR); + save_ier = serial_inp(info, UART_IER); + serial_outp(info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); - serial_outp(&scr_info, UART_MCR, 0); + serial_outp(info, UART_MCR, 0); udelay (10); if (state->flags & ASYNC_FOURPORT) { - serial_outp(&scr_info, UART_MCR, + serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); } else { - serial_outp(&scr_info, UART_MCR, + serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } - serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ - (void)serial_inp(&scr_info, UART_LSR); - (void)serial_inp(&scr_info, UART_RX); - (void)serial_inp(&scr_info, UART_IIR); - (void)serial_inp(&scr_info, UART_MSR); - serial_outp(&scr_info, UART_TX, 0xFF); + serial_outp(info, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); + serial_outp(info, UART_TX, 0xFF); udelay (20); irq = probe_irq_off(irqs); - serial_outp(&scr_info, UART_MCR, save_mcr); - serial_outp(&scr_info, UART_IER, save_ier); + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_IER, save_ier); #ifdef CONFIG_SERIAL_MANY_PORTS if (state->flags & ASYNC_FOURPORT) outb_p(save_ICP, ICP); #endif + spin_unlock(SERIAL_LOCK); return (irq > 0)? irq : 0; } @@ -2922,28 +2966,30 @@ * whether or not this UART is a 16550A, since this will determine * whether or not we can use its FIFO features. */ -static void autoconfig(struct serial_state * state) +static void autoconfig(struct serial_state *state) { unsigned char status1, status2, scratch, scratch2; - struct async_struct *info, scr_info; + struct async_struct *info = state->info; unsigned long flags; state->type = PORT_UNKNOWN; if (!state->port) return; - - info = &scr_info; /* This is just for serial_{in,out} */ - info->magic = SERIAL_MAGIC; - info->port = state->port; - info->flags = state->flags; + if (!info) + info = &scr_info; + + spin_lock_irqsave(SERIAL_LOCK, flags); + + if (info == &scr_info) { + info->port = state->port; + info->flags = state->flags; #ifdef CONFIG_HUB6 - info->hub6 = state->hub6; + info->hub6 = state->hub6; #endif + } - save_flags(flags); cli(); - /* * Do a simple existence test first; if we fail this, there's * no point trying anything else. @@ -2958,10 +3004,8 @@ outb(0xff, 0x080); scratch2 = serial_inp(info, UART_IER); serial_outp(info, UART_IER, scratch); - if (scratch2) { - restore_flags(flags); - return; /* We failed; there's nothing here */ - } + if (scratch2) + goto out; /* We failed; there's nothing here */ /* * Check to see if a UART is really there. Certain broken @@ -2978,10 +3022,8 @@ serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); - if (status1 != 0x90) { - restore_flags(flags); - return; - } + if (status1 != 0x90) + goto out; } scratch2 = serial_in(info, UART_LCR); @@ -3043,10 +3085,8 @@ } state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; - if (state->type == PORT_UNKNOWN) { - restore_flags(flags); - return; - } + if (state->type == PORT_UNKNOWN) + goto out; request_region(info->port,8,"serial(auto)"); @@ -3066,8 +3106,8 @@ UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); serial_outp(info, UART_IER, 0); - - restore_flags(flags); + out: + spin_unlock_irqrestore(SERIAL_LOCK, flags); } int register_serial(struct serial_struct *req); @@ -3105,6 +3145,9 @@ sizeof(struct rs_multiport_struct)); #endif } + memset(&scr_info, 0, sizeof scr_info); + scr_info.magic = SERIAL_MAGIC; + spin_lock_init(&scr_info.xmit_lock); #ifdef CONFIG_SERIAL_CONSOLE /* * The interrupt of the serial console port @@ -3224,9 +3267,9 @@ int i; unsigned long flags; struct serial_state *state; + struct async_struct *info = &scr_info; - save_flags(flags); - cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); for (i = 0; i < NR_PORTS; i++) { if (rs_table[i].port == req->port) break; @@ -3238,12 +3281,12 @@ break; } if (i == NR_PORTS) { - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); return -1; } state = &rs_table[i]; if (rs_table[i].count) { - restore_flags(flags); + spin_unlock_irqrestore(SERIAL_LOCK, flags); printk("Couldn't configure serial #%d (port=%d,irq=%d): " "device already open\n", i, req->port, req->irq); return -1; @@ -3252,13 +3295,13 @@ state->port = req->port; state->flags = req->flags; + spin_unlock_irqrestore(SERIAL_LOCK, flags); + autoconfig(state); if (state->type == PORT_UNKNOWN) { - restore_flags(flags); printk("register_serial(): autoconfig failed\n"); return -1; } - restore_flags(flags); if ((state->flags & ASYNC_AUTO_IRQ) && (state->port != 0)) state->irq = detect_uart_irq(state); @@ -3273,14 +3316,14 @@ { unsigned long flags; struct serial_state *state = &rs_table[line]; + struct async_struct *info = &scr_info; - save_flags(flags); - cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); if (state->info && state->info->tty) tty_hangup(state->info->tty); state->type = PORT_UNKNOWN; + spin_unlock_irqrestore(SERIAL_LOCK, flags); printk(KERN_INFO "tty%02d unloaded\n", state->line); - restore_flags(flags); } #ifdef MODULE @@ -3294,11 +3337,10 @@ unsigned long flags; int e1, e2; int i; - struct async_struct *info; + struct async_struct *info = &scr_info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ - save_flags(flags); - cli(); + spin_lock_irqsave(SERIAL_LOCK, flags); timer_active &= ~(1 << RS_TIMER); timer_table[RS_TIMER].fn = NULL; timer_table[RS_TIMER].expires = 0; @@ -3309,20 +3351,23 @@ if ((e2 = tty_unregister_driver(&callout_driver))) printk("SERIAL: failed to unregister callout driver (%d)\n", e2); - restore_flags(flags); for (i = 0; i < NR_PORTS; i++) { + struct async_struct *inf; if (rs_table[i].type != PORT_UNKNOWN) release_region(rs_table[i].port, 8); - info = rs_table[i].info; - if (info) { + inf = rs_table[i].info; + if (inf) { rs_table[i].info = NULL; - kfree_s(info, sizeof(struct async_struct)); + kfree_s(inf, sizeof(struct async_struct)); } } + spin_unlock_irqrestore(SERIAL_LOCK, flags); + if (tmp_buf) { - free_page((unsigned long) tmp_buf); + unsigned long pg = (unsigned long) tmp_buf; tmp_buf = NULL; + free_page(pg); } } #endif /* MODULE */ diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/tty_io.c linux-2.2.15-smp/drivers/char/tty_io.c --- linux-2.2.15-pre14/drivers/char/tty_io.c Wed Mar 15 14:29:38 2000 +++ linux-2.2.15-smp/drivers/char/tty_io.c Sun Mar 19 23:28:18 2000 @@ -1457,7 +1457,7 @@ } } else { if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; + tty->minimum_to_wake = N_TTY_BUF_SIZE - 1; } return 0; } @@ -1943,7 +1943,8 @@ tty->tq_hangup.data = tty; sema_init(&tty->atomic_read, 1); sema_init(&tty->atomic_write, 1); - spin_lock_init(&tty->read_lock); + spin_lock_init(&tty->head_lock); + spin_lock_init(&tty->tail_lock); } /* diff -urP -x*~ -xTAGS linux-2.2.15-pre14/drivers/char/tty_ioctl.c linux-2.2.15-smp/drivers/char/tty_ioctl.c --- linux-2.2.15-pre14/drivers/char/tty_ioctl.c Wed Jan 27 21:28:33 1999 +++ linux-2.2.15-smp/drivers/char/tty_ioctl.c Sat Mar 18 17:55:45 2000 @@ -107,12 +107,12 @@ canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; if (canon_change) { memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; + tty->canon_head = tty->read.tail; tty->canon_data = 0; tty->erasing = 0; } sti(); - if (canon_change && !L_ICANON(tty) && tty->read_cnt) + if (canon_change && !L_ICANON(tty) && tty->read.head != tty->read.tail) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->read_wait); @@ -184,15 +184,15 @@ { int nr, head, tail; - if (!tty->canon_data || !tty->read_buf) + if (!tty->canon_data || !tty->read.buf) return 0; head = tty->canon_head; - tail = tty->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); + tail = tty->read.tail; + nr = CIRC_CNT(head, tail, N_TTY_BUF_SIZE); /* Skip EOF-chars.. */ while (head != tail) { if (test_bit(tail, &tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) + tty->read.buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } @@ -470,7 +470,9 @@ tty->driver.chars_in_buffer(tty) : 0, (int *) arg); case TIOCINQ: - retval = tty->read_cnt; + retval = CIRC_CNT(tty->read.head, + tty->read.tail, + N_TTY_BUF_SIZE); if (L_ICANON(tty)) retval = inq_canon(tty); return put_user(retval, (unsigned int *) arg); diff -urP -x*~ -xTAGS linux-2.2.15-pre14/include/linux/circ_buf.h linux-2.2.15-smp/include/linux/circ_buf.h --- linux-2.2.15-pre14/include/linux/circ_buf.h Thu Jan 1 09:30:00 1970 +++ linux-2.2.15-smp/include/linux/circ_buf.h Sat Mar 18 17:43:34 2000 @@ -0,0 +1,32 @@ +#ifndef _LINUX_CIRC_BUF_H +#define _LINUX_CIRC_BUF_H 1 + +struct circ_buf { + char *buf; + int head; + int tail; +}; + +/* Return count in buffer. */ +#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1)) + +/* Return space available, 0..size-1. We always leave one free char + as a completely full buffer has head == tail, which is the same as + empty. */ +#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size)) + +/* Return count up to the end of the buffer. Carefully avoid + accessing head and tail more than once, so they can change + underneath us without returning inconsistent results. */ +#define CIRC_CNT_TO_END(head,tail,size) \ + ({int end = (size) - (tail); \ + int n = ((head) + end) & ((size)-1); \ + n < end ? n : end;}) + +/* Return space available up to the end of the buffer. */ +#define CIRC_SPACE_TO_END(head,tail,size) \ + ({int end = (size) - 1 - (head); \ + int n = (end + (tail)) & ((size)-1); \ + n <= end ? n : end+1;}) + +#endif /* _LINUX_CIRC_BUF_H */ diff -urP -x*~ -xTAGS linux-2.2.15-pre14/include/linux/serialP.h linux-2.2.15-smp/include/linux/serialP.h --- linux-2.2.15-pre14/include/linux/serialP.h Thu Nov 27 20:42:59 1997 +++ linux-2.2.15-smp/include/linux/serialP.h Mon Mar 20 16:27:12 2000 @@ -21,6 +21,7 @@ #include #include +#include /* * Counters of the input lines (CTS, DSR, RI, CD) interrupts @@ -75,10 +76,8 @@ int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ long pgrp; /* pgrp of opening process */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; + struct circ_buf xmit; + spinlock_t xmit_lock; struct tq_struct tqueue; struct wait_queue *open_wait; struct wait_queue *close_wait; diff -urP -x*~ -xTAGS linux-2.2.15-pre14/include/linux/tty.h linux-2.2.15-smp/include/linux/tty.h --- linux-2.2.15-pre14/include/linux/tty.h Wed Mar 15 17:04:41 2000 +++ linux-2.2.15-smp/include/linux/tty.h Mon Mar 20 16:27:12 2000 @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -281,8 +282,9 @@ void *disc_data; void *driver_data; +/* N_TTY_BUF_SIZE must be a power of two. */ #define N_TTY_BUF_SIZE 4096 - + /* * The following is data for the N_TTY line discipline. For * historical reasons, this is included in the tty structure. @@ -294,17 +296,15 @@ unsigned overrun_time; int num_overrun; unsigned long process_char_map[256/(8*sizeof(unsigned long))]; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; + struct circ_buf read; + spinlock_t head_lock; + spinlock_t tail_lock; unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; int canon_data; - unsigned long canon_head; + int canon_head; unsigned int canon_column; struct semaphore atomic_read; struct semaphore atomic_write; - spinlock_t read_lock; }; /* tty magic number */