Skip to content

Commit 63d14d9

Browse files
jhovoldgregkh
authored andcommitted
serial: qcom-geni: fix polled console corruption
The polled UART operations are used by the kernel debugger (KDB, KGDB), which can interrupt the kernel at any point in time. The current Qualcomm GENI implementation does not really work when there is on-going serial output as it inadvertently "hijacks" the current tx command, which can result in both the initial debugger output being corrupted as well as the corruption of any on-going serial output (up to 4k characters) when execution resumes: 0190: abcdefghijklmnopqrstuvwxyz0123456789 0190: abcdefghijklmnopqrstuvwxyz0123456789 0191: abcdefghijklmnop[ 50.825552] sysrq: DEBUG qrstuvwxyz0123456789 0191: abcdefghijklmnopqrstuvwxyz0123456789 Entering kdb (current=0xffff53510b4cd280, pid 640) on processor 2 due to Keyboard Entry [2]kdb> go omlji3h3h2g2g1f1f0e0ezdzdycycxbxbwawav :t72r2rp o9n976k5j5j4i4i3h3h2g2g1f1f0e0ezdzdycycxbxbwawavu:t7t8s8s8r2r2q0q0p o9n9n8ml6k6k5j5j4i4i3h3h2g2g1f1f0e0ezdzdycycxbxbwawav v u:u:t9t0s4s4rq0p o9n9n8m8m7l7l6k6k5j5j40q0p p o o9n9n8m8m7l7l6k6k5j5j4i4i3h3h2g2g1f1f0e0ezdzdycycxbxbwawav :t8t9s4s4r4r4q0q0p Fix this by making sure that the polled output implementation waits for the tx fifo to drain before cancelling any on-going longer transfers. As the polled code cannot take any locks, leave the state variables as they are and instead make sure that the interrupt handler always starts a new tx command when there is data in the write buffer. Since the debugger can interrupt the interrupt handler when it is writing data to the tx fifo, it is currently not possible to fully prevent losing up to 64 bytes of tty output on resume. Fixes: c4f5287 ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") Cc: [email protected] # 4.17 Reviewed-by: Douglas Anderson <[email protected]> Tested-by: Nícolas F. R. A. Prado <[email protected]> Signed-off-by: Johan Hovold <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6f3c3ca commit 63d14d9

File tree

1 file changed

+18
-9
lines changed

1 file changed

+18
-9
lines changed

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ static const struct uart_ops qcom_geni_uart_pops;
145145
static struct uart_driver qcom_geni_console_driver;
146146
static struct uart_driver qcom_geni_uart_driver;
147147

148+
static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
148149
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
149150

150151
static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
@@ -384,13 +385,14 @@ static int qcom_geni_serial_get_char(struct uart_port *uport)
384385
static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
385386
unsigned char c)
386387
{
387-
writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
388+
if (qcom_geni_serial_main_active(uport)) {
389+
qcom_geni_serial_poll_tx_done(uport);
390+
__qcom_geni_serial_cancel_tx_cmd(uport);
391+
}
392+
388393
writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
389394
qcom_geni_serial_setup_tx(uport, 1);
390-
WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
391-
M_TX_FIFO_WATERMARK_EN, true));
392395
writel(c, uport->membase + SE_GENI_TX_FIFOn);
393-
writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
394396
qcom_geni_serial_poll_tx_done(uport);
395397
}
396398
#endif
@@ -677,13 +679,10 @@ static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport)
677679
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
678680
}
679681

680-
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
682+
static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
681683
{
682684
struct qcom_geni_serial_port *port = to_dev_port(uport);
683685

684-
if (!qcom_geni_serial_main_active(uport))
685-
return;
686-
687686
geni_se_cancel_m_cmd(&port->se);
688687
if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
689688
M_CMD_CANCEL_EN, true)) {
@@ -693,6 +692,16 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
693692
writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
694693
}
695694
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
695+
}
696+
697+
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
698+
{
699+
struct qcom_geni_serial_port *port = to_dev_port(uport);
700+
701+
if (!qcom_geni_serial_main_active(uport))
702+
return;
703+
704+
__qcom_geni_serial_cancel_tx_cmd(uport);
696705

697706
port->tx_remaining = 0;
698707
port->tx_queued = 0;
@@ -919,7 +928,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
919928
if (!chunk)
920929
goto out_write_wakeup;
921930

922-
if (!port->tx_remaining) {
931+
if (!active) {
923932
qcom_geni_serial_setup_tx(uport, pending);
924933
port->tx_remaining = pending;
925934
port->tx_queued = 0;

0 commit comments

Comments
 (0)