Skip to content

Commit 15b82b4

Browse files
Nicolas PitrePierre Ossman
authored andcommitted
sdio: fix recursion issues between sdio-uart driver and tty layer
In a few places, sdio_uart_irq() is called directly instead of waiting for the actual interrupt to be raised and the SDIO IRQ thread scheduled in order to reduce latency. However, some interaction with the tty core may end up calling us back (serial echo, flow control, etc.) creating two issues: - the host lock gets claimed twice from the same thread causing a deadlock; - the same direct calls to sdio_uart_irq() may be performed causing unexpected reentrancy into the IRQ handler. This patch handles both of those issues. Signed-off-by: Nicolas Pitre <npitre@mvista.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
1 parent 2ba30ee commit 15b82b4

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

drivers/mmc/card/sdio_uart.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct sdio_uart_port {
7979
struct mutex open_lock;
8080
struct sdio_func *func;
8181
struct mutex func_lock;
82+
struct task_struct *in_sdio_uart_irq;
8283
unsigned int regs_offset;
8384
struct circ_buf xmit;
8485
spinlock_t write_lock;
@@ -186,14 +187,16 @@ static int sdio_uart_claim_func(struct sdio_uart_port *port)
186187
mutex_unlock(&port->func_lock);
187188
return -ENODEV;
188189
}
189-
sdio_claim_host(port->func);
190+
if (likely(port->in_sdio_uart_irq != current))
191+
sdio_claim_host(port->func);
190192
mutex_unlock(&port->func_lock);
191193
return 0;
192194
}
193195

194196
static inline void sdio_uart_release_func(struct sdio_uart_port *port)
195197
{
196-
sdio_release_host(port->func);
198+
if (likely(port->in_sdio_uart_irq != current))
199+
sdio_release_host(port->func);
197200
}
198201

199202
static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
@@ -511,15 +514,29 @@ static void sdio_uart_irq(struct sdio_func *func)
511514
struct sdio_uart_port *port = sdio_get_drvdata(func);
512515
unsigned int iir, lsr;
513516

517+
/*
518+
* In a few places sdio_uart_irq() is called directly instead of
519+
* waiting for the actual interrupt to be raised and the SDIO IRQ
520+
* thread scheduled in order to reduce latency. However, some
521+
* interaction with the tty core may end up calling us back
522+
* (serial echo, flow control, etc.) through those same places
523+
* causing undesirable effects. Let's stop the recursion here.
524+
*/
525+
if (unlikely(port->in_sdio_uart_irq == current))
526+
return;
527+
514528
iir = sdio_in(port, UART_IIR);
515529
if (iir & UART_IIR_NO_INT)
516530
return;
531+
532+
port->in_sdio_uart_irq = current;
517533
lsr = sdio_in(port, UART_LSR);
518534
if (lsr & UART_LSR_DR)
519535
sdio_uart_receive_chars(port, &lsr);
520536
sdio_uart_check_modem_status(port);
521537
if (lsr & UART_LSR_THRE)
522538
sdio_uart_transmit_chars(port);
539+
port->in_sdio_uart_irq = NULL;
523540
}
524541

525542
static int sdio_uart_startup(struct sdio_uart_port *port)

0 commit comments

Comments
 (0)