Skip to content

Commit 101aa46

Browse files
Uwe Kleine-Königgregkh
authored andcommitted
serial: imx: fix a race condition in receive path
The main irq handler function starts by first masking disabled interrupts in the status register values to ensure to only handle enabled interrupts. This is important as when the RX path in the hardware is disabled reading the RX fifo results in an external abort. This checking must be done under the port lock, otherwise the following can happen: CPU1 | CPU2 | irq triggers as there are chars | in the RX fifo | | grab port lock imx_uart_int finds RRDY enabled | and calls imx_uart_rxint which | has to wait for port lock | | disable RX (e.g. because we're | using RS485 with !RX_DURING_TX) | | release port lock read from RX fifo with RX | disabled => exception | So take the port lock only once in imx_uart_int() instead of in the functions called from there. Reported-by: Andre Renaud <[email protected]> Cc: [email protected] Signed-off-by: Uwe Kleine-König <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 48d414a commit 101aa46

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

drivers/tty/serial/imx.c

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -696,22 +696,33 @@ static void imx_uart_start_tx(struct uart_port *port)
696696
}
697697
}
698698

699-
static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
699+
static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)
700700
{
701701
struct imx_port *sport = dev_id;
702702
u32 usr1;
703703

704-
spin_lock(&sport->port.lock);
705-
706704
imx_uart_writel(sport, USR1_RTSD, USR1);
707705
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
708706
uart_handle_cts_change(&sport->port, !!usr1);
709707
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
710708

711-
spin_unlock(&sport->port.lock);
712709
return IRQ_HANDLED;
713710
}
714711

712+
static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
713+
{
714+
struct imx_port *sport = dev_id;
715+
irqreturn_t ret;
716+
717+
spin_lock(&sport->port.lock);
718+
719+
ret = __imx_uart_rtsint(irq, dev_id);
720+
721+
spin_unlock(&sport->port.lock);
722+
723+
return ret;
724+
}
725+
715726
static irqreturn_t imx_uart_txint(int irq, void *dev_id)
716727
{
717728
struct imx_port *sport = dev_id;
@@ -722,14 +733,12 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id)
722733
return IRQ_HANDLED;
723734
}
724735

725-
static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
736+
static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
726737
{
727738
struct imx_port *sport = dev_id;
728739
unsigned int rx, flg, ignored = 0;
729740
struct tty_port *port = &sport->port.state->port;
730741

731-
spin_lock(&sport->port.lock);
732-
733742
while (imx_uart_readl(sport, USR2) & USR2_RDR) {
734743
u32 usr2;
735744

@@ -786,11 +795,25 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
786795
}
787796

788797
out:
789-
spin_unlock(&sport->port.lock);
790798
tty_flip_buffer_push(port);
799+
791800
return IRQ_HANDLED;
792801
}
793802

803+
static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
804+
{
805+
struct imx_port *sport = dev_id;
806+
irqreturn_t ret;
807+
808+
spin_lock(&sport->port.lock);
809+
810+
ret = __imx_uart_rxint(irq, dev_id);
811+
812+
spin_unlock(&sport->port.lock);
813+
814+
return ret;
815+
}
816+
794817
static void imx_uart_clear_rx_errors(struct imx_port *sport);
795818

796819
/*
@@ -849,6 +872,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
849872
unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
850873
irqreturn_t ret = IRQ_NONE;
851874

875+
spin_lock(&sport->port.lock);
876+
852877
usr1 = imx_uart_readl(sport, USR1);
853878
usr2 = imx_uart_readl(sport, USR2);
854879
ucr1 = imx_uart_readl(sport, UCR1);
@@ -882,27 +907,25 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
882907
usr2 &= ~USR2_ORE;
883908

884909
if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
885-
imx_uart_rxint(irq, dev_id);
910+
__imx_uart_rxint(irq, dev_id);
886911
ret = IRQ_HANDLED;
887912
}
888913

889914
if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
890-
imx_uart_txint(irq, dev_id);
915+
imx_uart_transmit_buffer(sport);
891916
ret = IRQ_HANDLED;
892917
}
893918

894919
if (usr1 & USR1_DTRD) {
895920
imx_uart_writel(sport, USR1_DTRD, USR1);
896921

897-
spin_lock(&sport->port.lock);
898922
imx_uart_mctrl_check(sport);
899-
spin_unlock(&sport->port.lock);
900923

901924
ret = IRQ_HANDLED;
902925
}
903926

904927
if (usr1 & USR1_RTSD) {
905-
imx_uart_rtsint(irq, dev_id);
928+
__imx_uart_rtsint(irq, dev_id);
906929
ret = IRQ_HANDLED;
907930
}
908931

@@ -917,6 +940,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
917940
ret = IRQ_HANDLED;
918941
}
919942

943+
spin_unlock(&sport->port.lock);
944+
920945
return ret;
921946
}
922947

0 commit comments

Comments
 (0)