TTY/Serial fixes for 3.15-rc3
Here are a few tty/serial fixes for 3.15-rc3 that resolve a number of reported issues in the 8250 and samsung serial drivers, as well as a character loss fix for the tty core that was caused by the lock removal patches a release ago. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlNcUDMACgkQMUfUDdst+ylZvgCgjeJTIxh+7Lpo/yqbFRZz05dc KZ4AoLBTof/1fV2UMczNm61vrp0+wvyi =FnZz -----END PGP SIGNATURE----- Merge tag 'tty-3.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial fixes from Greg KH: "Here are a few tty/serial fixes for 3.15-rc3 that resolve a number of reported issues in the 8250 and samsung serial drivers, as well as a character loss fix for the tty core that was caused by the lock removal patches a release ago" * tag 'tty-3.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: serial_core: fix uart PORT_UNKNOWN handling serial: samsung: Change barrier() to cpu_relax() in console output serial: samsung: don't check config for every character serial: samsung: Use the passed in "port", fixing kgdb w/ no console serial: 8250: Fix thread unsafe __dma_tx_complete function 8250_core: Fix unwanted TX chars write tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc
This commit is contained in:
Коммит
a8d706986c
|
@ -1520,7 +1520,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
|
||||||
status = serial8250_rx_chars(up, status);
|
status = serial8250_rx_chars(up, status);
|
||||||
}
|
}
|
||||||
serial8250_modem_status(up);
|
serial8250_modem_status(up);
|
||||||
if (status & UART_LSR_THRE)
|
if (!up->dma && (status & UART_LSR_THRE))
|
||||||
serial8250_tx_chars(up);
|
serial8250_tx_chars(up);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&port->lock, flags);
|
spin_unlock_irqrestore(&port->lock, flags);
|
||||||
|
|
|
@ -20,12 +20,15 @@ static void __dma_tx_complete(void *param)
|
||||||
struct uart_8250_port *p = param;
|
struct uart_8250_port *p = param;
|
||||||
struct uart_8250_dma *dma = p->dma;
|
struct uart_8250_dma *dma = p->dma;
|
||||||
struct circ_buf *xmit = &p->port.state->xmit;
|
struct circ_buf *xmit = &p->port.state->xmit;
|
||||||
|
unsigned long flags;
|
||||||
dma->tx_running = 0;
|
|
||||||
|
|
||||||
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
|
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
|
||||||
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&p->port.lock, flags);
|
||||||
|
|
||||||
|
dma->tx_running = 0;
|
||||||
|
|
||||||
xmit->tail += dma->tx_size;
|
xmit->tail += dma->tx_size;
|
||||||
xmit->tail &= UART_XMIT_SIZE - 1;
|
xmit->tail &= UART_XMIT_SIZE - 1;
|
||||||
p->port.icount.tx += dma->tx_size;
|
p->port.icount.tx += dma->tx_size;
|
||||||
|
@ -35,6 +38,8 @@ static void __dma_tx_complete(void *param)
|
||||||
|
|
||||||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
|
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
|
||||||
serial8250_tx_dma(p);
|
serial8250_tx_dma(p);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&p->port.lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __dma_rx_complete(void *param)
|
static void __dma_rx_complete(void *param)
|
||||||
|
|
|
@ -1446,8 +1446,8 @@ static int s3c24xx_serial_get_poll_char(struct uart_port *port)
|
||||||
static void s3c24xx_serial_put_poll_char(struct uart_port *port,
|
static void s3c24xx_serial_put_poll_char(struct uart_port *port,
|
||||||
unsigned char c)
|
unsigned char c)
|
||||||
{
|
{
|
||||||
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
|
unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
|
||||||
unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
|
unsigned int ucon = rd_regl(port, S3C2410_UCON);
|
||||||
|
|
||||||
/* not possible to xmit on unconfigured port */
|
/* not possible to xmit on unconfigured port */
|
||||||
if (!s3c24xx_port_configured(ucon))
|
if (!s3c24xx_port_configured(ucon))
|
||||||
|
@ -1455,7 +1455,7 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port,
|
||||||
|
|
||||||
while (!s3c24xx_serial_console_txrdy(port, ufcon))
|
while (!s3c24xx_serial_console_txrdy(port, ufcon))
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
wr_regb(cons_uart, S3C2410_UTXH, c);
|
wr_regb(port, S3C2410_UTXH, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_CONSOLE_POLL */
|
#endif /* CONFIG_CONSOLE_POLL */
|
||||||
|
@ -1463,22 +1463,23 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port,
|
||||||
static void
|
static void
|
||||||
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
|
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
|
||||||
{
|
{
|
||||||
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
|
unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
|
||||||
unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
|
|
||||||
|
|
||||||
/* not possible to xmit on unconfigured port */
|
|
||||||
if (!s3c24xx_port_configured(ucon))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (!s3c24xx_serial_console_txrdy(port, ufcon))
|
while (!s3c24xx_serial_console_txrdy(port, ufcon))
|
||||||
barrier();
|
cpu_relax();
|
||||||
wr_regb(cons_uart, S3C2410_UTXH, ch);
|
wr_regb(port, S3C2410_UTXH, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
s3c24xx_serial_console_write(struct console *co, const char *s,
|
s3c24xx_serial_console_write(struct console *co, const char *s,
|
||||||
unsigned int count)
|
unsigned int count)
|
||||||
{
|
{
|
||||||
|
unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
|
||||||
|
|
||||||
|
/* not possible to xmit on unconfigured port */
|
||||||
|
if (!s3c24xx_port_configured(ucon))
|
||||||
|
return;
|
||||||
|
|
||||||
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
|
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
|
||||||
if (uport->type == PORT_UNKNOWN)
|
if (uport->type == PORT_UNKNOWN)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the device is in D0 state.
|
||||||
|
*/
|
||||||
|
uart_change_pm(state, UART_PM_STATE_ON);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialise and allocate the transmit and temporary
|
* Initialise and allocate the transmit and temporary
|
||||||
* buffer.
|
* buffer.
|
||||||
|
@ -825,25 +830,29 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
|
||||||
* If we fail to request resources for the
|
* If we fail to request resources for the
|
||||||
* new port, try to restore the old settings.
|
* new port, try to restore the old settings.
|
||||||
*/
|
*/
|
||||||
if (retval && old_type != PORT_UNKNOWN) {
|
if (retval) {
|
||||||
uport->iobase = old_iobase;
|
uport->iobase = old_iobase;
|
||||||
uport->type = old_type;
|
uport->type = old_type;
|
||||||
uport->hub6 = old_hub6;
|
uport->hub6 = old_hub6;
|
||||||
uport->iotype = old_iotype;
|
uport->iotype = old_iotype;
|
||||||
uport->regshift = old_shift;
|
uport->regshift = old_shift;
|
||||||
uport->mapbase = old_mapbase;
|
uport->mapbase = old_mapbase;
|
||||||
retval = uport->ops->request_port(uport);
|
|
||||||
/*
|
|
||||||
* If we failed to restore the old settings,
|
|
||||||
* we fail like this.
|
|
||||||
*/
|
|
||||||
if (retval)
|
|
||||||
uport->type = PORT_UNKNOWN;
|
|
||||||
|
|
||||||
/*
|
if (old_type != PORT_UNKNOWN) {
|
||||||
* We failed anyway.
|
retval = uport->ops->request_port(uport);
|
||||||
*/
|
/*
|
||||||
retval = -EBUSY;
|
* If we failed to restore the old settings,
|
||||||
|
* we fail like this.
|
||||||
|
*/
|
||||||
|
if (retval)
|
||||||
|
uport->type = PORT_UNKNOWN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We failed anyway.
|
||||||
|
*/
|
||||||
|
retval = -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/* Added to return the correct error -Ram Gupta */
|
/* Added to return the correct error -Ram Gupta */
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
@ -1570,12 +1579,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
|
||||||
goto err_dec_count;
|
goto err_dec_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure the device is in D0 state.
|
|
||||||
*/
|
|
||||||
if (port->count == 1)
|
|
||||||
uart_change_pm(state, UART_PM_STATE_ON);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start up the serial port.
|
* Start up the serial port.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -255,11 +255,16 @@ 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;
|
||||||
smp_mb();
|
|
||||||
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
|
||||||
|
@ -443,6 +448,7 @@ 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;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
|
@ -450,14 +456,19 @@ static void flush_to_ldisc(struct work_struct *work)
|
||||||
if (atomic_read(&buf->priority))
|
if (atomic_read(&buf->priority))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&buf->flush_lock, flags);
|
||||||
count = head->commit - head->read;
|
count = head->commit - head->read;
|
||||||
if (!count) {
|
if (!count) {
|
||||||
if (head->next == NULL)
|
if (head->next == NULL) {
|
||||||
|
spin_unlock_irqrestore(&buf->flush_lock, flags);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
buf->head = 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)
|
||||||
|
@ -512,6 +523,7 @@ 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,6 +61,7 @@ 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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче