diff mbox series

[net] net: ipa: disable ipa interrupt during suspend

Message ID 20230115175925.465918-1-caleb.connolly@linaro.org (mailing list archive)
State Accepted
Commit 9ec9b2a30853ba843b70ea16f196e5fe3327be5f
Delegated to: Netdev Maintainers
Headers show
Series [net] net: ipa: disable ipa interrupt during suspend | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 67 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Caleb Connolly Jan. 15, 2023, 5:59 p.m. UTC
The IPA interrupt can fire when pm_runtime is disabled due to it racing
with the PM suspend/resume code. This causes a splat in the interrupt
handler when it tries to call pm_runtime_get().

Explicitly disable the interrupt in our ->suspend callback, and
re-enable it in ->resume to avoid this. If there is an interrupt pending
it will be handled after resuming. The interrupt is a wake_irq, as a
result even when disabled if it fires it will cause the system to wake
from suspend as well as cancel any suspend transition that may be in
progress. If there is an interrupt pending, the ipa_isr_thread handler
will be called after resuming.

Fixes: 1aac309d3207 ("net: ipa: use autosuspend")
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
 drivers/net/ipa/ipa_interrupt.c | 10 ++++++++++
 drivers/net/ipa/ipa_interrupt.h | 16 ++++++++++++++++
 drivers/net/ipa/ipa_power.c     | 17 +++++++++++++++++
 3 files changed, 43 insertions(+)

Comments

Alex Elder Jan. 17, 2023, 6:29 p.m. UTC | #1
On 1/15/23 11:59 AM, Caleb Connolly wrote:
> The IPA interrupt can fire when pm_runtime is disabled due to it racing
> with the PM suspend/resume code. This causes a splat in the interrupt
> handler when it tries to call pm_runtime_get().
> 
> Explicitly disable the interrupt in our ->suspend callback, and
> re-enable it in ->resume to avoid this. If there is an interrupt pending
> it will be handled after resuming. The interrupt is a wake_irq, as a
> result even when disabled if it fires it will cause the system to wake
> from suspend as well as cancel any suspend transition that may be in
> progress. If there is an interrupt pending, the ipa_isr_thread handler
> will be called after resuming.

Looks good to me.  Thanks!

Reviewed-by: Alex Elder <elder@linaro.org>

> 
> Fixes: 1aac309d3207 ("net: ipa: use autosuspend")
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
> ---
>   drivers/net/ipa/ipa_interrupt.c | 10 ++++++++++
>   drivers/net/ipa/ipa_interrupt.h | 16 ++++++++++++++++
>   drivers/net/ipa/ipa_power.c     | 17 +++++++++++++++++
>   3 files changed, 43 insertions(+)
> 
> diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c
> index d458a35839cc..c1b3977e1ae4 100644
> --- a/drivers/net/ipa/ipa_interrupt.c
> +++ b/drivers/net/ipa/ipa_interrupt.c
> @@ -127,6 +127,16 @@ static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
>   	return IRQ_HANDLED;
>   }
>   
> +void ipa_interrupt_irq_disable(struct ipa *ipa)
> +{
> +	disable_irq(ipa->interrupt->irq);
> +}
> +
> +void ipa_interrupt_irq_enable(struct ipa *ipa)
> +{
> +	enable_irq(ipa->interrupt->irq);
> +}
> +
>   /* Common function used to enable/disable TX_SUSPEND for an endpoint */
>   static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
>   					  u32 endpoint_id, bool enable)
> diff --git a/drivers/net/ipa/ipa_interrupt.h b/drivers/net/ipa/ipa_interrupt.h
> index f31fd9965fdc..8a1bd5b89393 100644
> --- a/drivers/net/ipa/ipa_interrupt.h
> +++ b/drivers/net/ipa/ipa_interrupt.h
> @@ -85,6 +85,22 @@ void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt);
>    */
>   void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt);
>   
> +/**
> + * ipa_interrupt_irq_enable() - Enable IPA interrupts
> + * @ipa:	IPA pointer
> + *
> + * This enables the IPA interrupt line
> + */
> +void ipa_interrupt_irq_enable(struct ipa *ipa);
> +
> +/**
> + * ipa_interrupt_irq_disable() - Disable IPA interrupts
> + * @ipa:	IPA pointer
> + *
> + * This disables the IPA interrupt line
> + */
> +void ipa_interrupt_irq_disable(struct ipa *ipa);
> +
>   /**
>    * ipa_interrupt_config() - Configure the IPA interrupt framework
>    * @ipa:	IPA pointer
> diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c
> index 8420f93128a2..8057be8cda80 100644
> --- a/drivers/net/ipa/ipa_power.c
> +++ b/drivers/net/ipa/ipa_power.c
> @@ -181,6 +181,17 @@ static int ipa_suspend(struct device *dev)
>   
>   	__set_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
>   
> +	/* Increment the disable depth to ensure that the IRQ won't
> +	 * be re-enabled until the matching _enable call in
> +	 * ipa_resume(). We do this to ensure that the interrupt
> +	 * handler won't run whilst PM runtime is disabled.
> +	 *
> +	 * Note that disabling the IRQ is NOT the same as disabling
> +	 * irq wake. If wakeup is enabled for the IPA then the IRQ
> +	 * will still cause the system to wake up, see irq_set_irq_wake().
> +	 */
> +	ipa_interrupt_irq_disable(ipa);
> +
>   	return pm_runtime_force_suspend(dev);
>   }
>   
> @@ -193,6 +204,12 @@ static int ipa_resume(struct device *dev)
>   
>   	__clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
>   
> +	/* Now that PM runtime is enabled again it's safe
> +	 * to turn the IRQ back on and process any data
> +	 * that was received during suspend.
> +	 */
> +	ipa_interrupt_irq_enable(ipa);
> +
>   	return ret;
>   }
>
patchwork-bot+netdevbpf@kernel.org Jan. 18, 2023, 3:10 a.m. UTC | #2
Hello:

