* [PATCH v3 0/2] scsi: mpt3sas: Fix delayed not-responding sdev block handling
@ 2026-11-06 20:29 Jeff Layton
2026-11-06 20:29 ` [PATCH v3 1/2] scsi: mpt3sas: Serialize interrupt-time sdev block with hostdata teardown Jeff Layton
2026-11-06 20:29 ` [PATCH v3 2/2] scsi: mpt3sas: Time out delayed not-responding device blocks Jeff Layton
0 siblings, 2 replies; 3+ messages in thread
From: Jeff Layton @ 2026-11-06 20:29 UTC (permalink / raw)
To: Sathya Prakash, Sreekanth Reddy, Suganath Prabu Subramani,
Ranjan Kumar, James E.J. Bottomley, Martin K. Petersen,
Nagalakshmi Nandigama
Cc: Keith Busch, MPT-FusionLinux.pdl, linux-scsi, linux-kernel, Jeff Layton
v3 fixes a yet more issues that Sashiko pointed out.
This fix is for a deadlock that we see regularly in Meta's fleet. The
idea is to break the deadlock by unblocking sdevs after a timeout.
I don't do a lot of SCSI driver work, but this one fell in my lap. I
leaned heavily on agentic help for this series.
If this looks good, it'd be nice to see this in linux-next soon.
Thanks,
Jeff
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Changes in v3:
- Patch 1: publish sdev->hostdata under ioc->block_lock in
scsih_sdev_init(), so the interrupt-time block can't see it before
its sas_target store
- Clear block_deadline on all affected sdevs, under ioc->block_lock,
before the fw event worker starts TUR polling, not per-sdev mid-poll
- Take ioc->block_lock in _scsih_block_io_all_device() and clear
block_deadline on already-blocked sdevs, so dnr_unblock_work can't
unblock them during broadcast primitive TM
- Defer dnr_unblock_work while scsi_host_in_recovery(), and use
mod_delayed_work() for the retry
- Link to v2: https://lore.kernel.org/r/20261102-sas-abba-v2-0-b3b9ba874b5a@kernel.org
Changes in v2:
- New patch 1: serialize the interrupt-time sdev block with hostdata
teardown in scsih_sdev_destroy() via new ioc->block_lock (fixes a
pre-existing UAF)
- Do the timed unblock under ioc->block_lock, so it can't race with an
interrupt-time block
- Clear block_deadline before the fw event worker polls a returning
device with TURs, so dnr_unblock_work can't unblock it mid-poll
- Link to v1: https://lore.kernel.org/r/20261030-sas-abba-v1-1-b8cdd714a37c@kernel.org
---
Jeff Layton (2):
scsi: mpt3sas: Serialize interrupt-time sdev block with hostdata teardown
scsi: mpt3sas: Time out delayed not-responding device blocks
drivers/scsi/mpt3sas/mpt3sas_base.c | 13 ++
drivers/scsi/mpt3sas/mpt3sas_base.h | 10 ++
drivers/scsi/mpt3sas/mpt3sas_scsih.c | 245 ++++++++++++++++++++++++++++++++---
3 files changed, 247 insertions(+), 21 deletions(-)
---
base-commit: 8b7179210b22f8e48a82c5ceacb9d56a9c4cfec4
change-id: 20261030-sas-abba-eacfcc45fe36
Best regards,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v3 1/2] scsi: mpt3sas: Serialize interrupt-time sdev block with hostdata teardown
2026-11-06 20:29 [PATCH v3 0/2] scsi: mpt3sas: Fix delayed not-responding sdev block handling Jeff Layton
@ 2026-11-06 20:29 ` Jeff Layton
2026-11-06 20:29 ` [PATCH v3 2/2] scsi: mpt3sas: Time out delayed not-responding device blocks Jeff Layton
1 sibling, 0 replies; 3+ messages in thread
From: Jeff Layton @ 2026-11-06 20:29 UTC (permalink / raw)
To: Sathya Prakash, Sreekanth Reddy, Suganath Prabu Subramani,
Ranjan Kumar, James E.J. Bottomley, Martin K. Petersen,
Nagalakshmi Nandigama
Cc: Keith Busch, MPT-FusionLinux.pdl, linux-scsi, linux-kernel, Jeff Layton
On DELAY_NOT_RESPONDING, the event callback runs _scsih_block_io_device()
at interrupt time. It walks the sdevs with shost_for_each_device() and
uses sdev->hostdata and its sas_target, with no lock held.
The sdev reference only pins the scsi_device. __scsi_remove_device() can
still call ->sdev_destroy() concurrently, e.g. when a scan fails to
probe a new LUN, and scsih_sdev_destroy() frees hostdata, leading to a
use-after-free. The target's hostdata is freed in scsi_target_reap(),
after sdev_destroy.
Add ioc->block_lock. Take it in _scsih_block_io_device() for each sdev,
and around setting and clearing sdev->hostdata in scsih_sdev_init() and
scsih_sdev_destroy(). The sdev is on shost->__devices before sdev_init,
so publishing under the lock also orders the sas_target store. While
hostdata is non-NULL under the lock, it and its target are live.
Fixes: f92363d12359 ("[SCSI] mpt3sas: add new driver supporting 12GB SAS")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
drivers/scsi/mpt3sas/mpt3sas_base.h | 2 ++
drivers/scsi/mpt3sas/mpt3sas_scsih.c | 30 ++++++++++++++++++++++--------
2 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 47255bf9cdda..419fdf164fc6 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -1249,6 +1249,7 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
* @pcie_device_list: pcie device object list
* @pcie_device_init_list: pcie device object list (used only at init time)
* @pcie_device_lock:
+ * @block_lock: serializes sdev blocking against sdev hostdata teardown
* @io_missing_delay: time for IO completed by fw when PDR enabled
* @device_missing_delay: time for device missing by fw when PDR enabled
* @sas_id : used for setting volume target IDs
@@ -1482,6 +1483,7 @@ struct MPT3SAS_ADAPTER {
struct list_head pcie_device_list;
struct list_head pcie_device_init_list;
spinlock_t pcie_device_lock;
+ spinlock_t block_lock;
struct list_head raid_device_list;
spinlock_t raid_device_lock;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index a77bbc3d2b66..60c9cf679a8a 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -2119,12 +2119,17 @@ scsih_sdev_init(struct scsi_device *sdev)
sas_target_priv_data = starget->hostdata;
sas_target_priv_data->num_luns++;
sas_device_priv_data->sas_target = sas_target_priv_data;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+
+ /* _scsih_block_io_device() may already see this sdev */
+ spin_lock_irqsave(&ioc->block_lock, flags);
sdev->hostdata = sas_device_priv_data;
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
sdev->no_uld_attach = 1;
- shost = dev_to_shost(&starget->dev);
- ioc = shost_priv(shost);
if (starget->channel == RAID_CHANNEL) {
spin_lock_irqsave(&ioc->raid_device_lock, flags);
raid_device = _scsih_raid_device_find_by_id(ioc,
@@ -2176,6 +2181,7 @@ scsih_sdev_init(struct scsi_device *sdev)
static void
scsih_sdev_destroy(struct scsi_device *sdev)
{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
struct MPT3SAS_TARGET *sas_target_priv_data;
struct scsi_target *starget;
struct Scsi_Host *shost;
@@ -2218,8 +2224,11 @@ scsih_sdev_destroy(struct scsi_device *sdev)
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
}
- kfree(sdev->hostdata);
+ spin_lock_irqsave(&ioc->block_lock, flags);
+ sas_device_priv_data = sdev->hostdata;
sdev->hostdata = NULL;
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
+ kfree(sas_device_priv_data);
}
/**
@@ -4234,26 +4243,30 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
struct MPT3SAS_DEVICE *sas_device_priv_data;
struct scsi_device *sdev;
struct _sas_device *sas_device;
+ unsigned long flags;
sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
shost_for_each_device(sdev, ioc->shost) {
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data = sdev->hostdata;
if (!sas_device_priv_data)
- continue;
+ goto next;
if (sas_device_priv_data->sas_target->handle != handle)
- continue;
+ goto next;
if (sas_device_priv_data->block)
- continue;
+ goto next;
if (sas_device && sas_device->pend_sas_rphy_add)
- continue;
+ goto next;
if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev,
"%s skip device_block for SES handle(0x%04x)\n",
__func__, sas_device_priv_data->sas_target->handle);
- continue;
+ goto next;
}
_scsih_internal_device_block(sdev, sas_device_priv_data);
+next:
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
}
if (sas_device)
@@ -13539,6 +13552,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
spin_lock_init(&ioc->raid_device_lock);
spin_lock_init(&ioc->pcie_device_lock);
spin_lock_init(&ioc->diag_trigger_lock);
+ spin_lock_init(&ioc->block_lock);
INIT_LIST_HEAD(&ioc->sas_device_list);
INIT_LIST_HEAD(&ioc->sas_device_init_list);
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v3 2/2] scsi: mpt3sas: Time out delayed not-responding device blocks
2026-11-06 20:29 [PATCH v3 0/2] scsi: mpt3sas: Fix delayed not-responding sdev block handling Jeff Layton
2026-11-06 20:29 ` [PATCH v3 1/2] scsi: mpt3sas: Serialize interrupt-time sdev block with hostdata teardown Jeff Layton
@ 2026-11-06 20:29 ` Jeff Layton
1 sibling, 0 replies; 3+ messages in thread
From: Jeff Layton @ 2026-11-06 20:29 UTC (permalink / raw)
To: Sathya Prakash, Sreekanth Reddy, Suganath Prabu Subramani,
Ranjan Kumar, James E.J. Bottomley, Martin K. Petersen,
Nagalakshmi Nandigama
Cc: Keith Busch, MPT-FusionLinux.pdl, linux-scsi, linux-kernel, Jeff Layton
We have an agent that writes "- - -" to the sysfs scan attribute at
regular intervals. Several of these scans ended up deadlocked:
1. A drive in reset failed its initial scan, so the worker started to
remove the drive.
2. A sysfs scan took sas_host->lock, allocated an sdev for the drive
and sent an INQUIRY.
3. A DELAY_NOT_RESPONDING event blocked the new sdev.
4. The worker waited for sas_host->lock in sas_rphy_free():
__mutex_lock
sas_port_delete+0x75 [scsi_transport_sas]
mpt3sas_transport_port_remove
_scsih_add_device
After that, all further scan attempts got stuck waiting on the
sas_host->lock.
On DELAY_NOT_RESPONDING the event callback blocks every sdev on the
handle. The block is released only:
- by the callback, if the firmware later reports TARG_NOT_RESPONDING
- by the firmware event worker, when it processes a later event
Commit e4bc7f5c21a1 ("mpt3sas: Don't block the drive when drive addition
under the control of SML") was a partial fix for a similar issue. Not
blocking sdevs that a scan is still probing is not enough: the scan also
sends REPORT LUNS to LUN 0 once it is visible, and LUN 0 may be blocked
before or during the scan.
Within the report device missing delay, the firmware either reports the
device as not responding, and the callback unblocks it, or the device
has come back. Past that delay the block has no further use.
Schedule delayed_work to the system_dfl_wq for the missing delay plus
30s. When it runs, unblock any expired sdevs. The driver only tracked
the SAS missing delay, so also read the PCIe one from PCIe IO Unit
Page 1, already fetched for the NVMe queue depth, and use the larger.
Take ioc->block_lock around the worker's unblocks, so they can't race
the interrupt-time block and lose its deadline. Don't timed-block
offline sdevs: expiry would bring them back running with no TUR check.
Cc: Keith Busch <kbusch@kernel.org>
Fixes: f92363d12359 ("[SCSI] mpt3sas: add new driver supporting 12GB SAS")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
drivers/scsi/mpt3sas/mpt3sas_base.c | 13 +++
drivers/scsi/mpt3sas/mpt3sas_base.h | 10 +-
drivers/scsi/mpt3sas/mpt3sas_scsih.c | 215 ++++++++++++++++++++++++++++++++---
3 files changed, 224 insertions(+), 14 deletions(-)
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 1af25a22611a..cc2dcbce0491 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -5405,6 +5405,7 @@ _base_update_diag_trigger_pages(struct MPT3SAS_ADAPTER *ioc)
/**
* _base_assign_fw_reported_qd - Get FW reported QD for SAS/SATA devices.
* - On failure set default QD values.
+ * - Also get the PCIe device missing delay.
* @ioc : per adapter object
*
* Returns 0 for success, non-zero for failure.
@@ -5416,6 +5417,7 @@ static int _base_assign_fw_reported_qd(struct MPT3SAS_ADAPTER *ioc)
Mpi2SasIOUnitPage1_t sas_iounit_pg1;
Mpi26PCIeIOUnitPage1_t pcie_iounit_pg1;
u16 depth;
+ u8 dmd;
int rc = 0;
ioc->max_wideport_qd = MPT3SAS_SAS_QUEUE_DEPTH;
@@ -5453,6 +5455,17 @@ static int _base_assign_fw_reported_qd(struct MPT3SAS_ADAPTER *ioc)
ioc->max_nvme_qd = (le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) ?
(le16_to_cpu(pcie_iounit_pg1.NVMeMaxQueueDepth)) :
MPT3SAS_NVME_QUEUE_DEPTH;
+
+ if ((le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK) !=
+ MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ dmd = pcie_iounit_pg1.DMDReportPCIe;
+ if (dmd & MPI26_PCIEIOUNIT1_DMDRPT_UNIT_16_SEC)
+ ioc->pcie_device_missing_delay =
+ (dmd & MPI26_PCIEIOUNIT1_DMDRPT_DELAY_TIME_MASK) * 16;
+ else
+ ioc->pcie_device_missing_delay =
+ dmd & MPI26_PCIEIOUNIT1_DMDRPT_DELAY_TIME_MASK;
out:
dinitprintk(ioc, pr_err(
"MaxWidePortQD: 0x%x MaxNarrowPortQD: 0x%x MaxSataQD: 0x%x MaxNvmeQD: 0x%x\n",
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 419fdf164fc6..11bbefbb9516 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -469,6 +469,8 @@ struct MPT3SAS_TARGET {
* @flags: MPT_DEVICE_XXX flags
* @configured_lun: lun is configured
* @block: device is in SDEV_BLOCK state
+ * @block_deadline: jiffies after which a delayed not-responding block is
+ * released without waiting for the firmware event worker, 0 if none
* @tlr_snoop_check: flag used in determining whether to disable TLR
* @eedp_enable: eedp support enable bit
* @eedp_type: 0(type_1), 1(type_2), 2(type_3)
@@ -486,6 +488,7 @@ struct MPT3SAS_DEVICE {
u8 ignore_delay_remove;
/* Iopriority Command Handling */
u8 ncq_prio_enable;
+ unsigned long block_deadline;
/*
* Bug workaround for SATL handling: the mpt2/3sas firmware
* doesn't return BUSY or TASK_SET_FULL for subsequent
@@ -1165,6 +1168,7 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
* pci resource handling
* @fault_reset_work_q: fw fault workqueue
* @fault_reset_work: fw fault work
+ * @dnr_unblock_work: releases expired delayed not-responding blocks
* @firmware_event_thread: fw event work queue
* @fw_event_lock:
* @fw_event_list: list of fw events
@@ -1249,9 +1253,11 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
* @pcie_device_list: pcie device object list
* @pcie_device_init_list: pcie device object list (used only at init time)
* @pcie_device_lock:
- * @block_lock: serializes sdev blocking against sdev hostdata teardown
+ * @block_lock: serializes sdev blocking and unblocking against
+ * dnr_unblock_work and sdev hostdata teardown
* @io_missing_delay: time for IO completed by fw when PDR enabled
* @device_missing_delay: time for device missing by fw when PDR enabled
+ * @pcie_device_missing_delay: time for PCIe device missing by fw
* @sas_id : used for setting volume target IDs
* @pcie_target_id: used for setting pcie target IDs
* @blocking_handles: bitmask used to identify which devices need blocking
@@ -1351,6 +1357,7 @@ struct MPT3SAS_ADAPTER {
/* fw fault handler */
struct workqueue_struct *fault_reset_work_q;
struct delayed_work fault_reset_work;
+ struct delayed_work dnr_unblock_work;
/* fw event handler */
struct workqueue_struct *firmware_event_thread;
@@ -1489,6 +1496,7 @@ struct MPT3SAS_ADAPTER {
spinlock_t raid_device_lock;
u8 io_missing_delay;
u16 device_missing_delay;
+ u16 pcie_device_missing_delay;
int sas_id;
int pcie_target_id;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 60c9cf679a8a..acdeece20140 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -3887,6 +3887,7 @@ _scsih_internal_device_block(struct scsi_device *sdev,
sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n",
sas_device_priv_data->sas_target->handle);
sas_device_priv_data->block = 1;
+ sas_device_priv_data->block_deadline = 0;
r = scsi_internal_device_block_nowait(sdev);
if (r == -EINVAL)
@@ -3912,6 +3913,7 @@ _scsih_internal_device_unblock(struct scsi_device *sdev,
sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, "
"handle(0x%04x)\n", sas_device_priv_data->sas_target->handle);
sas_device_priv_data->block = 0;
+ sas_device_priv_data->block_deadline = 0;
r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
if (r == -EINVAL) {
/* The device has been set to SDEV_RUNNING by SD layer during
@@ -3939,6 +3941,35 @@ _scsih_internal_device_unblock(struct scsi_device *sdev,
}
}
+/**
+ * _scsih_claim_block - keep dnr_unblock_work off a blocked sdev
+ * @ioc: per adapter object
+ * @sdev: scsi device struct
+ *
+ * Called by the firmware event worker before it polls a device with TURs.
+ * The worker unblocks the device when polling ends, and must not have
+ * dnr_unblock_work send I/O to it in the meantime.
+ *
+ * Return: true if @sdev is blocked, false if it was never blocked or its
+ * block has already expired.
+ */
+static bool
+_scsih_claim_block(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ unsigned long flags;
+ bool blocked = false;
+
+ spin_lock_irqsave(&ioc->block_lock, flags);
+ sas_device_priv_data = sdev->hostdata;
+ if (sas_device_priv_data && sas_device_priv_data->block) {
+ sas_device_priv_data->block_deadline = 0;
+ blocked = true;
+ }
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
+ return blocked;
+}
+
/**
* _scsih_ublock_io_all_device - unblock every device
* @ioc: per adapter object
@@ -3958,7 +3989,20 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc, u8 no_turs)
int count = 0;
u8 tr_method = 0;
u8 tr_timeout = 30;
+ unsigned long flags;
+ /* polling one device can outlast another's deadline */
+ if (!no_turs && issue_scsi_cmd_to_bringup_drive) {
+ shost_for_each_device(sdev, ioc->shost) {
+ spin_lock_irqsave(&ioc->block_lock, flags);
+ sas_device_priv_data = sdev->hostdata;
+ if (sas_device_priv_data &&
+ sas_device_priv_data->sas_target &&
+ !sas_device_priv_data->sas_target->deleted)
+ sas_device_priv_data->block_deadline = 0;
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
+ }
+ }
shost_for_each_device(sdev, ioc->shost) {
sas_device_priv_data = sdev->hostdata;
@@ -3975,10 +4019,18 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc, u8 no_turs)
if ((no_turs) || (!issue_scsi_cmd_to_bringup_drive)) {
sdev_printk(KERN_WARNING, sdev, "device_unblocked handle(0x%04x)\n",
sas_device_priv_data->sas_target->handle);
- _scsih_internal_device_unblock(sdev, sas_device_priv_data);
+ spin_lock_irqsave(&ioc->block_lock, flags);
+ /* dnr_unblock_work may have released it */
+ if (sas_device_priv_data->block)
+ _scsih_internal_device_unblock(sdev,
+ sas_device_priv_data);
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
continue;
}
+ /* may have been blocked since the first pass */
+ if (!_scsih_claim_block(ioc, sdev))
+ continue;
do {
pcie_device = mpt3sas_get_pdev_by_handle(ioc, sas_target->handle);
if (pcie_device && (!ioc->tm_custom_handling) &&
@@ -3997,11 +4049,13 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc, u8 no_turs)
} while ((rc == DEVICE_RETRY || rc == DEVICE_START_UNIT ||
rc == DEVICE_STOP_UNIT || rc == DEVICE_RETRY_UA)
&& count++ < command_retry_count);
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data->block = 0;
if (rc != DEVICE_READY)
sas_device_priv_data->deleted = 1;
_scsih_internal_device_unblock(sdev, sas_device_priv_data);
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
if (rc != DEVICE_READY) {
sdev_printk(KERN_WARNING, sdev, "%s: device_offlined,\n"
@@ -4060,24 +4114,30 @@ _scsih_ublock_io_device_wait(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
u8 tr_timeout = 30;
u8 tr_method = 0;
int count = 0;
+ unsigned long flags;
/* moving devices from SDEV_OFFLINE to SDEV_BLOCK */
shost_for_each_device(sdev, ioc->shost) {
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data = sdev->hostdata;
if (!sas_device_priv_data)
- continue;
+ goto next;
sas_target = sas_device_priv_data->sas_target;
if (!sas_target)
- continue;
+ goto next;
if (sas_target->sas_address != sas_address ||
sas_target->port != port)
- continue;
+ goto next;
+ /* polling one device can outlast another's deadline */
+ sas_device_priv_data->block_deadline = 0;
if (sdev->sdev_state == SDEV_OFFLINE) {
sas_device_priv_data->block = 1;
sas_device_priv_data->deleted = 0;
scsi_device_set_state(sdev, SDEV_RUNNING);
scsi_internal_device_block_nowait(sdev);
}
+next:
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
}
/* moving devices from SDEV_BLOCK to SDEV_RUNNING state */
@@ -4091,7 +4151,7 @@ _scsih_ublock_io_device_wait(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
if (sas_target->sas_address != sas_address ||
sas_target->port != port)
continue;
- if (!sas_device_priv_data->block)
+ if (!_scsih_claim_block(ioc, sdev))
continue;
do {
@@ -4129,10 +4189,12 @@ _scsih_ublock_io_device_wait(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
rc == DEVICE_STOP_UNIT || rc == DEVICE_RETRY_UA)
&& count++ <= command_retry_count);
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data->block = 0;
if (rc != DEVICE_READY)
sas_device_priv_data->deleted = 1;
scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
if (rc != DEVICE_READY) {
sdev_printk(KERN_WARNING, sdev,
@@ -4186,19 +4248,23 @@ _scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc,
{
struct MPT3SAS_DEVICE *sas_device_priv_data;
struct scsi_device *sdev;
+ unsigned long flags;
shost_for_each_device(sdev, ioc->shost) {
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data = sdev->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
- continue;
+ goto next;
if (sas_device_priv_data->sas_target->sas_address
!= sas_address)
- continue;
+ goto next;
if (sas_device_priv_data->sas_target->port != port)
- continue;
+ goto next;
if (sas_device_priv_data->block)
_scsih_internal_device_unblock(sdev,
sas_device_priv_data);
+next:
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
}
}
@@ -4213,21 +4279,119 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
{
struct MPT3SAS_DEVICE *sas_device_priv_data;
struct scsi_device *sdev;
+ unsigned long flags;
shost_for_each_device(sdev, ioc->shost) {
+ spin_lock_irqsave(&ioc->block_lock, flags);
sas_device_priv_data = sdev->hostdata;
if (!sas_device_priv_data)
- continue;
- if (sas_device_priv_data->block)
- continue;
+ goto next;
+ if (sas_device_priv_data->block) {
+ /* the caller unblocks it; dnr_unblock_work must not */
+ sas_device_priv_data->block_deadline = 0;
+ goto next;
+ }
if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev,
"%s skip device_block for SES handle(0x%04x)\n",
__func__, sas_device_priv_data->sas_target->handle);
- continue;
+ goto next;
}
_scsih_internal_device_block(sdev, sas_device_priv_data);
+next:
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
+ }
+}
+
+/*
+ * Margin on top of the firmware's report device missing delay before
+ * _scsih_dnr_unblock_work() releases a delayed not-responding block.
+ */
+#define MPT3SAS_DNR_BLOCK_SLACK 30
+
+static unsigned long
+_scsih_dnr_block_timeout(struct MPT3SAS_ADAPTER *ioc)
+{
+ return (max(ioc->device_missing_delay, ioc->pcie_device_missing_delay) +
+ MPT3SAS_DNR_BLOCK_SLACK) * HZ;
+}
+
+/**
+ * _scsih_dnr_unblock_sdev - release an expired delayed not-responding block
+ * @ioc: per adapter object
+ * @sdev: scsi device struct
+ *
+ * Return: the deadline of an unexpired block on @sdev, or 0 if none.
+ */
+static unsigned long
+_scsih_dnr_unblock_sdev(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ unsigned long flags, deadline = 0;
+
+ spin_lock_irqsave(&ioc->block_lock, flags);
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->block ||
+ !sas_device_priv_data->block_deadline)
+ goto out;
+ if (time_before(jiffies, sas_device_priv_data->block_deadline)) {
+ deadline = sas_device_priv_data->block_deadline;
+ goto out;
}
+ sdev_printk(KERN_WARNING, sdev,
+ "delayed not-responding block expired, handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle);
+ _scsih_internal_device_unblock(sdev, sas_device_priv_data);
+out:
+ spin_unlock_irqrestore(&ioc->block_lock, flags);
+ return deadline;
+}
+
+/**
+ * _scsih_dnr_unblock_work - release expired delayed not-responding blocks
+ * @work: the dnr_unblock_work of the adapter
+ *
+ * Within the device missing delay, the firmware either reports the device
+ * as not responding, and the event callback unblocks it, or the device
+ * comes back, and only the firmware event worker unblocks it. The worker
+ * may first need shost->scan_mutex or the SAS transport's host lock, and
+ * a scan holding those can have a command queued on the blocked sdev.
+ * Past the delay the block has no further use, so release it here.
+ */
+static void
+_scsih_dnr_unblock_work(struct work_struct *work)
+{
+ struct MPT3SAS_ADAPTER *ioc = container_of(to_delayed_work(work),
+ struct MPT3SAS_ADAPTER, dnr_unblock_work);
+ struct scsi_device *sdev;
+ unsigned long deadline, next = 0;
+
+ if (ioc->remove_host)
+ return;
+ /*
+ * scsih_qcmd() relies on blocked devices during SCSI EH, which
+ * outlasts shost_recovery.
+ */
+ if (ioc->shost_recovery || ioc->pci_error_recovery ||
+ scsi_host_in_recovery(ioc->shost)) {
+ mod_delayed_work(system_dfl_wq, &ioc->dnr_unblock_work, HZ);
+ return;
+ }
+
+ shost_for_each_device(sdev, ioc->shost) {
+ deadline = _scsih_dnr_unblock_sdev(ioc, sdev);
+ if (deadline && (!next || time_before(deadline, next)))
+ next = deadline;
+ }
+
+ /*
+ * A block made while this ran may have queued the work already,
+ * with its own later deadline. Pull the timer in to the earliest
+ * one seen here; the next run reschedules for the rest.
+ */
+ if (next)
+ mod_delayed_work(system_dfl_wq, &ioc->dnr_unblock_work,
+ max_t(long, next - jiffies, 1));
}
/**
@@ -4243,7 +4407,9 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
struct MPT3SAS_DEVICE *sas_device_priv_data;
struct scsi_device *sdev;
struct _sas_device *sas_device;
+ unsigned long timeout = _scsih_dnr_block_timeout(ioc);
unsigned long flags;
+ bool blocked = false;
sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
@@ -4254,10 +4420,23 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
goto next;
if (sas_device_priv_data->sas_target->handle != handle)
goto next;
- if (sas_device_priv_data->block)
+ if (sas_device_priv_data->block) {
+ /* a repeat DNR restarts the firmware's missing delay */
+ if (sas_device_priv_data->block_deadline) {
+ sas_device_priv_data->block_deadline =
+ (jiffies + timeout) ?: 1;
+ blocked = true;
+ }
goto next;
+ }
if (sas_device && sas_device->pend_sas_rphy_add)
goto next;
+ /*
+ * Expiry would bring it back running without a TUR check.
+ * _scsih_ublock_io_device_wait() polls offline devices anyway.
+ */
+ if (sdev->sdev_state == SDEV_OFFLINE)
+ goto next;
if (sas_device_priv_data->ignore_delay_remove) {
sdev_printk(KERN_INFO, sdev,
"%s skip device_block for SES handle(0x%04x)\n",
@@ -4265,12 +4444,18 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
goto next;
}
_scsih_internal_device_block(sdev, sas_device_priv_data);
+ sas_device_priv_data->block_deadline = (jiffies + timeout) ?: 1;
+ blocked = true;
next:
spin_unlock_irqrestore(&ioc->block_lock, flags);
}
if (sas_device)
sas_device_put(sas_device);
+
+ if (blocked)
+ queue_delayed_work(system_dfl_wq, &ioc->dnr_unblock_work,
+ timeout);
}
/**
@@ -12556,6 +12741,7 @@ static void scsih_remove(struct pci_dev *pdev)
return;
ioc->remove_host = 1;
+ disable_delayed_work_sync(&ioc->dnr_unblock_work);
if (!pci_device_is_present(pdev)) {
mpt3sas_base_pause_mq_polling(ioc);
@@ -12656,6 +12842,7 @@ scsih_shutdown(struct pci_dev *pdev)
return;
ioc->remove_host = 1;
+ disable_delayed_work_sync(&ioc->dnr_unblock_work);
if (!pci_device_is_present(pdev)) {
mpt3sas_base_pause_mq_polling(ioc);
@@ -13569,6 +13756,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_LIST_HEAD(&ioc->delayed_tr_volume_list);
INIT_LIST_HEAD(&ioc->reply_queue_list);
INIT_LIST_HEAD(&ioc->port_table_list);
+ INIT_DELAYED_WORK(&ioc->dnr_unblock_work, _scsih_dnr_unblock_work);
sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id);
@@ -13679,6 +13867,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mpt3sas_setup_debugfs(ioc);
return 0;
out_add_shost_fail:
+ disable_delayed_work_sync(&ioc->dnr_unblock_work);
mpt3sas_base_detach(ioc);
out_attach_fail:
destroy_workqueue(ioc->firmware_event_thread);
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-06 20:29 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 20:29 [PATCH v3 0/2] scsi: mpt3sas: Fix delayed not-responding sdev block handling Jeff Layton
2026-11-06 20:29 ` [PATCH v3 1/2] scsi: mpt3sas: Serialize interrupt-time sdev block with hostdata teardown Jeff Layton
2026-11-06 20:29 ` [PATCH v3 2/2] scsi: mpt3sas: Time out delayed not-responding device blocks Jeff Layton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®