Linux SCSI subsystem development
 help / color / mirror / Atom feed
* [RFC PATCH] scsi: mvsas: add suspend/resume support
@ 2026-09-05 22:12 FVMA
  2026-09-07  7:12 ` John Garry
  0 siblings, 1 reply; 2+ messages in thread
From: FVMA @ 2026-09-05 22:12 UTC (permalink / raw)
  To: linux-scsi@vger.kernel.org
  Cc: James.Bottomley@HansenPartnership.com, martin.petersen@oracle.com


[-- Attachment #1.1: Type: text/plain, Size: 4199 bytes --]

The driver has no PM callbacks. After a system suspend the controller returns with its registers at reset values while the driver state assumes a configured chip, so the first command after resume times out and libsas error handling wedges the host.

Add dev_pm_ops. On suspend, quiesce libsas with sas_suspend_ha(), mask controller interrupts, drain the IRQ and the tasklet, and cancel pending deferred phy events. On resume, reset the ring indices and the STP register set bookkeeping that chip_init() reinitialises in hardware, run chip_init(), re-enable interrupts, report the attached phys through mvs_scan_start() and hand the host back to libsas with sas_resume_ha().

Signed-off-by: Francois <fvma@protonmail.com>
Assisted-by: Claude:claude-fable-5-1
---
Compile-tested against v7.1.13 and current mainline; not yet exercised
on hardware (88SE9485). The ring index and register set reset could be
moved into chip_init() if preferred.

drivers/scsi/mvsas/mv_init.c | 104 +++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)

diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 5abc17a..d450e1d 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -669,11 +669,115 @@ static const struct pci_device_id mvs_pci_table[] = {
{ } /* terminate list */
};

+/*
+ * The controller returns from suspend at reset values. Resume reruns the
+ * probe-time chip initialisation and hands the host back to libsas.
+ */
+static void mvs_flush_events(struct mvs_info *mvi)
+{
+ struct mvs_wq *mwq, *tmp;
+ unsigned long flags;
+ int i;
+ LIST_HEAD(pending);
+
+ /*
+ * Pending events are cancelled and freed here. A running event holds
+ * mvi->lock and frees itself on completion.
+ */
+ spin_lock_irqsave(&mvi->lock, flags);
+ list_for_each_entry_safe(mwq, tmp, &mvi->wq_list, entry)
+ if (cancel_delayed_work(&mwq->work_q))
+ list_move_tail(&mwq->entry, &pending);
+ spin_unlock_irqrestore(&mvi->lock, flags);
+
+ list_for_each_entry_safe(mwq, tmp, &pending, entry) {
+ list_del(&mwq->entry);
+ kfree(mwq);
+ }
+
+ for (i = 0; i < 50 && !list_empty(&mvi->wq_list); i++)
+ msleep(20);
+}
+
+static int mvs_pci_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct mvs_prv_info *mpi = sha->lldd_ha;
+ struct mvs_info *mvi;
+ int i;
+
+ sas_suspend_ha(sha);
+
+ for (i = 0; i < mpi->n_host; i++) {
+ mvi = mpi->mvi[i];
+ MVS_CHIP_DISP->interrupt_disable(mvi);
+ }
+ synchronize_irq(pdev->irq);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+ /* the tasklet re-enables interrupts on completion */
+ tasklet_kill(&mpi->mv_tasklet);
+ for (i = 0; i < mpi->n_host; i++) {
+ mvi = mpi->mvi[i];
+ MVS_CHIP_DISP->interrupt_disable(mvi);
+ }
+#endif
+ for (i = 0; i < mpi->n_host; i++)
+ mvs_flush_events(mpi->mvi[i]);
+
+ dev_info(dev, "suspended\n");
+ return 0;
+}
+
+static int mvs_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct mvs_prv_info *mpi = sha->lldd_ha;
+ struct mvs_info *mvi;
+ int i, j, rc;
+
+ sas_prep_resume_ha(sha);
+
+ for (i = 0; i < mpi->n_host; i++) {
+ mvi = mpi->mvi[i];
+ /* chip_init() resets the rings and the STP register sets */
+ mvi->tx_prod = 0;
+ mvi->rx_cons = 0xfff;
+ mvi->rx[0] = cpu_to_le32(0xfff);
+ mvi->sata_reg_set = 0;
+ for (j = 0; j < MVS_MAX_DEVICES; j++)
+ mvi->devices[j].taskfileset = MVS_ID_NOT_MAPPED;
+
+ rc = MVS_CHIP_DISP->chip_init(mvi);
+ if (rc) {
+ dev_err(dev, "chip_init failed on core %d: %d\n", i, rc);
+ return rc;
+ }
+ }
+
+ for (i = 0; i < mpi->n_host; i++) {
+ mvi = mpi->mvi[i];
+ MVS_CHIP_DISP->interrupt_enable(mvi);
+ }
+
+ /* report attached phys as at probe */
+ mvs_scan_start(sha->shost);
+ sas_resume_ha(sha);
+
+ dev_info(dev, "resumed\n");
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mvs_pci_pm_ops, mvs_pci_suspend,
+ mvs_pci_resume);
+
static struct pci_driver mvs_pci_driver = {
.name = DRV_NAME,
.id_table = mvs_pci_table,
.probe = mvs_pci_init,
.remove = mvs_pci_remove,
+ .driver.pm = pm_sleep_ptr(&mvs_pci_pm_ops),
};

static DEVICE_STRING_ATTR_RO(driver_version, 0444, DRV_VERSION);
--
2.55.0

[-- Attachment #1.2: Type: text/html, Size: 10372 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-RFC-scsi-mvsas-add-suspend-resume-support.patch --]
[-- Type: text/x-patch; name=0001-RFC-scsi-mvsas-add-suspend-resume-support.patch, Size: 4465 bytes --]

From 78b68fa74a13e37b007684ac8941bdd2c2c77338 Mon Sep 17 00:00:00 2001
From: Francois <fvma@protonmail.com>
Date: Sun, 6 Sep 2026 00:03:43 +0200
Subject: [RFC PATCH] scsi: mvsas: add suspend/resume support

The driver has no PM callbacks. After a system suspend the controller
returns with its registers at reset values while the driver state
assumes a configured chip, so the first command after resume times out
and libsas error handling wedges the host.

Add dev_pm_ops. On suspend, quiesce libsas with sas_suspend_ha(), mask
controller interrupts, drain the IRQ and the tasklet, and cancel pending
deferred phy events. On resume, reset the ring indices and the STP
register set bookkeeping that chip_init() reinitialises in hardware, run
chip_init(), re-enable interrupts, report the attached phys through
mvs_scan_start() and hand the host back to libsas with sas_resume_ha().

Signed-off-by: Francois <fvma@protonmail.com>
Assisted-by: Claude:claude-fable-5-1
---
Compile-tested against v7.1.13 and current mainline; not yet exercised
on hardware (88SE9485). The ring index and register set reset could be
moved into chip_init() if preferred.

 drivers/scsi/mvsas/mv_init.c | 104 +++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 5abc17a..d450e1d 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -669,11 +669,115 @@ static const struct pci_device_id mvs_pci_table[] = {
 	{ }	/* terminate list */
 };
 
+/*
+ * The controller returns from suspend at reset values. Resume reruns the
+ * probe-time chip initialisation and hands the host back to libsas.
+ */
+static void mvs_flush_events(struct mvs_info *mvi)
+{
+	struct mvs_wq *mwq, *tmp;
+	unsigned long flags;
+	int i;
+	LIST_HEAD(pending);
+
+	/*
+	 * Pending events are cancelled and freed here. A running event holds
+	 * mvi->lock and frees itself on completion.
+	 */
+	spin_lock_irqsave(&mvi->lock, flags);
+	list_for_each_entry_safe(mwq, tmp, &mvi->wq_list, entry)
+		if (cancel_delayed_work(&mwq->work_q))
+			list_move_tail(&mwq->entry, &pending);
+	spin_unlock_irqrestore(&mvi->lock, flags);
+
+	list_for_each_entry_safe(mwq, tmp, &pending, entry) {
+		list_del(&mwq->entry);
+		kfree(mwq);
+	}
+
+	for (i = 0; i < 50 && !list_empty(&mvi->wq_list); i++)
+		msleep(20);
+}
+
+static int mvs_pci_suspend(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+	struct mvs_prv_info *mpi = sha->lldd_ha;
+	struct mvs_info *mvi;
+	int i;
+
+	sas_suspend_ha(sha);
+
+	for (i = 0; i < mpi->n_host; i++) {
+		mvi = mpi->mvi[i];
+		MVS_CHIP_DISP->interrupt_disable(mvi);
+	}
+	synchronize_irq(pdev->irq);
+#ifdef CONFIG_SCSI_MVSAS_TASKLET
+	/* the tasklet re-enables interrupts on completion */
+	tasklet_kill(&mpi->mv_tasklet);
+	for (i = 0; i < mpi->n_host; i++) {
+		mvi = mpi->mvi[i];
+		MVS_CHIP_DISP->interrupt_disable(mvi);
+	}
+#endif
+	for (i = 0; i < mpi->n_host; i++)
+		mvs_flush_events(mpi->mvi[i]);
+
+	dev_info(dev, "suspended\n");
+	return 0;
+}
+
+static int mvs_pci_resume(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+	struct mvs_prv_info *mpi = sha->lldd_ha;
+	struct mvs_info *mvi;
+	int i, j, rc;
+
+	sas_prep_resume_ha(sha);
+
+	for (i = 0; i < mpi->n_host; i++) {
+		mvi = mpi->mvi[i];
+		/* chip_init() resets the rings and the STP register sets */
+		mvi->tx_prod = 0;
+		mvi->rx_cons = 0xfff;
+		mvi->rx[0] = cpu_to_le32(0xfff);
+		mvi->sata_reg_set = 0;
+		for (j = 0; j < MVS_MAX_DEVICES; j++)
+			mvi->devices[j].taskfileset = MVS_ID_NOT_MAPPED;
+
+		rc = MVS_CHIP_DISP->chip_init(mvi);
+		if (rc) {
+			dev_err(dev, "chip_init failed on core %d: %d\n", i, rc);
+			return rc;
+		}
+	}
+
+	for (i = 0; i < mpi->n_host; i++) {
+		mvi = mpi->mvi[i];
+		MVS_CHIP_DISP->interrupt_enable(mvi);
+	}
+
+	/* report attached phys as at probe */
+	mvs_scan_start(sha->shost);
+	sas_resume_ha(sha);
+
+	dev_info(dev, "resumed\n");
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mvs_pci_pm_ops, mvs_pci_suspend,
+				mvs_pci_resume);
+
 static struct pci_driver mvs_pci_driver = {
 	.name		= DRV_NAME,
 	.id_table	= mvs_pci_table,
 	.probe		= mvs_pci_init,
 	.remove		= mvs_pci_remove,
+	.driver.pm	= pm_sleep_ptr(&mvs_pci_pm_ops),
 };
 
 static DEVICE_STRING_ATTR_RO(driver_version, 0444, DRV_VERSION);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [RFC PATCH] scsi: mvsas: add suspend/resume support
  2026-09-05 22:12 [RFC PATCH] scsi: mvsas: add suspend/resume support FVMA
@ 2026-09-07  7:12 ` John Garry
  0 siblings, 0 replies; 2+ messages in thread
