Skip to content

Commit

Permalink
Merge tag 'tty-3.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Apr 27, 2014
2 parents d0c15ad + 7deb39e commit a8d7069
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 34 deletions.
2 changes: 1 addition & 1 deletion drivers/tty/serial/8250/8250_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1520,7 +1520,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial8250_rx_chars(up, status);
}
serial8250_modem_status(up);
if (status & UART_LSR_THRE)
if (!up->dma && (status & UART_LSR_THRE))
serial8250_tx_chars(up);

spin_unlock_irqrestore(&port->lock, flags);
Expand Down
9 changes: 7 additions & 2 deletions drivers/tty/serial/8250/8250_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ static void __dma_tx_complete(void *param)
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct circ_buf *xmit = &p->port.state->xmit;

dma->tx_running = 0;
unsigned long flags;

dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);

spin_lock_irqsave(&p->port.lock, flags);

dma->tx_running = 0;

xmit->tail += dma->tx_size;
xmit->tail &= UART_XMIT_SIZE - 1;
p->port.icount.tx += dma->tx_size;
Expand All @@ -35,6 +38,8 @@ static void __dma_tx_complete(void *param)

if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
serial8250_tx_dma(p);

spin_unlock_irqrestore(&p->port.lock, flags);
}

static void __dma_rx_complete(void *param)
Expand Down
23 changes: 12 additions & 11 deletions drivers/tty/serial/samsung.c
Original file line number Diff line number Diff line change
Expand Up @@ -1446,39 +1446,40 @@ static int s3c24xx_serial_get_poll_char(struct uart_port *port)
static void s3c24xx_serial_put_poll_char(struct uart_port *port,
unsigned char c)
{
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
unsigned int ucon = rd_regl(port, S3C2410_UCON);

/* not possible to xmit on unconfigured port */
if (!s3c24xx_port_configured(ucon))
return;

while (!s3c24xx_serial_console_txrdy(port, ufcon))
cpu_relax();
wr_regb(cons_uart, S3C2410_UTXH, c);
wr_regb(port, S3C2410_UTXH, c);
}

#endif /* CONFIG_CONSOLE_POLL */

static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);

/* not possible to xmit on unconfigured port */
if (!s3c24xx_port_configured(ucon))
return;
unsigned int ufcon = rd_regl(port, S3C2410_UFCON);

while (!s3c24xx_serial_console_txrdy(port, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, ch);
cpu_relax();
wr_regb(port, S3C2410_UTXH, ch);
}

static void
s3c24xx_serial_console_write(struct console *co, const char *s,
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);
}

Expand Down
39 changes: 21 additions & 18 deletions drivers/tty/serial/serial_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
if (uport->type == PORT_UNKNOWN)
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
* buffer.
Expand Down Expand Up @@ -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
* new port, try to restore the old settings.
*/
if (retval && old_type != PORT_UNKNOWN) {
if (retval) {
uport->iobase = old_iobase;
uport->type = old_type;
uport->hub6 = old_hub6;
uport->iotype = old_iotype;
uport->regshift = old_shift;
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;

/*
* We failed anyway.
*/
retval = -EBUSY;
if (old_type != PORT_UNKNOWN) {
retval = uport->ops->request_port(uport);
/*
* 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 */
goto exit;
}
Expand Down Expand Up @@ -1570,12 +1579,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
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.
*/
Expand Down
16 changes: 14 additions & 2 deletions drivers/tty/tty_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,16 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size,
if (change || left < size) {
/* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_alloc(port, size)) != NULL) {
unsigned long iflags;

n->flags = flags;
buf->tail = n;

spin_lock_irqsave(&buf->flush_lock, iflags);
b->commit = b->used;
smp_mb();
b->next = n;
spin_unlock_irqrestore(&buf->flush_lock, iflags);

} else if (change)
size = 0;
else
Expand Down Expand Up @@ -443,21 +448,27 @@ static void flush_to_ldisc(struct work_struct *work)
mutex_lock(&buf->lock);

while (1) {
unsigned long flags;
struct tty_buffer *head = buf->head;
int count;

/* Ldisc or user is trying to gain exclusive access */
if (atomic_read(&buf->priority))
break;

spin_lock_irqsave(&buf->flush_lock, flags);
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
if (head->next == NULL) {
spin_unlock_irqrestore(&buf->flush_lock, flags);
break;
}
buf->head = head->next;
spin_unlock_irqrestore(&buf->flush_lock, flags);
tty_buffer_free(port, head);
continue;
}
spin_unlock_irqrestore(&buf->flush_lock, flags);

count = receive_buf(tty, head, count);
if (!count)
Expand Down Expand Up @@ -512,6 +523,7 @@ void tty_buffer_init(struct tty_port *port)
struct tty_bufhead *buf = &port->buf;

mutex_init(&buf->lock);
spin_lock_init(&buf->flush_lock);
tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
Expand Down
1 change: 1 addition & 0 deletions include/linux/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct tty_bufhead {
struct tty_buffer *head; /* Queue head */
struct work_struct work;
struct mutex lock;
spinlock_t flush_lock;
atomic_t priority;
struct tty_buffer sentinel;
struct llist_head free; /* Free queue head */
Expand Down

0 comments on commit a8d7069

Please sign in to comment.