This patch was applied to netdev/net.git (master)
by Jakub Kicinski <kuba@kernel.org>:

On Sun, 15 Jan 2023 17:59:24 +0000 you wrote:
> The IPA interrupt can fire when pm_runtime is disabled due to it racing
> with the PM suspend/resume code. This causes a splat in the interrupt
> handler when it tries to call pm_runtime_get().
> 
> Explicitly disable the interrupt in our ->suspend callback, and
> re-enable it in ->resume to avoid this. If there is an interrupt pending
> it will be handled after resuming. The interrupt is a wake_irq, as a
> result even when disabled if it fires it will cause the system to wake
> from suspend as well as cancel any suspend transition that may be in
> progress. If there is an interrupt pending, the ipa_isr_thread handler
> will be called after resuming.
> 
> [...]

Here is the summary with links:
  - [net] net: ipa: disable ipa interrupt during suspend
    https://git.kernel.org/netdev/net/c/9ec9b2a30853

You are awesome, thank you!
Matthieu Baerts Jan. 18, 2023, 10:19 a.m. UTC | #3
Hello,

On 15/01/2023 18:59, Caleb Connolly wrote:
> The IPA interrupt can fire when pm_runtime is disabled due to it racing
> with the PM suspend/resume code. This causes a splat in the interrupt
> handler when it tries to call pm_runtime_get().
> 
> Explicitly disable the interrupt in our ->suspend callback, and
> re-enable it in ->resume to avoid this. If there is an interrupt pending
> it will be handled after resuming. The interrupt is a wake_irq, as a
> result even when disabled if it fires it will cause the system to wake
> from suspend as well as cancel any suspend transition that may be in
> progress. If there is an interrupt pending, the ipa_isr_thread handler
> will be called after resuming.
> 
> Fixes: 1aac309d3207 ("net: ipa: use autosuspend")
> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>

FYI, we got some small conflicts when merging -net in net-next in the
MPTCP tree due to this patch applied in -net:

  9ec9b2a30853 ("net: ipa: disable ipa interrupt during suspend")

and these one from net-next:

  8e461e1f092b ("net: ipa: introduce ipa_interrupt_enable()")
  d50ed3558719 ("net: ipa: enable IPA interrupt handlers separate from
registration")

These conflicts have been resolved on our side[1] and the resolution we
suggest is attached to this email: I simply took both part (new
functions) and changed the order to have the functions from -net first.

Cheers,
Matt

[1] https://github.com/multipath-tcp/mptcp_net-next/commit/00697360079c
diff mbox series

Patch

diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c
index d458a35839cc..c1b3977e1ae4 100644
--- a/drivers/net/ipa/ipa_interrupt.c
+++ b/drivers/net/ipa/ipa_interrupt.c
@@ -127,6 +127,16 @@  static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+void ipa_interrupt_irq_disable(struct ipa *ipa)
+{
+	disable_irq(ipa->interrupt->irq);
+}
+
+void ipa_interrupt_irq_enable(struct ipa *ipa)
+{
+	enable_irq(ipa->interrupt->irq);
+}
+
 /* Common function used to enable/disable TX_SUSPEND for an endpoint */
 static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
 					  u32 endpoint_id, bool enable)
diff --git a/drivers/net/ipa/ipa_interrupt.h b/drivers/net/ipa/ipa_interrupt.h
index f31fd9965fdc..8a1bd5b89393 100644
--- a/drivers/net/ipa/ipa_interrupt.h
+++ b/drivers/net/ipa/ipa_interrupt.h
@@ -85,6 +85,22 @@  void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt);
  */
 void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt);
 
+/**
+ * ipa_interrupt_irq_enable() - Enable IPA interrupts
+ * @ipa:	IPA pointer
+ *
+ * This enables the IPA interrupt line
+ */
+void ipa_interrupt_irq_enable(struct ipa *ipa);
+
+/**
+ * ipa_interrupt_irq_disable() - Disable IPA interrupts
+ * @ipa:	IPA pointer
+ *
+ * This disables the IPA interrupt line
+ */
+void ipa_interrupt_irq_disable(struct ipa *ipa);
+
 /**
  * ipa_interrupt_config() - Configure the IPA interrupt framework
  * @ipa:	IPA pointer
diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c
index 8420f93128a2..8057be8cda80 100644
--- a/drivers/net/ipa/ipa_power.c
+++ b/drivers/net/ipa/ipa_power.c
@@ -181,6 +181,17 @@  static int ipa_suspend(struct device *dev)
 
 	__set_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
 
+	/* Increment the disable depth to ensure that the IRQ won't
+	 * be re-enabled until the matching _enable call in
+	 * ipa_resume(). We do this to ensure that the interrupt
+	 * handler won't run whilst PM runtime is disabled.
+	 *
+	 * Note that disabling the IRQ is NOT the same as disabling
+	 * irq wake. If wakeup is enabled for the IPA then the IRQ
+	 * will still cause the system to wake up, see irq_set_irq_wake().
+	 */
+	ipa_interrupt_irq_disable(ipa);
+
 	return pm_runtime_force_suspend(dev);
 }
 
@@ -193,6 +204,12 @@  static int ipa_resume(struct device *dev)
 
 	__clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
 
+	/* Now that PM runtime is enabled again it's safe
+	 * to turn the IRQ back on and process any data
+	 * that was received during suspend.
+	 */
+	ipa_interrupt_irq_enable(ipa);
+
 	return ret;
 }