From: John Garry @ 2026-09-07  7:12 UTC (permalink / raw)
  To: FVMA, linux-scsi@vger.kernel.org
  Cc: James.Bottomley@HansenPartnership.com, martin.petersen@oracle.com

On 9/5/26 23:12, FVMA wrote:
> The driver has no PM callbacks. After a system suspend the controller 
> returns with its registers at reset values while the driver state 
> assumes a configured chip, so the first command after resume times out 
> and libsas error handling wedges the host.
> 
> Add dev_pm_ops. On suspend, quiesce libsas with sas_suspend_ha(), mask 
> controller interrupts, drain the IRQ and the tasklet, and cancel pending 
> deferred phy events. On resume, reset the ring indices and the STP 
> register set bookkeeping that chip_init() reinitialises in hardware, run 
> chip_init(), re-enable interrupts, report the attached phys through 
> mvs_scan_start() and hand the host back to libsas with sas_resume_ha().
> 
> Signed-off-by: Francois <fvma@protonmail.com <mailto:fvma@protonmail.com>>
> Assisted-by: Claude:claude-fable-5-1
> ---
> Compile-tested against v7.1.13 and current mainline; not yet exercised
> on hardware (88SE9485). 

What is your motivation in posting this change?

You don't even have access to the relevant HW.


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-07  7:13 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-05 22:12 [RFC PATCH] scsi: mvsas: add suspend/resume support FVMA
2026-09-07  7:12 ` John Garry

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox