diff mbox series

serial: stm32: Move hard IRQ handling to threaded interrupt context

Message ID 20221207195929.160267-1-marex@denx.de (mailing list archive)
State New, archived
Headers show
Series serial: stm32: Move hard IRQ handling to threaded interrupt context | expand

Commit Message

Marek Vasut Dec. 7, 2022, 7:59 p.m. UTC
Avoid locking in hard interrupt context, move the entirety of hard IRQ
context code into the threaded IRQ handler. This fixes the following
splat with preempt-rt enabled:

 BUG: scheduling while atomic: (mount)/1289/0x00010001
 Modules linked in:
 Preemption disabled at:
 [<c0119127>] irq_enter_rcu+0xb/0x42
 CPU: 0 PID: 1289 Comm: (mount) Not tainted 6.1.0-rc7-rt5-stable-standard-00006-gd70aeccb9f0f #17
 Hardware name: STM32 (Device Tree Support)
  unwind_backtrace from show_stack+0xb/0xc
  show_stack from dump_stack_lvl+0x2b/0x34
  dump_stack_lvl from __schedule_bug+0x53/0x80
  __schedule_bug from __schedule+0x47/0x404
  __schedule from schedule_rtlock+0x15/0x34
  schedule_rtlock from rtlock_slowlock_locked+0x1d7/0x57e
  rtlock_slowlock_locked from rt_spin_lock+0x29/0x3c
  rt_spin_lock from stm32_usart_interrupt+0xa9/0x110
  stm32_usart_interrupt from __handle_irq_event_percpu+0x73/0x14e
  __handle_irq_event_percpu from handle_irq_event_percpu+0x9/0x22
  handle_irq_event_percpu from handle_irq_event+0x53/0x76
  handle_irq_event from handle_fasteoi_irq+0x65/0xa8
  handle_fasteoi_irq from handle_irq_desc+0xf/0x18
  handle_irq_desc from gic_handle_irq+0x45/0x54
  gic_handle_irq from generic_handle_arch_irq+0x19/0x2c
  generic_handle_arch_irq from call_with_stack+0xd/0x10

Signed-off-by: Marek Vasut <marex@denx.de>
---
Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: Erwan Le Ray <erwan.leray@foss.st.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jean Philippe Romain <jean-philippe.romain@foss.st.com>
Cc: Jiri Slaby <jirislaby@kernel.org>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Valentin Caron <valentin.caron@foss.st.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-stm32@st-md-mailman.stormreply.com
To: linux-serial@vger.kernel.org
---
 drivers/tty/serial/stm32-usart.c | 27 +++++++--------------------
 1 file changed, 7 insertions(+), 20 deletions(-)

Comments

Sebastian Sewior Dec. 8, 2022, 7:52 a.m. UTC | #1
- Jean Philippe Romain <jean-philippe.romain@foss.st.com>

Could you please drop that email address in future post as I did? It
comes back here with:
| Remote-MTA: dns; mxb-00178001.gslb.pphosted.com
| Diagnostic-Code: smtp; 550 5.1.1 User Unknown

