Linux SCSI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jeff Layton" <jlayton@kernel.org>
Cc: linux-scsi@vger.kernel.org
Subject: Re: [PATCH] scsi: mpt3sas: Time out delayed not-responding device blocks
Date: Fri, 30 Oct 2026 15:11:49 +0000	[thread overview]
Message-ID: <sashiko-outbox-191144@kernel.org> (raw)
In-Reply-To: <20261030-sas-abba-v1-1-b8cdd714a37c@kernel.org>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] scsi: mpt3sas: use-after-free of sdev->hostdata in _scsih_dnr_unblock_work()
- [High] scsi: mpt3sas: race on block state in _scsih_dnr_unblock_work()
--

commit e9db80ae705cdf5de784aed30440e0d8af678dd9
Author: Jeff Layton <jlayton@kernel.org>

scsi: mpt3sas: Time out delayed not-responding device blocks

This commit adds a delayed work item (dnr_unblock_work) to time out and
release delayed not-responding SDEV_BLOCK states on SCSI devices after the
firmware's report device missing delay plus a 30-second margin, preventing
deadlocks during concurrent sysfs scans.

> diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
> index 085d4672db854..dda5a429a9173 100644
> --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
> +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
> @@ -3878,6 +3878,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)
> @@ -3903,6 +3904,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
> @@ -4121,6 +4123,7 @@ _scsih_ublock_io_device_wait(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
>  			&& count++ <= command_retry_count);
>  
>  		sas_device_priv_data->block = 0;
> +		sas_device_priv_data->block_deadline = 0;
>  		if (rc != DEVICE_READY)
>  			sas_device_priv_data->deleted = 1;
>  		scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
> @@ -4221,6 +4224,74 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
>  	}
>  }
>  
> +/*
> + * 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_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 MPT3SAS_DEVICE *sas_device_priv_data;
> +	struct scsi_device *sdev;
> +	unsigned long deadline, next = 0;
> +
> +	if (ioc->remove_host)
> +		return;
> +	if (ioc->shost_recovery || ioc->pci_error_recovery) {
> +		queue_delayed_work(system_dfl_wq, &ioc->dnr_unblock_work, HZ);
> +		return;
> +	}
> +
> +	shost_for_each_device(sdev, ioc->shost) {
> +		sas_device_priv_data = sdev->hostdata;
> +		if (!sas_device_priv_data || !sas_device_priv_data->block)
> +			continue;
> +		deadline = sas_device_priv_data->block_deadline;
> +		if (!deadline)
> +			continue;
> +		if (time_before(jiffies, deadline)) {
> +			if (!next || time_before(deadline, next))
> +				next = deadline;
> +			continue;
> +		}
> +		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);
> +	}

[Severity: High]
Can _scsih_dnr_unblock_work() trigger a use-after-free on sdev->hostdata
(struct MPT3SAS_DEVICE) and sas_device_priv_data->sas_target (struct
MPT3SAS_TARGET)?

_scsih_dnr_unblock_work() runs on system_dfl_wq without holding
shost->scan_mutex, shost->host_lock, or any adapter lock. Inside
shost_for_each_device(), __scsi_iterate_devices() only holds
shost->host_lock while calling scsi_device_get(sdev) to take a reference on
sdev->sdev_gendev, then drops shost->host_lock before returning sdev to the
loop body.

Concurrently on another CPU, __scsi_remove_device(sdev) can run (for
example, when scsi_probe_and_add_lun() fails on a newly allocated sdev
after TARG_NOT_RESPONDING unblocks it, when ioc->firmware_event_thread
removes the target via scsi_remove_target(), or via sysfs delete).
__scsi_remove_device() synchronously invokes scsih_sdev_destroy() before
put_device(&sdev->sdev_gendev):

drivers/scsi/mpt3sas/mpt3sas_scsih.c:scsih_sdev_destroy() {
    ...
	kfree(sdev->hostdata);
	sdev->hostdata = NULL;
}

and scsi_target_reap() invokes scsih_target_destroy(), freeing
starget->hostdata (sas_target).

Because holding a reference on sdev->sdev_gendev does not prevent
__scsi_remove_device() from calling ->sdev_destroy(),
_scsih_dnr_unblock_work() and _scsih_internal_device_unblock() can read and
write freed sas_device_priv_data (block and block_deadline) and dereference
freed sas_device_priv_data->sas_target->handle.

> +
> +	/*
> +	 * 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));
> +}
> +
>  /**
>   * _scsih_block_io_device - set the device state to SDEV_BLOCK
>   * @ioc: per adapter object
> @@ -4234,6 +4305,8 @@ _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);
> +	bool blocked = false;
>  
>  	sas_device = mpt3sas_get_sdev_by_handle(ioc, handle);
>  
> @@ -4254,10 +4327,16 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
>  			continue;
>  		}
>  		_scsih_internal_device_block(sdev, sas_device_priv_data);
> +		sas_device_priv_data->block_deadline = (jiffies + timeout) ?: 1;
> +		blocked = true;
>  	}

[Severity: High]
Does running _scsih_dnr_unblock_work() on system_dfl_wq without
synchronization race with _scsih_block_io_device() in interrupt context and
_scsih_ublock_io_device_wait() on firmware_event_thread?

There appear to be two races on sas_device_priv_data->block,
sas_device_priv_data->block_deadline, and sdev->sdev_state:

1. _scsih_dnr_unblock_work() runs in process context with interrupts
enabled and calls _scsih_internal_device_unblock():

drivers/scsi/mpt3sas/mpt3sas_scsih.c:_scsih_internal_device_unblock() {
    ...
	sas_device_priv_data->block = 0;
	sas_device_priv_data->block_deadline = 0;
	r = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
    ...
}

If an interrupt for a new DELAY_NOT_RESPONDING event fires right after
sas_device_priv_data->block = 0 is written,
mpt3sas_scsih_event_callback()->_scsih_block_io_device() sees
sas_device_priv_data->block == 0, calls _scsih_internal_device_block()
(setting sas_device_priv_data->block = 1 and sdev->sdev_state = SDEV_BLOCK),
and sets a new non-zero sas_device_priv_data->block_deadline.

When _scsih_internal_device_unblock() resumes, it overwrites
sas_device_priv_data->block_deadline = 0 and calls
scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING). That leaves sdev
in SDEV_RUNNING with sas_device_priv_data->block == 1 and
block_deadline == 0, causing scsih_qcmd() to reject all commands with
SCSI_MLQUEUE_DEVICE_BUSY indefinitely while _scsih_dnr_unblock_work()
ignores the device because block_deadline is 0.

2. In _scsih_ublock_io_device_wait() (and _scsih_ublock_io_all_device()),
sas_device_priv_data->block_deadline remains armed throughout the
_scsih_wait_for_device_to_become_ready() retry loop (up to
command_retry_count = 144 retries). If block_deadline expires while
_scsih_ublock_io_device_wait() is still polling the returning device or
offlining a failed device, _scsih_dnr_unblock_work() will concurrently call
_scsih_internal_device_unblock() and transition the device to SDEV_RUNNING.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261030-sas-abba-v1-1-b8cdd714a37c@kernel.org?part=1

      reply	other threads:[~2026-10-30 15:11 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 14:49 [PATCH] scsi: mpt3sas: Time out delayed not-responding device blocks Jeff Layton
2026-10-30 15:11 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-191144@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=jlayton@kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox