Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5175e0d3 authored by Mukesh Kumar Savaliya's avatar Mukesh Kumar Savaliya
Browse files

serial: msm_geni_serial: Fix the race between termios and ISR



This patch makes sure to process the RX EOT bit post cancel command
as part of stop rx sequencer. There could be a race between ISR and
userspace thread doing stop rx where ISR clears out the interrupts
generated as part of other operations and EOT poll may timeout.

Also there are chances that stop_rx can generate an interrupt if the
peer device sends data when client hasn't disabled the flow control.
This will trigger a call to handle_dma_rx which basically un-maps the
rx dma buffer, handles the rx data and remaps the same rx dma buffer.

As part of baud change, make sure ISR gets called exclusively against
the start_rx call. There is a slight window where dma_map of start_rx
sets the iova as DMA_ERROR_CODE for a while before actually mapping
to valid dma address and ISR uses this invalid address as part of
un-mapping the same buffer address which results into the page fault.

Change-Id: I9c69f7f9399aac060188ccee5648b8b7c46a656b
Signed-off-by: default avatarMukesh Kumar Savaliya <msavaliy@codeaurora.org>
parent 020642e2
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -614,7 +614,6 @@ static void msm_geni_serial_abort_rx(struct uart_port *uport)
static void msm_geni_serial_complete_rx_eot(struct uart_port *uport)
{
	int poll_done = 0, tries = 0;
	u32 geni_status = 0;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

	do {
@@ -623,11 +622,11 @@ static void msm_geni_serial_complete_rx_eot(struct uart_port *uport)
		tries++;
	} while (!poll_done && tries < 5);

	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);

	if (!poll_done)
		IPC_LOG_MSG(port->ipc_log_misc, "%s: RX_EOT, GENI:0x%x\n",
							__func__, geni_status);
		IPC_LOG_MSG(port->ipc_log_misc,
		"%s: RX_EOT, GENI:0x%x, DMA_DEBUG:0x%x\n", __func__,
		geni_read_reg_nolog(uport->membase, SE_GENI_STATUS),
		geni_read_reg_nolog(uport->membase, SE_DMA_DEBUG_REG0));
	else
		geni_write_reg_nolog(RX_EOT, uport->membase, SE_DMA_RX_IRQ_CLR);
}
@@ -1128,7 +1127,9 @@ static void stop_rx_sequencer(struct uart_port *uport)
	 * cancel control bit.
	 */
	mb();
	if (!uart_console(uport))
		msm_geni_serial_complete_rx_eot(uport);

	done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
					S_GENI_CMD_CANCEL, false);
	if (done) {
@@ -1853,6 +1854,7 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
	unsigned long ser_clk_cfg = 0;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
	unsigned long clk_rate;
	unsigned long flags;

	if (!uart_console(uport)) {
		int ret = msm_geni_serial_power_on(uport);
@@ -1864,7 +1866,13 @@ static void msm_geni_serial_set_termios(struct uart_port *uport,
			return;
		}
	}
	/* Take a spinlock else stop_rx causes a race with an ISR due to Cancel
	 * and FSM_RESET. This also has a potential race with the dma_map/unmap
	 * operations of ISR.
	 */
	spin_lock_irqsave(&uport->lock, flags);
	msm_geni_serial_stop_rx(uport);
	spin_unlock_irqrestore(&uport->lock, flags);
	/* baud rate */
	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
	port->cur_baud = baud;