Skip to content

Commit 507786c

Browse files
jhovoldgregkh
authored andcommitted
serial: qcom-geni: fix hard lockup on buffer flush
The Qualcomm GENI serial driver does not handle buffer flushing and used to continue printing discarded characters when the circular buffer was cleared. Since commit 1788cf6 ("tty: serial: switch from circ_buf to kfifo") this instead results in a hard lockup due to qcom_geni_serial_send_chunk_fifo() spinning indefinitely in the interrupt handler. This is easily triggered by interrupting a command such as dmesg in a serial console but can also happen when stopping a serial getty on reboot. Implement the flush_buffer() callback and use it to cancel any active TX command when the write buffer has been emptied. Reported-by: Douglas Anderson <[email protected]> Link: https://lore.kernel.org/lkml/[email protected]/ Fixes: 1788cf6 ("tty: serial: switch from circ_buf to kfifo") Fixes: a1fee89 ("tty: serial: qcom_geni_serial: Fix softlock") Cc: [email protected] # 5.0 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 947cc4e commit 507786c

File tree

1 file changed

+13
-3
lines changed

1 file changed

+13
-3
lines changed

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -906,13 +906,17 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
906906
else
907907
pending = kfifo_len(&tport->xmit_fifo);
908908

909-
/* All data has been transmitted and acknowledged as received */
910-
if (!pending && !status && done) {
909+
/* All data has been transmitted or command has been cancelled */
910+
if (!pending && done) {
911911
qcom_geni_serial_stop_tx_fifo(uport);
912912
goto out_write_wakeup;
913913
}
914914

915-
avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
915+
if (active)
916+
avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
917+
else
918+
avail = port->tx_fifo_depth;
919+
916920
avail *= BYTES_PER_FIFO_WORD;
917921

918922
chunk = min(avail, pending);
@@ -1091,6 +1095,11 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
10911095
qcom_geni_serial_cancel_tx_cmd(uport);
10921096
}
10931097

1098+
static void qcom_geni_serial_flush_buffer(struct uart_port *uport)
1099+
{
1100+
qcom_geni_serial_cancel_tx_cmd(uport);
1101+
}
1102+
10941103
static int qcom_geni_serial_port_setup(struct uart_port *uport)
10951104
{
10961105
struct qcom_geni_serial_port *port = to_dev_port(uport);
@@ -1547,6 +1556,7 @@ static const struct uart_ops qcom_geni_console_pops = {
15471556
.request_port = qcom_geni_serial_request_port,
15481557
.config_port = qcom_geni_serial_config_port,
15491558
.shutdown = qcom_geni_serial_shutdown,
1559+
.flush_buffer = qcom_geni_serial_flush_buffer,
15501560
.type = qcom_geni_serial_get_type,
15511561
.set_mctrl = qcom_geni_serial_set_mctrl,
15521562
.get_mctrl = qcom_geni_serial_get_mctrl,

0 commit comments

Comments
 (0)