2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler

On systems with a large number request slots and unavailable MCQ ESI,
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 wake
up the threaded interrupt routine, the interrupt line would be masked
until the processing is finished in the thread thanks to the
IRQS_ONESHOT flag.

When MCQ & ESI interrupts are enabled the I/O completions are now
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)

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250407-topic-ufs-use-threaded-irq-v3-3-08bee980f71e@linaro.org
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Neil Armstrong 2025-04-07 12:17:05 +02:00 committed by Martin K. Petersen
parent 2d6c7bcc6c
commit 3c7ac40d73

View File

@ -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 * @irq: irq number
* @__hba: pointer to adapter instance * @__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_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt * 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; u32 last_intr_status, intr_status, enabled_intr_status = 0;
irqreturn_t retval = IRQ_NONE; irqreturn_t retval = IRQ_NONE;
@ -7018,6 +7018,29 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
return retval; 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 & ESI are not enabled */
if (!hba->mcq_enabled || !hba->mcq_esi_enabled)
return IRQ_WAKE_THREAD;
/* Directly handle interrupts since MCQ ESI 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) static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
{ {
int err = 0; int err = 0;
@ -10577,7 +10600,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_readl(hba, REG_INTERRUPT_ENABLE); ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
/* IRQ registration */ /* 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) { if (err) {
dev_err(hba->dev, "request irq failed\n"); dev_err(hba->dev, "request irq failed\n");
goto out_disable; goto out_disable;