[PATCH] scsi: lpfc: shut down CPU hotplug poll timer during removal
From: Runyu Xiao
Date: Wed Sep 30 2026 - 05:56:26 EST
The CPU hotplug removal path waits for the poll timer to retire with
timer_delete_sync(). The poll callback can rearm the timer after the
deletion, so the timer can continue to reference the HBA after the
hotplug instance has been removed.
Use timer_shutdown_sync() only for the final HBA teardown. lpfc_offline()
also removes the CPU hotplug instance during recoverable offline/online
cycles, so it must leave the timer reusable with timer_delete_sync().
When the HBA is finally removed after such an offline transition, shut
down the timer even though the CPU hotplug instance was already removed.
Fixes: 93a4d6f40198 ("scsi: lpfc: Add registration for CPU Offline/Online events")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
drivers/scsi/lpfc/lpfc_init.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 23355f12fb..1cdd484ad8 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -70,7 +70,7 @@ static enum cpuhp_state lpfc_cpuhp_state;
static uint32_t lpfc_present_cpu;
static bool lpfc_pldv_detect;
-static void __lpfc_cpuhp_remove(struct lpfc_hba *phba);
+static void __lpfc_cpuhp_remove(struct lpfc_hba *phba, bool shutdown);
static void lpfc_cpuhp_remove(struct lpfc_hba *phba);
static void lpfc_cpuhp_add(struct lpfc_hba *phba);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
@@ -3936,7 +3936,7 @@ lpfc_offline(struct lpfc_hba *phba)
* in hba_unset
*/
if (test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
- __lpfc_cpuhp_remove(phba);
+ __lpfc_cpuhp_remove(phba, false);
if (phba->cfg_xri_rebalancing)
lpfc_destroy_multixri_pools(phba);
@@ -12745,7 +12745,7 @@ lpfc_cpuhp_get_eq(struct lpfc_hba *phba, unsigned int cpu,
return 0;
}
-static void __lpfc_cpuhp_remove(struct lpfc_hba *phba)
+static void __lpfc_cpuhp_remove(struct lpfc_hba *phba, bool shutdown)
{
if (phba->sli_rev != LPFC_SLI_REV4)
return;
@@ -12757,16 +12757,24 @@ static void __lpfc_cpuhp_remove(struct lpfc_hba *phba)
* timer. Wait for the poll timer to retire.
*/
synchronize_rcu();
- timer_delete_sync(&phba->cpuhp_poll_timer);
+ if (shutdown)
+ timer_shutdown_sync(&phba->cpuhp_poll_timer);
+ else
+ timer_delete_sync(&phba->cpuhp_poll_timer);
}
static void lpfc_cpuhp_remove(struct lpfc_hba *phba)
{
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return;
+
if (phba->pport &&
- test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag))
+ test_bit(FC_OFFLINE_MODE, &phba->pport->fc_flag)) {
+ timer_shutdown_sync(&phba->cpuhp_poll_timer);
return;
+ }
- __lpfc_cpuhp_remove(phba);
+ __lpfc_cpuhp_remove(phba, true);
}
static void lpfc_cpuhp_add(struct lpfc_hba *phba)