TTY/Serial fixes for 3.15-rc4
Here are some tty and serial driver fixes for things reported recently. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlNn/10ACgkQMUfUDdst+ynKXgCg1BE7sLm2Nmxkm+nfYceWmRG2 pQAAnRnY2wBwtpVa4mVy1ZR3ykiKPvmY =42O2 -----END PGP SIGNATURE----- Merge tag 'tty-3.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial fixes from Greg KH: "Here are some tty and serial driver fixes for things reported recently" * tag 'tty-3.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: tty: Fix lockless tty buffer race Revert "tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc" drivers/tty/hvc: don't free hvc_console_setup after init n_tty: Fix n_tty_write crash when echoing in raw mode tty: serial: 8250_core.c Bug fix for Exar chips.
This commit is contained in:
commit
df862f625d
@ -190,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index)
|
|||||||
return hvc_driver;
|
return hvc_driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init hvc_console_setup(struct console *co, char *options)
|
static int hvc_console_setup(struct console *co, char *options)
|
||||||
{
|
{
|
||||||
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)
|
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -2353,8 +2353,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
|
|||||||
if (tty->ops->flush_chars)
|
if (tty->ops->flush_chars)
|
||||||
tty->ops->flush_chars(tty);
|
tty->ops->flush_chars(tty);
|
||||||
} else {
|
} else {
|
||||||
|
struct n_tty_data *ldata = tty->disc_data;
|
||||||
|
|
||||||
while (nr > 0) {
|
while (nr > 0) {
|
||||||
|
mutex_lock(&ldata->output_lock);
|
||||||
c = tty->ops->write(tty, b, nr);
|
c = tty->ops->write(tty, b, nr);
|
||||||
|
mutex_unlock(&ldata->output_lock);
|
||||||
if (c < 0) {
|
if (c < 0) {
|
||||||
retval = c;
|
retval = c;
|
||||||
goto break_out;
|
goto break_out;
|
||||||
|
@ -555,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
|
|||||||
*/
|
*/
|
||||||
if ((p->port.type == PORT_XR17V35X) ||
|
if ((p->port.type == PORT_XR17V35X) ||
|
||||||
(p->port.type == PORT_XR17D15X)) {
|
(p->port.type == PORT_XR17D15X)) {
|
||||||
serial_out(p, UART_EXAR_SLEEP, 0xff);
|
serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,16 +255,15 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size,
|
|||||||
if (change || left < size) {
|
if (change || left < size) {
|
||||||
/* This is the slow path - looking for new buffers to use */
|
/* This is the slow path - looking for new buffers to use */
|
||||||
if ((n = tty_buffer_alloc(port, size)) != NULL) {
|
if ((n = tty_buffer_alloc(port, size)) != NULL) {
|
||||||
unsigned long iflags;
|
|
||||||
|
|
||||||
n->flags = flags;
|
n->flags = flags;
|
||||||
buf->tail = n;
|
buf->tail = n;
|
||||||
|
|
||||||
spin_lock_irqsave(&buf->flush_lock, iflags);
|
|
||||||
b->commit = b->used;
|
b->commit = b->used;
|
||||||
|
/* paired w/ barrier in flush_to_ldisc(); ensures the
|
||||||
|
* latest commit value can be read before the head is
|
||||||
|
* advanced to the next buffer
|
||||||
|
*/
|
||||||
|
smp_wmb();
|
||||||
b->next = n;
|
b->next = n;
|
||||||
spin_unlock_irqrestore(&buf->flush_lock, iflags);
|
|
||||||
|
|
||||||
} else if (change)
|
} else if (change)
|
||||||
size = 0;
|
size = 0;
|
||||||
else
|
else
|
||||||
@ -448,27 +447,28 @@ static void flush_to_ldisc(struct work_struct *work)
|
|||||||
mutex_lock(&buf->lock);
|
mutex_lock(&buf->lock);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
unsigned long flags;
|
|
||||||
struct tty_buffer *head = buf->head;
|
struct tty_buffer *head = buf->head;
|
||||||
|
struct tty_buffer *next;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
/* Ldisc or user is trying to gain exclusive access */
|
/* Ldisc or user is trying to gain exclusive access */
|
||||||
if (atomic_read(&buf->priority))
|
if (atomic_read(&buf->priority))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
spin_lock_irqsave(&buf->flush_lock, flags);
|
next = head->next;
|
||||||
|
/* paired w/ barrier in __tty_buffer_request_room();
|
||||||
|
* ensures commit value read is not stale if the head
|
||||||
|
* is advancing to the next buffer
|
||||||
|
*/
|
||||||
|
smp_rmb();
|
||||||
count = head->commit - head->read;
|
count = head->commit - head->read;
|
||||||
if (!count) {
|
if (!count) {
|
||||||
if (head->next == NULL) {
|
if (next == NULL)
|
||||||
spin_unlock_irqrestore(&buf->flush_lock, flags);
|
|
||||||
break;
|
break;
|
||||||
}
|
buf->head = next;
|
||||||
buf->head = head->next;
|
|
||||||
spin_unlock_irqrestore(&buf->flush_lock, flags);
|
|
||||||
tty_buffer_free(port, head);
|
tty_buffer_free(port, head);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&buf->flush_lock, flags);
|
|
||||||
|
|
||||||
count = receive_buf(tty, head, count);
|
count = receive_buf(tty, head, count);
|
||||||
if (!count)
|
if (!count)
|
||||||
@ -523,7 +523,6 @@ void tty_buffer_init(struct tty_port *port)
|
|||||||
struct tty_bufhead *buf = &port->buf;
|
struct tty_bufhead *buf = &port->buf;
|
||||||
|
|
||||||
mutex_init(&buf->lock);
|
mutex_init(&buf->lock);
|
||||||
spin_lock_init(&buf->flush_lock);
|
|
||||||
tty_buffer_reset(&buf->sentinel, 0);
|
tty_buffer_reset(&buf->sentinel, 0);
|
||||||
buf->head = &buf->sentinel;
|
buf->head = &buf->sentinel;
|
||||||
buf->tail = &buf->sentinel;
|
buf->tail = &buf->sentinel;
|
||||||
|
@ -61,7 +61,6 @@ struct tty_bufhead {
|
|||||||
struct tty_buffer *head; /* Queue head */
|
struct tty_buffer *head; /* Queue head */
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
spinlock_t flush_lock;
|
|
||||||
atomic_t priority;
|
atomic_t priority;
|
||||||
struct tty_buffer sentinel;
|
struct tty_buffer sentinel;
|
||||||
struct llist_head free; /* Free queue head */
|
struct llist_head free; /* Free queue head */
|
||||||
|
Loading…
Reference in New Issue
Block a user