| From a0ec1fcfd1a61b1e07e971941299db39889a7715 Mon Sep 17 00:00:00 2001 |
| From: Takatoshi Akiyama <takatoshi.akiyama.kj@ps.hitachi-solutions.com> |
| Date: Thu, 2 Nov 2017 11:14:55 +0100 |
| Subject: [PATCH 0227/1795] serial: sh-sci: Fix unlocked access to SCSCR |
| register |
| |
| The SCSCR register access in sci_break_ctl() is not locked. |
| |
| sci_start_tx() and sci_set_termios() changes the SCSCR register, |
| but does not lock sci_port. |
| |
| Therefore, this patch adds lock during register access. |
| |
| Also, remove the log output that leads to a double lock. |
| |
| Some analysis of where locks are not taken is as follows. |
| It appears that the lock is not taken in: |
| - sci_start_tx(), sci_stop_tx() as this is installed as a callback. |
| And all callers of the callback take the lock. |
| - start_rx as callers take the lock. |
| - stop_rx. this is both installed as a callback and called directly. |
| In both cases the caller takes the lock. |
| |
| Signed-off-by: Takatoshi Akiyama <takatoshi.akiyama.kj@ps.hitachi-solutions.com> |
| Signed-off-by: Takeshi Kihara <takeshi.kihara.df@renesas.com> |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit 1be2266392196de82d1cfcc8a68e770cf8f48c60) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| drivers/tty/serial/sh-sci.c | 25 ++++++++++++++++--------- |
| 1 file changed, 16 insertions(+), 9 deletions(-) |
| |
| diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c |
| index 7a6b896a2324..78f862df4f6e 100644 |
| --- a/drivers/tty/serial/sh-sci.c |
| +++ b/drivers/tty/serial/sh-sci.c |
| @@ -1225,8 +1225,11 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) |
| dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], |
| sg_dma_address(&s->sg_rx[0])); |
| dma_release_channel(chan); |
| - if (enable_pio) |
| + if (enable_pio) { |
| + spin_lock_irqsave(&port->lock, flags); |
| sci_start_rx(port); |
| + spin_unlock_irqrestore(&port->lock, flags); |
| + } |
| } |
| |
| static void sci_dma_rx_complete(void *arg) |
| @@ -1293,8 +1296,11 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) |
| dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, |
| DMA_TO_DEVICE); |
| dma_release_channel(chan); |
| - if (enable_pio) |
| + if (enable_pio) { |
| + spin_lock_irqsave(&port->lock, flags); |
| sci_start_tx(port); |
| + spin_unlock_irqrestore(&port->lock, flags); |
| + } |
| } |
| |
| static void sci_submit_rx(struct sci_port *s) |
| @@ -2003,6 +2009,7 @@ static void sci_enable_ms(struct uart_port *port) |
| static void sci_break_ctl(struct uart_port *port, int break_state) |
| { |
| unsigned short scscr, scsptr; |
| + unsigned long flags; |
| |
| /* check wheter the port has SCSPTR */ |
| if (!sci_getreg(port, SCSPTR)->size) { |
| @@ -2013,6 +2020,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) |
| return; |
| } |
| |
| + spin_lock_irqsave(&port->lock, flags); |
| scsptr = serial_port_in(port, SCSPTR); |
| scscr = serial_port_in(port, SCSCR); |
| |
| @@ -2026,6 +2034,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) |
| |
| serial_port_out(port, SCSPTR, scsptr); |
| serial_port_out(port, SCSCR, scscr); |
| + spin_unlock_irqrestore(&port->lock, flags); |
| } |
| |
| static int sci_startup(struct uart_port *port) |
| @@ -2254,6 +2263,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| int min_err = INT_MAX, err; |
| unsigned long max_freq = 0; |
| int best_clk = -1; |
| + unsigned long flags; |
| |
| if ((termios->c_cflag & CSIZE) == CS7) |
| smr_val |= SCSMR_CHR; |
| @@ -2363,6 +2373,8 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| serial_port_out(port, SCCKS, sccks); |
| } |
| |
| + spin_lock_irqsave(&port->lock, flags); |
| + |
| sci_reset(port); |
| |
| uart_update_timeout(port, termios->c_cflag, baud); |
| @@ -2380,9 +2392,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| case 27: smr_val |= SCSMR_SRC_27; break; |
| } |
| smr_val |= cks; |
| - dev_dbg(port->dev, |
| - "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x DL %u SRR %u\n", |
| - scr_val, smr_val, brr, sccks, dl, srr); |
| serial_port_out(port, SCSCR, scr_val | s->hscif_tot); |
| serial_port_out(port, SCSMR, smr_val); |
| serial_port_out(port, SCBRR, brr); |
| @@ -2396,7 +2405,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0); |
| smr_val |= serial_port_in(port, SCSMR) & |
| (SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS); |
| - dev_dbg(port->dev, "SCR 0x%x SMR 0x%x\n", scr_val, smr_val); |
| serial_port_out(port, SCSCR, scr_val | s->hscif_tot); |
| serial_port_out(port, SCSMR, smr_val); |
| } |
| @@ -2433,7 +2441,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| |
| scr_val |= SCSCR_RE | SCSCR_TE | |
| (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)); |
| - dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val); |
| serial_port_out(port, SCSCR, scr_val | s->hscif_tot); |
| if ((srr + 1 == 5) && |
| (port->type == PORT_SCIFA || port->type == PORT_SCIFB)) { |
| @@ -2480,8 +2487,6 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| s->rx_frame = (100 * bits * HZ) / (baud / 10); |
| #ifdef CONFIG_SERIAL_SH_SCI_DMA |
| s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000); |
| - dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", |
| - s->rx_timeout * 1000 / HZ, port->timeout); |
| if (s->rx_timeout < msecs_to_jiffies(20)) |
| s->rx_timeout = msecs_to_jiffies(20); |
| #endif |
| @@ -2489,6 +2494,8 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, |
| if ((termios->c_cflag & CREAD) != 0) |
| sci_start_rx(port); |
| |
| + spin_unlock_irqrestore(&port->lock, flags); |
| + |
| sci_port_disable(s); |
| |
| if (UART_ENABLE_MS(port, termios->c_cflag)) |
| -- |
| 2.19.0 |
| |