diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-11 03:20:12 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-11 03:20:12 -0500 |
| commit | 434fd6353b4c83938029ca6ea7dfa4fc82d602bd (patch) | |
| tree | 2f8c8c6214c16b6c20168594b9720833fcb4e191 | |
| parent | 85298808617299fe713ed3e03114058883ce3d8a (diff) | |
| parent | f98c7bce570bdbe344b74ff5daa7dfeef3f22929 (diff) | |
Merge tag 'tty-4.11-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial fixes frpm Greg KH:
"Here are two bugfixes for tty stuff for 4.11-rc2.
One of them resolves the pretty bad bug in the n_hdlc code that
Alexander Popov found and fixed and has been reported everywhere. The
other just fixes a samsung serial driver issue when DMA fails on some
systems.
Both have been in linux-next with no reported issues"
* tag 'tty-4.11-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
serial: samsung: Continue to work if DMA request fails
tty: n_hdlc: get rid of racy n_hdlc.tbuf
| -rw-r--r-- | drivers/tty/n_hdlc.c | 132 | ||||
| -rw-r--r-- | drivers/tty/serial/samsung.c | 6 |
2 files changed, 73 insertions, 65 deletions
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 1bacbc3b19a0..e94aea8c0d05 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c | |||
| @@ -114,7 +114,7 @@ | |||
| 114 | #define DEFAULT_TX_BUF_COUNT 3 | 114 | #define DEFAULT_TX_BUF_COUNT 3 |
| 115 | 115 | ||
| 116 | struct n_hdlc_buf { | 116 | struct n_hdlc_buf { |
| 117 | struct n_hdlc_buf *link; | 117 | struct list_head list_item; |
| 118 | int count; | 118 | int count; |
| 119 | char buf[1]; | 119 | char buf[1]; |
| 120 | }; | 120 | }; |
| @@ -122,8 +122,7 @@ struct n_hdlc_buf { | |||
| 122 | #define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) | 122 | #define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) |
| 123 | 123 | ||
| 124 | struct n_hdlc_buf_list { | 124 | struct n_hdlc_buf_list { |
| 125 | struct n_hdlc_buf *head; | 125 | struct list_head list; |
| 126 | struct n_hdlc_buf *tail; | ||
| 127 | int count; | 126 | int count; |
| 128 | spinlock_t spinlock; | 127 | spinlock_t spinlock; |
| 129 | }; | 128 | }; |
| @@ -136,7 +135,6 @@ struct n_hdlc_buf_list { | |||
| 136 | * @backup_tty - TTY to use if tty gets closed | 135 | * @backup_tty - TTY to use if tty gets closed |
| 137 | * @tbusy - reentrancy flag for tx wakeup code | 136 | * @tbusy - reentrancy flag for tx wakeup code |
| 138 | * @woke_up - FIXME: describe this field | 137 | * @woke_up - FIXME: describe this field |
| 139 | * @tbuf - currently transmitting tx buffer | ||
| 140 | * @tx_buf_list - list of pending transmit frame buffers | 138 | * @tx_buf_list - list of pending transmit frame buffers |
| 141 | * @rx_buf_list - list of received frame buffers | 139 | * @rx_buf_list - list of received frame buffers |
| 142 | * @tx_free_buf_list - list unused transmit frame buffers | 140 | * @tx_free_buf_list - list unused transmit frame buffers |
| @@ -149,7 +147,6 @@ struct n_hdlc { | |||
| 149 | struct tty_struct *backup_tty; | 147 | struct tty_struct *backup_tty; |
| 150 | int tbusy; | 148 | int tbusy; |
| 151 | int woke_up; | 149 | int woke_up; |
| 152 | struct n_hdlc_buf *tbuf; | ||
| 153 | struct n_hdlc_buf_list tx_buf_list; | 150 | struct n_hdlc_buf_list tx_buf_list; |
| 154 | struct n_hdlc_buf_list rx_buf_list; | 151 | struct n_hdlc_buf_list rx_buf_list; |
| 155 | struct n_hdlc_buf_list tx_free_buf_list; | 152 | struct n_hdlc_buf_list tx_free_buf_list; |
| @@ -159,6 +156,8 @@ struct n_hdlc { | |||
| 159 | /* | 156 | /* |
| 160 | * HDLC buffer list manipulation functions | 157 | * HDLC buffer list manipulation functions |
| 161 | */ | 158 | */ |
| 159 | static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, | ||
| 160 | struct n_hdlc_buf *buf); | ||
| 162 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, | 161 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, |
| 163 | struct n_hdlc_buf *buf); | 162 | struct n_hdlc_buf *buf); |
| 164 | static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); | 163 | static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); |
| @@ -208,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty) | |||
| 208 | { | 207 | { |
| 209 | struct n_hdlc *n_hdlc = tty2n_hdlc(tty); | 208 | struct n_hdlc *n_hdlc = tty2n_hdlc(tty); |
| 210 | struct n_hdlc_buf *buf; | 209 | struct n_hdlc_buf *buf; |
| 211 | unsigned long flags; | ||
| 212 | 210 | ||
| 213 | while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) | 211 | while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) |
| 214 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); | 212 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); |
| 215 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); | ||
| 216 | if (n_hdlc->tbuf) { | ||
| 217 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf); | ||
| 218 | n_hdlc->tbuf = NULL; | ||
| 219 | } | ||
| 220 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); | ||
| 221 | } | 213 | } |
| 222 | 214 | ||
| 223 | static struct tty_ldisc_ops n_hdlc_ldisc = { | 215 | static struct tty_ldisc_ops n_hdlc_ldisc = { |
| @@ -283,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc) | |||
| 283 | } else | 275 | } else |
| 284 | break; | 276 | break; |
| 285 | } | 277 | } |
| 286 | kfree(n_hdlc->tbuf); | ||
| 287 | kfree(n_hdlc); | 278 | kfree(n_hdlc); |
| 288 | 279 | ||
| 289 | } /* end of n_hdlc_release() */ | 280 | } /* end of n_hdlc_release() */ |
| @@ -402,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) | |||
| 402 | n_hdlc->woke_up = 0; | 393 | n_hdlc->woke_up = 0; |
| 403 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); | 394 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); |
| 404 | 395 | ||
| 405 | /* get current transmit buffer or get new transmit */ | 396 | tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); |
| 406 | /* buffer from list of pending transmit buffers */ | ||
| 407 | |||
| 408 | tbuf = n_hdlc->tbuf; | ||
| 409 | if (!tbuf) | ||
| 410 | tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); | ||
| 411 | |||
| 412 | while (tbuf) { | 397 | while (tbuf) { |
| 413 | if (debuglevel >= DEBUG_LEVEL_INFO) | 398 | if (debuglevel >= DEBUG_LEVEL_INFO) |
| 414 | printk("%s(%d)sending frame %p, count=%d\n", | 399 | printk("%s(%d)sending frame %p, count=%d\n", |
| @@ -420,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) | |||
| 420 | 405 | ||
| 421 | /* rollback was possible and has been done */ | 406 | /* rollback was possible and has been done */ |
| 422 | if (actual == -ERESTARTSYS) { | 407 | if (actual == -ERESTARTSYS) { |
| 423 | n_hdlc->tbuf = tbuf; | 408 | n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); |
| 424 | break; | 409 | break; |
| 425 | } | 410 | } |
| 426 | /* if transmit error, throw frame away by */ | 411 | /* if transmit error, throw frame away by */ |
| @@ -435,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) | |||
| 435 | 420 | ||
| 436 | /* free current transmit buffer */ | 421 | /* free current transmit buffer */ |
| 437 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); | 422 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); |
| 438 | 423 | ||
| 439 | /* this tx buffer is done */ | ||
| 440 | n_hdlc->tbuf = NULL; | ||
| 441 | |||
| 442 | /* wait up sleeping writers */ | 424 | /* wait up sleeping writers */ |
| 443 | wake_up_interruptible(&tty->write_wait); | 425 | wake_up_interruptible(&tty->write_wait); |
| 444 | 426 | ||
| @@ -448,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) | |||
| 448 | if (debuglevel >= DEBUG_LEVEL_INFO) | 430 | if (debuglevel >= DEBUG_LEVEL_INFO) |
| 449 | printk("%s(%d)frame %p pending\n", | 431 | printk("%s(%d)frame %p pending\n", |
| 450 | __FILE__,__LINE__,tbuf); | 432 | __FILE__,__LINE__,tbuf); |
| 451 | 433 | ||
| 452 | /* buffer not accepted by driver */ | 434 | /* |
| 453 | /* set this buffer as pending buffer */ | 435 | * the buffer was not accepted by driver, |
| 454 | n_hdlc->tbuf = tbuf; | 436 | * return it back into tx queue |
| 437 | */ | ||
| 438 | n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf); | ||
| 455 | break; | 439 | break; |
| 456 | } | 440 | } |
| 457 | } | 441 | } |
| @@ -749,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, | |||
| 749 | int error = 0; | 733 | int error = 0; |
| 750 | int count; | 734 | int count; |
| 751 | unsigned long flags; | 735 | unsigned long flags; |
| 752 | 736 | struct n_hdlc_buf *buf = NULL; | |
| 737 | |||
| 753 | if (debuglevel >= DEBUG_LEVEL_INFO) | 738 | if (debuglevel >= DEBUG_LEVEL_INFO) |
| 754 | printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", | 739 | printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", |
| 755 | __FILE__,__LINE__,cmd); | 740 | __FILE__,__LINE__,cmd); |
| @@ -763,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, | |||
| 763 | /* report count of read data available */ | 748 | /* report count of read data available */ |
| 764 | /* in next available frame (if any) */ | 749 | /* in next available frame (if any) */ |
| 765 | spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); | 750 | spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); |
| 766 | if (n_hdlc->rx_buf_list.head) | 751 | buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list, |
| 767 | count = n_hdlc->rx_buf_list.head->count; | 752 | struct n_hdlc_buf, list_item); |
| 753 | if (buf) | ||
| 754 | count = buf->count; | ||
| 768 | else | 755 | else |
| 769 | count = 0; | 756 | count = 0; |
| 770 | spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); | 757 | spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); |
| @@ -776,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, | |||
| 776 | count = tty_chars_in_buffer(tty); | 763 | count = tty_chars_in_buffer(tty); |
| 777 | /* add size of next output frame in queue */ | 764 | /* add size of next output frame in queue */ |
| 778 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); | 765 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); |
| 779 | if (n_hdlc->tx_buf_list.head) | 766 | buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list, |
| 780 | count += n_hdlc->tx_buf_list.head->count; | 767 | struct n_hdlc_buf, list_item); |
| 768 | if (buf) | ||
| 769 | count += buf->count; | ||
| 781 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); | 770 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); |
| 782 | error = put_user(count, (int __user *)arg); | 771 | error = put_user(count, (int __user *)arg); |
| 783 | break; | 772 | break; |
| @@ -825,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, | |||
| 825 | poll_wait(filp, &tty->write_wait, wait); | 814 | poll_wait(filp, &tty->write_wait, wait); |
| 826 | 815 | ||
| 827 | /* set bits for operations that won't block */ | 816 | /* set bits for operations that won't block */ |
| 828 | if (n_hdlc->rx_buf_list.head) | 817 | if (!list_empty(&n_hdlc->rx_buf_list.list)) |
| 829 | mask |= POLLIN | POLLRDNORM; /* readable */ | 818 | mask |= POLLIN | POLLRDNORM; /* readable */ |
| 830 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) | 819 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) |
| 831 | mask |= POLLHUP; | 820 | mask |= POLLHUP; |
| 832 | if (tty_hung_up_p(filp)) | 821 | if (tty_hung_up_p(filp)) |
| 833 | mask |= POLLHUP; | 822 | mask |= POLLHUP; |
| 834 | if (!tty_is_writelocked(tty) && | 823 | if (!tty_is_writelocked(tty) && |
| 835 | n_hdlc->tx_free_buf_list.head) | 824 | !list_empty(&n_hdlc->tx_free_buf_list.list)) |
| 836 | mask |= POLLOUT | POLLWRNORM; /* writable */ | 825 | mask |= POLLOUT | POLLWRNORM; /* writable */ |
| 837 | } | 826 | } |
| 838 | return mask; | 827 | return mask; |
| @@ -856,7 +845,12 @@ static struct n_hdlc *n_hdlc_alloc(void) | |||
| 856 | spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); | 845 | spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock); |
| 857 | spin_lock_init(&n_hdlc->rx_buf_list.spinlock); | 846 | spin_lock_init(&n_hdlc->rx_buf_list.spinlock); |
| 858 | spin_lock_init(&n_hdlc->tx_buf_list.spinlock); | 847 | spin_lock_init(&n_hdlc->tx_buf_list.spinlock); |
| 859 | 848 | ||
| 849 | INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list); | ||
| 850 | INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list); | ||
| 851 | INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list); | ||
| 852 | INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list); | ||
| 853 | |||
| 860 | /* allocate free rx buffer list */ | 854 | /* allocate free rx buffer list */ |
| 861 | for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { | 855 | for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { |
| 862 | buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); | 856 | buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); |
| @@ -884,53 +878,65 @@ static struct n_hdlc *n_hdlc_alloc(void) | |||
| 884 | } /* end of n_hdlc_alloc() */ | 878 | } /* end of n_hdlc_alloc() */ |
| 885 | 879 | ||
| 886 | /** | 880 | /** |
| 881 | * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list | ||
| 882 | * @buf_list - pointer to the buffer list | ||
| 883 | * @buf - pointer to the buffer | ||
| 884 | */ | ||
| 885 | static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list, | ||
| 886 | struct n_hdlc_buf *buf) | ||
| 887 | { | ||
| 888 | unsigned long flags; | ||
| 889 | |||
| 890 | spin_lock_irqsave(&buf_list->spinlock, flags); | ||
| 891 | |||
| 892 | list_add(&buf->list_item, &buf_list->list); | ||
| 893 | buf_list->count++; | ||
| 894 | |||
| 895 | spin_unlock_irqrestore(&buf_list->spinlock, flags); | ||
| 896 | } | ||
| 897 | |||
| 898 | /** | ||
| 887 | * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list | 899 | * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list |
| 888 | * @list - pointer to buffer list | 900 | * @buf_list - pointer to buffer list |
| 889 | * @buf - pointer to buffer | 901 | * @buf - pointer to buffer |
| 890 | */ | 902 | */ |
| 891 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, | 903 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list, |
| 892 | struct n_hdlc_buf *buf) | 904 | struct n_hdlc_buf *buf) |
| 893 | { | 905 | { |
| 894 | unsigned long flags; | 906 | unsigned long flags; |
| 895 | spin_lock_irqsave(&list->spinlock,flags); | 907 | |
| 896 | 908 | spin_lock_irqsave(&buf_list->spinlock, flags); | |
| 897 | buf->link=NULL; | 909 | |
| 898 | if (list->tail) | 910 | list_add_tail(&buf->list_item, &buf_list->list); |
| 899 | list->tail->link = buf; | 911 | buf_list->count++; |
| 900 | else | 912 | |
| 901 | list->head = buf; | 913 | spin_unlock_irqrestore(&buf_list->spinlock, flags); |
| 902 | list->tail = buf; | ||
| 903 | (list->count)++; | ||
| 904 | |||
| 905 | spin_unlock_irqrestore(&list->spinlock,flags); | ||
| 906 | |||
| 907 | } /* end of n_hdlc_buf_put() */ | 914 | } /* end of n_hdlc_buf_put() */ |
| 908 | 915 | ||
| 909 | /** | 916 | /** |
| 910 | * n_hdlc_buf_get - remove and return an HDLC buffer from list | 917 | * n_hdlc_buf_get - remove and return an HDLC buffer from list |
| 911 | * @list - pointer to HDLC buffer list | 918 | * @buf_list - pointer to HDLC buffer list |
| 912 | * | 919 | * |
| 913 | * Remove and return an HDLC buffer from the head of the specified HDLC buffer | 920 | * Remove and return an HDLC buffer from the head of the specified HDLC buffer |
| 914 | * list. | 921 | * list. |
| 915 | * Returns a pointer to HDLC buffer if available, otherwise %NULL. | 922 | * Returns a pointer to HDLC buffer if available, otherwise %NULL. |
| 916 | */ | 923 | */ |
| 917 | static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) | 924 | static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list) |
| 918 | { | 925 | { |
| 919 | unsigned long flags; | 926 | unsigned long flags; |
| 920 | struct n_hdlc_buf *buf; | 927 | struct n_hdlc_buf *buf; |
| 921 | spin_lock_irqsave(&list->spinlock,flags); | 928 | |
| 922 | 929 | spin_lock_irqsave(&buf_list->spinlock, flags); | |
| 923 | buf = list->head; | 930 | |
| 931 | buf = list_first_entry_or_null(&buf_list->list, | ||
| 932 | struct n_hdlc_buf, list_item); | ||
| 924 | if (buf) { | 933 | if (buf) { |
| 925 | list->head = buf->link; | 934 | list_del(&buf->list_item); |
| 926 | (list->count)--; | 935 | buf_list->count--; |
| 927 | } | 936 | } |
| 928 | if (!list->head) | 937 | |
| 929 | list->tail = NULL; | 938 | spin_unlock_irqrestore(&buf_list->spinlock, flags); |
| 930 | |||
| 931 | spin_unlock_irqrestore(&list->spinlock,flags); | ||
| 932 | return buf; | 939 | return buf; |
| 933 | |||
| 934 | } /* end of n_hdlc_buf_get() */ | 940 | } /* end of n_hdlc_buf_get() */ |
| 935 | 941 | ||
| 936 | static char hdlc_banner[] __initdata = | 942 | static char hdlc_banner[] __initdata = |
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index b4f86c219db1..7a17aedbf902 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c | |||
| @@ -1031,8 +1031,10 @@ static int s3c64xx_serial_startup(struct uart_port *port) | |||
| 1031 | if (ourport->dma) { | 1031 | if (ourport->dma) { |
| 1032 | ret = s3c24xx_serial_request_dma(ourport); | 1032 | ret = s3c24xx_serial_request_dma(ourport); |
| 1033 | if (ret < 0) { | 1033 | if (ret < 0) { |
| 1034 | dev_warn(port->dev, "DMA request failed\n"); | 1034 | dev_warn(port->dev, |
| 1035 | return ret; | 1035 | "DMA request failed, DMA will not be used\n"); |
| 1036 | devm_kfree(port->dev, ourport->dma); | ||
| 1037 | ourport->dma = NULL; | ||
| 1036 | } | 1038 | } |
| 1037 | } | 1039 | } |
| 1038 | 1040 | ||
