diff mbox series

[RFC,v2,2/2] ufs: core: delegate the interrupt service routine to a threaded irq handler

Message ID 20250326-topic-ufs-use-threaded-irq-v2-2-7b3e8a5037e6@linaro.org (mailing list archive)
State Handled Elsewhere
Headers show
Series ufs: core: cleanup and threaded irq handler | expand

Commit Message

Neil Armstrong March 26, 2025, 8:36 a.m. UTC
On systems with a large number request slots and unavailable MCQ,
the current design of the interrupt handler can delay handling of
other subsystems interrupts causing display artifacts, GPU stalls
or system firmware requests timeouts.

Since the interrupt routine can take quite some time, it's
preferable to move it to a threaded handler and leave the
hard interrupt handler save the status and disable the irq
until processing is finished in the thread.

When MCQ & Interrupt Aggregation are supported, the interrupt
are directly handled in the "hard" interrupt routine to
keep IOPs high since queues handling is done in separate
per-queue interrupt routines.

This fixes all encountered issued when running FIO tests
on the Qualcomm SM8650 platform.

Example of errors reported on a loaded system:
 [drm:dpu_encoder_frame_done_timeout:2706] [dpu error]enc32 frame done timeout
 msm_dpu ae01000.display-controller: [drm:hangcheck_handler [msm]] *ERROR* 67.5.20.1: hangcheck detected gpu lockup rb 2!
 msm_dpu ae01000.display-controller: [drm:hangcheck_handler [msm]] *ERROR* 67.5.20.1:     completed fence: 74285
 msm_dpu ae01000.display-controller: [drm:hangcheck_handler [msm]] *ERROR* 67.5.20.1:     submitted fence: 74286
 Error sending AMC RPMH requests (-110)

Reported bandwidth is not affected on various tests.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/ufs/core/ufshcd.c | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

Comments

Bart Van Assche March 27, 2025, 11:56 a.m. UTC | #1
On 3/26/25 4:36 AM, Neil Armstrong wrote:
 > When MCQ & Interrupt Aggregation are supported, the interrupt
 > are directly handled in the "hard" interrupt routine to
 > keep IOPs high since queues handling is done in separate
 > per-queue interrupt routines.

The above explanation suggests that I/O completions are handled by the
modified interrupt handler. This is not necessarily the case. With MCQ,
I/O completions are either handled by dedicated interrupts or by the
legacy interrupt handler.

> Reported bandwidth is not affected on various tests.

This kind of patch can only affect command completion latency but not
the bandwidth, isn't it?

> +/**
> + * ufshcd_intr - Main interrupt service routine
> + * @irq: irq number
> + * @__hba: pointer to adapter instance
> + *
> + * Return:
> + *  IRQ_HANDLED     - If interrupt is valid
> + *  IRQ_WAKE_THREAD - If handling is moved to threaded handled
> + *  IRQ_NONE        - If invalid interrupt
> + */
> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
> +{
> +	struct ufs_hba *hba = __hba;
> +
> +	/*
> +	 * Move interrupt handling to thread when MCQ is not supported
> +	 * or when Interrupt Aggregation is not supported, leading to
> +	 * potentially longer interrupt handling.
> +	 */
> +	if (!is_mcq_supported(hba) || !ufshcd_is_intr_aggr_allowed(hba))
> +		return IRQ_WAKE_THREAD;
> +
> +	/* Directly handle interrupts since MCQ handlers does the hard job */
> +	return ufshcd_sl_intr(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS) &
> +				   ufshcd_readl(hba, REG_INTERRUPT_ENABLE));
> +}

Where has ufshcd_is_intr_aggr_allowed() been defined? I can't find this
function.

For the MCQ case, this patch removes the loop from around
ufshcd_sl_intr() without explaining in the patch description why this 
change has been made. Please explain all changes in the patch
description.

Thanks,

Bart.
Neil Armstrong March 27, 2025, 12:47 p.m. UTC | #2
Hi,

On 27/03/2025 12:56, Bart Van Assche wrote:
> On 3/26/25 4:36 AM, Neil Armstrong wrote:
>  > When MCQ & Interrupt Aggregation are supported, the interrupt
>  > are directly handled in the "hard" interrupt routine to
>  > keep IOPs high since queues handling is done in separate
>  > per-queue interrupt routines.
> 
> The above explanation suggests that I/O completions are handled by the
> modified interrupt handler. This is not necessarily the case. With MCQ,
> I/O completions are either handled by dedicated interrupts or by the
> legacy interrupt handler.

Will update the sentence with that

> 
>> Reported bandwidth is not affected on various tests.
> 
> This kind of patch can only affect command completion latency but not
> the bandwidth, isn't it?

Yes, but on a fully loaded system, it will enhance bandwidth
but with a greater latency, but without eating irq handling time
for other routines.

