diff options
| -rw-r--r-- | drivers/tty/n_tty.c | 42 |
1 files changed, 24 insertions, 18 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index b279f8730e04..431742201709 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c | |||
| @@ -143,6 +143,7 @@ static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) | |||
| 143 | 143 | ||
| 144 | static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) | 144 | static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) |
| 145 | { | 145 | { |
| 146 | smp_rmb(); /* Matches smp_wmb() in add_echo_byte(). */ | ||
| 146 | return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; | 147 | return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; |
| 147 | } | 148 | } |
| 148 | 149 | ||
| @@ -318,9 +319,7 @@ static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) | |||
| 318 | static void reset_buffer_flags(struct n_tty_data *ldata) | 319 | static void reset_buffer_flags(struct n_tty_data *ldata) |
| 319 | { | 320 | { |
| 320 | ldata->read_head = ldata->canon_head = ldata->read_tail = 0; | 321 | ldata->read_head = ldata->canon_head = ldata->read_tail = 0; |
| 321 | ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; | ||
| 322 | ldata->commit_head = 0; | 322 | ldata->commit_head = 0; |
| 323 | ldata->echo_mark = 0; | ||
| 324 | ldata->line_start = 0; | 323 | ldata->line_start = 0; |
| 325 | 324 | ||
| 326 | ldata->erasing = 0; | 325 | ldata->erasing = 0; |
| @@ -619,13 +618,20 @@ static size_t __process_echoes(struct tty_struct *tty) | |||
| 619 | old_space = space = tty_write_room(tty); | 618 | old_space = space = tty_write_room(tty); |
| 620 | 619 | ||
| 621 | tail = ldata->echo_tail; | 620 | tail = ldata->echo_tail; |
| 622 | while (ldata->echo_commit != tail) { | 621 | while (MASK(ldata->echo_commit) != MASK(tail)) { |
| 623 | c = echo_buf(ldata, tail); | 622 | c = echo_buf(ldata, tail); |
| 624 | if (c == ECHO_OP_START) { | 623 | if (c == ECHO_OP_START) { |
| 625 | unsigned char op; | 624 | unsigned char op; |
| 626 | int no_space_left = 0; | 625 | int no_space_left = 0; |
| 627 | 626 | ||
| 628 | /* | 627 | /* |
| 628 | * Since add_echo_byte() is called without holding | ||
| 629 | * output_lock, we might see only portion of multi-byte | ||
| 630 | * operation. | ||
| 631 | */ | ||
| 632 | if (MASK(ldata->echo_commit) == MASK(tail + 1)) | ||
| 633 | goto not_yet_stored; | ||
| 634 | /* | ||
| 629 | * If the buffer byte is the start of a multi-byte | 635 | * If the buffer byte is the start of a multi-byte |
| 630 | * operation, get the next byte, which is either the | 636 | * operation, get the next byte, which is either the |
| 631 | * op code or a control character value. | 637 | * op code or a control character value. |
| @@ -636,6 +642,8 @@ static size_t __process_echoes(struct tty_struct *tty) | |||
| 636 | unsigned int num_chars, num_bs; | 642 | unsigned int num_chars, num_bs; |
| 637 | 643 | ||
| 638 | case ECHO_OP_ERASE_TAB: | 644 | case ECHO_OP_ERASE_TAB: |
| 645 | if (MASK(ldata->echo_commit) == MASK(tail + 2)) | ||
| 646 | goto not_yet_stored; | ||
| 639 | num_chars = echo_buf(ldata, tail + 2); | 647 | num_chars = echo_buf(ldata, tail + 2); |
| 640 | 648 | ||
| 641 | /* | 649 | /* |
| @@ -730,7 +738,8 @@ static size_t __process_echoes(struct tty_struct *tty) | |||
| 730 | /* If the echo buffer is nearly full (so that the possibility exists | 738 | /* If the echo buffer is nearly full (so that the possibility exists |
| 731 | * of echo overrun before the next commit), then discard enough | 739 | * of echo overrun before the next commit), then discard enough |
| 732 | * data at the tail to prevent a subsequent overrun */ | 740 | * data at the tail to prevent a subsequent overrun */ |
| 733 | while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { | 741 | while (ldata->echo_commit > tail && |
| 742 | ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { | ||
| 734 | if (echo_buf(ldata, tail) == ECHO_OP_START) { | 743 | if (echo_buf(ldata, tail) == ECHO_OP_START) { |
| 735 | if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) | 744 | if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) |
| 736 | tail += 3; | 745 | tail += 3; |
| @@ -740,6 +749,7 @@ static size_t __process_echoes(struct tty_struct *tty) | |||
| 740 | tail++; | 749 | tail++; |
| 741 | } | 750 | } |
| 742 | 751 | ||
| 752 | not_yet_stored: | ||
| 743 | ldata->echo_tail = tail; | 753 | ldata->echo_tail = tail; |
| 744 | return old_space - space; | 754 | return old_space - space; |
| 745 | } | 755 | } |
| @@ -750,6 +760,7 @@ static void commit_echoes(struct tty_struct *tty) | |||
| 750 | size_t nr, old, echoed; | 760 | size_t nr, old, echoed; |
| 751 | size_t head; | 761 | size_t head; |
| 752 | 762 | ||
| 763 | mutex_lock(&ldata->output_lock); | ||
| 753 | head = ldata->echo_head; | 764 | head = ldata->echo_head; |
| 754 | ldata->echo_mark = head; | 765 | ldata->echo_mark = head; |
| 755 | old = ldata->echo_commit - ldata->echo_tail; | 766 | old = ldata->echo_commit - ldata->echo_tail; |
| @@ -758,10 +769,12 @@ static void commit_echoes(struct tty_struct *tty) | |||
| 758 | * is over the threshold (and try again each time another | 769 | * is over the threshold (and try again each time another |
| 759 | * block is accumulated) */ | 770 | * block is accumulated) */ |
| 760 | nr = head - ldata->echo_tail; | 771 | nr = head - ldata->echo_tail; |
| 761 | if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) | 772 | if (nr < ECHO_COMMIT_WATERMARK || |
| 773 | (nr % ECHO_BLOCK > old % ECHO_BLOCK)) { | ||
| 774 | mutex_unlock(&ldata->output_lock); | ||
| 762 | return; | 775 | return; |
| 776 | } | ||
| 763 | 777 | ||
| 764 | mutex_lock(&ldata->output_lock); | ||
| 765 | ldata->echo_commit = head; | 778 | ldata->echo_commit = head; |
| 766 | echoed = __process_echoes(tty); | 779 | echoed = __process_echoes(tty); |
| 767 | mutex_unlock(&ldata->output_lock); | 780 | mutex_unlock(&ldata->output_lock); |
| @@ -812,7 +825,9 @@ static void flush_echoes(struct tty_struct *tty) | |||
| 812 | 825 | ||
| 813 | static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) | 826 | static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) |
| 814 | { | 827 | { |
| 815 | *echo_buf_addr(ldata, ldata->echo_head++) = c; | 828 | *echo_buf_addr(ldata, ldata->echo_head) = c; |
| 829 | smp_wmb(); /* Matches smp_rmb() in echo_buf(). */ | ||
| 830 | ldata->echo_head++; | ||
| 816 | } | 831 | } |
| 817 | 832 | ||
| 818 | /** | 833 | /** |
| @@ -1881,30 +1896,21 @@ static int n_tty_open(struct tty_struct *tty) | |||
| 1881 | struct n_tty_data *ldata; | 1896 | struct n_tty_data *ldata; |
| 1882 | 1897 | ||
| 1883 | /* Currently a malloc failure here can panic */ | 1898 | /* Currently a malloc failure here can panic */ |
| 1884 | ldata = vmalloc(sizeof(*ldata)); | 1899 | ldata = vzalloc(sizeof(*ldata)); |
| 1885 | if (!ldata) | 1900 | if (!ldata) |
| 1886 | goto err; | 1901 | return -ENOMEM; |
| 1887 | 1902 | ||
| 1888 | ldata->overrun_time = jiffies; | 1903 | ldata->overrun_time = jiffies; |
| 1889 | mutex_init(&ldata->atomic_read_lock); | 1904 | mutex_init(&ldata->atomic_read_lock); |
| 1890 | mutex_init(&ldata->output_lock); | 1905 | mutex_init(&ldata->output_lock); |
| 1891 | 1906 | ||
| 1892 | tty->disc_data = ldata; | 1907 | tty->disc_data = ldata; |
| 1893 | reset_buffer_flags(tty->disc_data); | ||
| 1894 | ldata->column = 0; | ||
| 1895 | ldata->canon_column = 0; | ||
| 1896 | ldata->num_overrun = 0; | ||
| 1897 | ldata->no_room = 0; | ||
| 1898 | ldata->lnext = 0; | ||
| 1899 | tty->closing = 0; | 1908 | tty->closing = 0; |
| 1900 | /* indicate buffer work may resume */ | 1909 | /* indicate buffer work may resume */ |
| 1901 | clear_bit(TTY_LDISC_HALTED, &tty->flags); | 1910 | clear_bit(TTY_LDISC_HALTED, &tty->flags); |
| 1902 | n_tty_set_termios(tty, NULL); | 1911 | n_tty_set_termios(tty, NULL); |
| 1903 | tty_unthrottle(tty); | 1912 | tty_unthrottle(tty); |
| 1904 | |||
| 1905 | return 0; | 1913 | return 0; |
| 1906 | err: | ||
| 1907 | return -ENOMEM; | ||
| 1908 | } | 1914 | } |
| 1909 | 1915 | ||
| 1910 | static inline int input_available_p(struct tty_struct *tty, int poll) | 1916 | static inline int input_available_p(struct tty_struct *tty, int poll) |