On 2022-12-07 20:59:29 [+0100], Marek Vasut wrote:
> diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
> index a1490033aa164..f5bce0be02676 100644
> --- a/drivers/tty/serial/stm32-usart.c
> +++ b/drivers/tty/serial/stm32-usart.c
> @@ -1015,7 +1002,7 @@ static int stm32_usart_startup(struct uart_port *port)
>  	u32 val;
>  	int ret;
>  
> -	ret = request_threaded_irq(port->irq, stm32_usart_interrupt,
> +	ret = request_threaded_irq(port->irq, NULL,
>  				   stm32_usart_threaded_interrupt,
>  				   IRQF_ONESHOT | IRQF_NO_SUSPEND,
>  				   name, port);

So why don't you just
	request_irq(port->irq, stm32_usart_interrupt, IRQF_NO_SUSPEND,
		    name, port)
? If there is a requirement to always run in threaded mode please
document it by at least making it part of the commit description.

Sebastian
Valentin Caron Dec. 8, 2022, 10:18 a.m. UTC | #2
Hi Marek,

I've got a patch in the same spirit in downstream.
The test campaign reveals performance issues with this patch.

In fact, hard IRQ have been introduced in stm32-usart driver to solve 
performance issues due to short FIFO size (16 bytes).

We are currently testing another patch, similar as your RFC proposition, 
for RT context.
But results are not ready yet. We can wait them before merging this big 
change into driver ?

Thanks,
Valentin

On 12/7/22 20:59, Marek Vasut wrote:
> Avoid locking in hard interrupt context, move the entirety of hard IRQ
> context code into the threaded IRQ handler. This fixes the following
> splat with preempt-rt enabled:
>
>   BUG: scheduling while atomic: (mount)/1289/0x00010001
>   Modules linked in:
>   Preemption disabled at:
>   [<c0119127>] irq_enter_rcu+0xb/0x42
>   CPU: 0 PID: 1289 Comm: (mount) Not tainted 6.1.0-rc7-rt5-stable-standard-00006-gd70aeccb9f0f #17
>   Hardware name: STM32 (Device Tree Support)
>    unwind_backtrace from show_stack+0xb/0xc
>    show_stack from dump_stack_lvl+0x2b/0x34
>    dump_stack_lvl from __schedule_bug+0x53/0x80
>    __schedule_bug from __schedule+0x47/0x404
>    __schedule from schedule_rtlock+0x15/0x34
>    schedule_rtlock from rtlock_slowlock_locked+0x1d7/0x57e
>    rtlock_slowlock_locked from rt_spin_lock+0x29/0x3c
>    rt_spin_lock from stm32_usart_interrupt+0xa9/0x110
>    stm32_usart_interrupt from __handle_irq_event_percpu+0x73/0x14e
>    __handle_irq_event_percpu from handle_irq_event_percpu+0x9/0x22
>    handle_irq_event_percpu from handle_irq_event+0x53/0x76
>    handle_irq_event from handle_fasteoi_irq+0x65/0xa8
>    handle_fasteoi_irq from handle_irq_desc+0xf/0x18
>    handle_irq_desc from gic_handle_irq+0x45/0x54
>    gic_handle_irq from generic_handle_arch_irq+0x19/0x2c
>    generic_handle_arch_irq from call_with_stack+0xd/0x10
>
> Signed-off-by: Marek Vasut <marex@denx.de>
> ---
> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
> Cc: Erwan Le Ray <erwan.leray@foss.st.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jean Philippe Romain <jean-philippe.romain@foss.st.com>
> Cc: Jiri Slaby <jirislaby@kernel.org>
> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Valentin Caron <valentin.caron@foss.st.com>
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-stm32@st-md-mailman.stormreply.com
> To: linux-serial@vger.kernel.org
> ---
>   drivers/tty/serial/stm32-usart.c | 27 +++++++--------------------
>   1 file changed, 7 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
> index a1490033aa164..f5bce0be02676 100644
> --- a/drivers/tty/serial/stm32-usart.c
> +++ b/drivers/tty/serial/stm32-usart.c
> @@ -745,14 +745,15 @@ static void stm32_usart_transmit_chars(struct uart_port *port)
>   	}
>   }
>   
> -static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
> +static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
>   {
>   	struct uart_port *port = ptr;
>   	struct tty_port *tport = &port->state->port;
>   	struct stm32_port *stm32_port = to_stm32_port(port);
>   	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
> -	u32 sr;
> +	unsigned long flags;
>   	unsigned int size;
> +	u32 sr;
>   
>   	sr = readl_relaxed(port->membase + ofs->isr);
>   
> @@ -792,27 +793,13 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
>   	}
>   
>   	if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) {
> -		spin_lock(&port->lock);
> +		spin_lock_irqsave(&port->lock, flags);
>   		stm32_usart_transmit_chars(port);
> -		spin_unlock(&port->lock);
> +		spin_unlock_irqrestore(&port->lock, flags);
>   	}
>   
> -	if (stm32_usart_rx_dma_enabled(port))
> -		return IRQ_WAKE_THREAD;
> -	else
> -		return IRQ_HANDLED;
> -}
> -
> -static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
> -{
> -	struct uart_port *port = ptr;
> -	struct tty_port *tport = &port->state->port;
> -	struct stm32_port *stm32_port = to_stm32_port(port);
> -	unsigned int size;
> -	unsigned long flags;
> -
>   	/* Receiver timeout irq for DMA RX */
> -	if (!stm32_port->throttled) {
> +	if (stm32_usart_rx_dma_enabled(port) && !stm32_port->throttled) {
>   		spin_lock_irqsave(&port->lock, flags);
>   		size = stm32_usart_receive_chars(port, false);
>   		uart_unlock_and_check_sysrq_irqrestore(port, flags);
> @@ -1015,7 +1002,7 @@ static int stm32_usart_startup(struct uart_port *port)
>   	u32 val;
>   	int ret;
>   
> -	ret = request_threaded_irq(port->irq, stm32_usart_interrupt,
> +	ret = request_threaded_irq(port->irq, NULL,
>   				   stm32_usart_threaded_interrupt,
>   				   IRQF_ONESHOT | IRQF_NO_SUSPEND,
>   				   name, port);
Marek Vasut Dec. 8, 2022, 1:19 p.m. UTC | #3
On 12/8/22 11:18, Valentin CARON wrote:
> Hi Marek,

Hi,

> I've got a patch in the same spirit in downstream.
> The test campaign reveals performance issues with this patch.
> 
> In fact, hard IRQ have been introduced in stm32-usart driver to solve 
> performance issues due to short FIFO size (16 bytes).
> 
> We are currently testing another patch, similar as your RFC proposition, 
> for RT context.
> But results are not ready yet. We can wait them before merging this big 
> change into driver ?

Can you post your patch as an RFC so others can test as well ?
Valentin Caron Dec. 15, 2022, 4:52 p.m. UTC | #4
Hi Marek,

I test your patch with a loop-back test between usart3 and uart7 on 
STM32MP157C-DK2. It
shows a speed transfer diminution (about -1,5%), some locks during 
transfer, RT throttling etc...
(I use lszrz tool).

But finally, I think your patch is the best solution that we have.
Other solutions always throws an RT exception on a spin_lock.

Thanks,
Valentin

On 12/8/22 14:19, Marek Vasut wrote:
> On 12/8/22 11:18, Valentin CARON wrote:
>> Hi Marek,
>
> Hi,
>
>> I've got a patch in the same spirit in downstream.
>> The test campaign reveals performance issues with this patch.
>>
>> In fact, hard IRQ have been introduced in stm32-usart driver to solve 
>> performance issues due to short FIFO size (16 bytes).
>>
>> We are currently testing another patch, similar as your RFC 
>> proposition, for RT context.
>> But results are not ready yet. We can wait them before merging this 
>> big change into driver ?
>
> Can you post your patch as an RFC so others can test as well ?
Marek Vasut Dec. 16, 2022, 2:16 a.m. UTC | #5
On 12/15/22 17:52, Valentin CARON wrote:
> Hi Marek,

Hi,

> I test your patch with a loop-back test between usart3 and uart7 on 
> STM32MP157C-DK2. It
> shows a speed transfer diminution (about -1,5%), some locks during 
> transfer, RT throttling etc...
> (I use lszrz tool).
> 
> But finally, I think your patch is the best solution that we have.
> Other solutions always throws an RT exception on a spin_lock.

I just sent a V2, so let's see how that works.

Thanks for testing!
diff mbox series

Patch

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index a1490033aa164..f5bce0be02676 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -745,14 +745,15 @@  static void stm32_usart_transmit_chars(struct uart_port *port)
 	}
 }
 
-static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
+static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
 {
 	struct uart_port *port = ptr;
 	struct tty_port *tport = &port->state->port;
 	struct stm32_port *stm32_port = to_stm32_port(port);
 	const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
-	u32 sr;
+	unsigned long flags;
 	unsigned int size;
+	u32 sr;
 
 	sr = readl_relaxed(port->membase + ofs->isr);
 
@@ -792,27 +793,13 @@  static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
 	}
 
 	if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) {
-		spin_lock(&port->lock);
+		spin_lock_irqsave(&port->lock, flags);
 		stm32_usart_transmit_chars(port);
-		spin_unlock(&port->lock);
+		spin_unlock_irqrestore(&port->lock, flags);
 	}
 
-	if (stm32_usart_rx_dma_enabled(port))
-		return IRQ_WAKE_THREAD;
-	else
-		return IRQ_HANDLED;
-}
-
-static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
-{
-	struct uart_port *port = ptr;
-	struct tty_port *tport = &port->state->port;
-	struct stm32_port *stm32_port = to_stm32_port(port);
-	unsigned int size;
-	unsigned long flags;
-
 	/* Receiver timeout irq for DMA RX */
-	if (!stm32_port->throttled) {
+	if (stm32_usart_rx_dma_enabled(port) && !stm32_port->throttled) {
 		spin_lock_irqsave(&port->lock, flags);
 		size = stm32_usart_receive_chars(port, false);
 		uart_unlock_and_check_sysrq_irqrestore(port, flags);
@@ -1015,7 +1002,7 @@  static int stm32_usart_startup(struct uart_port *port)
 	u32 val;
 	int ret;
 
-	ret = request_threaded_irq(port->irq, stm32_usart_interrupt,
+	ret = request_threaded_irq(port->irq, NULL,
 				   stm32_usart_threaded_interrupt,
 				   IRQF_ONESHOT | IRQF_NO_SUSPEND,
 				   name, port);