> 
>> +/**
>> + * ufshcd_intr - Main interrupt service routine
>> + * @irq: irq number
>> + * @__hba: pointer to adapter instance
>> + *
>> + * Return:
>> + *  IRQ_HANDLED     - If interrupt is valid
>> + *  IRQ_WAKE_THREAD - If handling is moved to threaded handled
>> + *  IRQ_NONE        - If invalid interrupt
>> + */
>> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
>> +{
>> +    struct ufs_hba *hba = __hba;
>> +
>> +    /*
>> +     * Move interrupt handling to thread when MCQ is not supported
>> +     * or when Interrupt Aggregation is not supported, leading to
>> +     * potentially longer interrupt handling.
>> +     */
>> +    if (!is_mcq_supported(hba) || !ufshcd_is_intr_aggr_allowed(hba))
>> +        return IRQ_WAKE_THREAD;
>> +
>> +    /* Directly handle interrupts since MCQ handlers does the hard job */
>> +    return ufshcd_sl_intr(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS) &
>> +                   ufshcd_readl(hba, REG_INTERRUPT_ENABLE));
>> +}
> 
> Where has ufshcd_is_intr_aggr_allowed() been defined? I can't find this
> function.

It's in include/ufs/ufshcd.h

> 
> For the MCQ case, this patch removes the loop from around
> ufshcd_sl_intr() without explaining in the patch description why this change has been made. Please explain all changes in the patch
> description.

Ack will update explaining this change.

Thanks,
Neil

> 
> Thanks,
> 
> Bart.
Bart Van Assche March 28, 2025, 5:14 p.m. UTC | #3
On 3/26/25 1:36 AM, Neil Armstrong wrote:
> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
> +{
> +	struct ufs_hba *hba = __hba;
> +
> +	/*
> +	 * Move interrupt handling to thread when MCQ is not supported
> +	 * or when Interrupt Aggregation is not supported, leading to
> +	 * potentially longer interrupt handling.
> +	 */
> +	if (!is_mcq_supported(hba) || !ufshcd_is_intr_aggr_allowed(hba))
> +		return IRQ_WAKE_THREAD;
> +
> +	/* Directly handle interrupts since MCQ handlers does the hard job */
> +	return ufshcd_sl_intr(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS) &
> +				   ufshcd_readl(hba, REG_INTERRUPT_ENABLE));
> +}

Calling ufshcd_is_intr_aggr_allowed() from the above interrupt handler
seems wrong to me. I think you want to check whether or not ESI has been
disabled since only if ESI is disabled all I/O completions are handled
by a single interrupt if MCQ is enabled.

Thanks,

Bart.
diff mbox series

Patch

diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 5e73ac1e00788f3d599f0b3eb6e2806df9b6f6c3..5de25fc978dd7c4c1ac3b9ccbca2ab3f13d6aa65 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -6971,7 +6971,7 @@  static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 }
 
 /**
- * ufshcd_intr - Main interrupt service routine
+ * ufshcd_threaded_intr - Threaded interrupt service routine
  * @irq: irq number
  * @__hba: pointer to adapter instance
  *
@@ -6979,7 +6979,7 @@  static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
  *  IRQ_HANDLED - If interrupt is valid
  *  IRQ_NONE    - If invalid interrupt
  */
-static irqreturn_t ufshcd_intr(int irq, void *__hba)
+static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
 {
 	u32 last_intr_status, intr_status, enabled_intr_status = 0;
 	irqreturn_t retval = IRQ_NONE;
@@ -7018,6 +7018,33 @@  static irqreturn_t ufshcd_intr(int irq, void *__hba)
 	return retval;
 }
 
+/**
+ * ufshcd_intr - Main interrupt service routine
+ * @irq: irq number
+ * @__hba: pointer to adapter instance
+ *
+ * Return:
+ *  IRQ_HANDLED     - If interrupt is valid
+ *  IRQ_WAKE_THREAD - If handling is moved to threaded handled
+ *  IRQ_NONE        - If invalid interrupt
+ */
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
+{
+	struct ufs_hba *hba = __hba;
+
+	/*
+	 * Move interrupt handling to thread when MCQ is not supported
+	 * or when Interrupt Aggregation is not supported, leading to
+	 * potentially longer interrupt handling.
+	 */
+	if (!is_mcq_supported(hba) || !ufshcd_is_intr_aggr_allowed(hba))
+		return IRQ_WAKE_THREAD;
+
+	/* Directly handle interrupts since MCQ handlers does the hard job */
+	return ufshcd_sl_intr(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS) &
+				   ufshcd_readl(hba, REG_INTERRUPT_ENABLE));
+}
+
 static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
 {
 	int err = 0;
@@ -10576,7 +10603,8 @@  int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 	ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
 
 	/* IRQ registration */
-	err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+	err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr,
+					IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba);
 	if (err) {
 		dev_err(hba->dev, "request irq failed\n");
 		goto out_disable;