* [PATCH v10 01/15] ata: libata-acpi: pin the SCSI device while emitting a bay event
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 02/15] scsi: core: release the device tree node at final device release Phil Pemberton
` (13 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
ata_acpi_uevent() reads dev->sdev without taking a reference. SCSI device
removal can clear the pointer and release the device after the notifier
has obtained its kobject, leaving kobject_uevent_env() accessing freed
memory. Dock notification ordering does not exclude independent SCSI
removal, such as deletion through sysfs.
Acquire a SCSI device reference while holding ap->lock, which protects
clearing dev->sdev during removal. Emit the event and drop the reference
outside the spinlock because both operations may sleep. Preserve the
existing port-level event behavior and skip devices that cannot be pinned.
Fixes: 1253f7aabfeb ("dock: introduce .uevent for devices in dock, eg libata")
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-acpi.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 4433f626246b..bee738b68b3f 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -149,19 +149,29 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
u32 event)
{
struct kobject *kobj = NULL;
+ struct scsi_device *sdev = NULL;
+ unsigned long flags;
char event_string[20];
char *envp[] = { event_string, NULL };
if (dev) {
- if (dev->sdev)
- kobj = &dev->sdev->sdev_gendev.kobj;
- } else
+ spin_lock_irqsave(ap->lock, flags);
+ sdev = dev->sdev;
+ if (sdev && scsi_device_get(sdev))
+ sdev = NULL;
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (sdev)
+ kobj = &sdev->sdev_gendev.kobj;
+ } else {
kobj = &ap->dev->kobj;
+ }
if (kobj) {
snprintf(event_string, 20, "BAY_EVENT=%d", event);
kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
}
+ if (sdev)
+ scsi_device_put(sdev);
}
static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 02/15] scsi: core: release the device tree node at final device release
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 01/15] ata: libata-acpi: pin the SCSI device while emitting a bay event Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 03/15] ata: libata-zpodd: reference SCSI devices in PM callbacks Phil Pemberton
` (12 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
libata's ata_scsi_assign_ofnode() retains the reference returned by
for_each_available_child_of_node() when assigning a matching child to
sdev_gendev.of_node. Neither SCSI nor the generic device release path
currently drops that reference, leaking it with CONFIG_OF_DYNAMIC.
Drop the reference in scsi_device_dev_release(), after the last reference
to the SCSI device has gone away. The node must remain valid for the
lifetime of the device; putting it at assignment would leave an unowned
pointer. Final release also covers assignment racing with device removal
while the scanner still holds a SCSI device reference.
Fixes: 45b8084f3fcb ("libata: Assign OF node to the SCSI device")
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/scsi_sysfs.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 9480432f650b..5e766e976a1d 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/bsg.h>
+#include <linux/of.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -517,6 +518,7 @@ static void scsi_device_dev_release(struct device *dev)
if (vpd_pgb7)
kfree_rcu(vpd_pgb7, rcu);
kfree(sdev->inquiry);
+ of_node_put(dev->of_node);
kfree(sdev);
if (parent)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 03/15] ata: libata-zpodd: reference SCSI devices in PM callbacks
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 01/15] ata: libata-acpi: pin the SCSI device while emitting a bay event Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 02/15] scsi: core: release the device tree node at final device release Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 04/15] ata: libata-zpodd: publish initialized state before registering notifications Phil Pemberton
` (11 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, stable
The ZPODD wake callback accesses dev->sdev without locking or taking a
reference. Concurrent sysfs deletion can release the device before the
callback accesses its runtime PM state. The disk-event enable and disable
paths have the same problem.
Take a SCSI device reference under ap->lock, skipping missing or deleting
devices. Drop the lock before accessing PM or disk-event state and before
releasing the reference.
Fixes: f064a20dded8 ("libata: move acpi notification code to zpodd")
Link: https://lore.kernel.org/linux-ide/20260611030131.5285D1F00893@smtp.kernel.org/
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-zpodd.c | 41 +++++++++++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 414e7c63bd85..581eb41ea0f3 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -173,6 +173,22 @@ bool zpodd_zpready(struct ata_device *dev)
return zpodd->zp_ready;
}
+/* Return a referenced SCSI device; the caller must drop it outside ap->lock. */
+static struct scsi_device *zpodd_get_sdev(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->link->ap;
+ struct scsi_device *sdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(ap->lock, flags);
+ sdev = dev->sdev;
+ if (sdev && scsi_device_get(sdev))
+ sdev = NULL;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return sdev;
+}
+
/*
* Enable runtime wake capability through ACPI and set the powered_off flag,
* this flag will be used during resume to decide what operations are needed
@@ -184,8 +200,12 @@ bool zpodd_zpready(struct ata_device *dev)
void zpodd_enable_run_wake(struct ata_device *dev)
{
struct zpodd *zpodd = dev->zpodd;
+ struct scsi_device *sdev = zpodd_get_sdev(dev);
- sdev_disable_disk_events(dev->sdev);
+ if (sdev) {
+ sdev_disable_disk_events(sdev);
+ scsi_device_put(sdev);
+ }
zpodd->powered_off = true;
acpi_pm_set_device_wakeup(&dev->tdev, true);
@@ -218,6 +238,7 @@ void zpodd_disable_run_wake(struct ata_device *dev)
void zpodd_post_poweron(struct ata_device *dev)
{
struct zpodd *zpodd = dev->zpodd;
+ struct scsi_device *sdev;
if (!zpodd->powered_off)
return;
@@ -233,19 +254,29 @@ void zpodd_post_poweron(struct ata_device *dev)
zpodd->zp_sampled = false;
zpodd->zp_ready = false;
- sdev_enable_disk_events(dev->sdev);
+ sdev = zpodd_get_sdev(dev);
+ if (sdev) {
+ sdev_enable_disk_events(sdev);
+ scsi_device_put(sdev);
+ }
}
static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
{
struct ata_device *ata_dev = context;
struct zpodd *zpodd = ata_dev->zpodd;
- struct device *dev = &ata_dev->sdev->sdev_gendev;
+ struct scsi_device *sdev;
- if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
+ if (event != ACPI_NOTIFY_DEVICE_WAKE)
+ return;
+ sdev = zpodd_get_sdev(ata_dev);
+ if (!sdev)
+ return;
+ if (pm_runtime_suspended(&sdev->sdev_gendev)) {
zpodd->from_notify = true;
- pm_runtime_resume(dev);
+ pm_runtime_resume(&sdev->sdev_gendev);
}
+ scsi_device_put(sdev);
}
static void ata_acpi_add_pm_notifier(struct ata_device *dev)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 04/15] ata: libata-zpodd: publish initialized state before registering notifications
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (2 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 03/15] ata: libata-zpodd: reference SCSI devices in PM callbacks Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 05/15] ata: libata-zpodd: serialize wake attribution with physical power-on Phil Pemberton
` (10 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, stable
zpodd_init() installs the ACPI notification handler before assigning
zpodd->dev and dev->zpodd. A notification delivered in that window can
enter zpodd_wake_dev() with a NULL context pointer. In particular,
initialization retried during EH can already have a suspended SCSI device,
allowing the callback to reach its from_notify assignment.
Initialize the back pointer and publish dev->zpodd before installing the
handler, so every callback observes initialized state. Keep removal of the
handler before freeing the state, as acpi_remove_notify_handler() waits
for queued notifications to complete.
Fixes: f064a20dded8 ("libata: move acpi notification code to zpodd")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-zpodd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 581eb41ea0f3..c57c49316fbd 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -311,9 +311,9 @@ void zpodd_init(struct ata_device *dev)
zpodd->mech_type = mech_type;
- ata_acpi_add_pm_notifier(dev);
zpodd->dev = dev;
dev->zpodd = zpodd;
+ ata_acpi_add_pm_notifier(dev);
dev_pm_qos_expose_flags(&dev->tdev, 0);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 05/15] ata: libata-zpodd: serialize wake attribution with physical power-on
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (3 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 04/15] ata: libata-zpodd: publish initialized state before registering notifications Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 06/15] ata: libata-core: pin SCSI devices during port detach Phil Pemberton
` (9 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, stable
The notification handler checks runtime suspension before setting
from_notify, while EH reads and clears that flag during physical power-on.
A concurrent resume can finish after the check but before the assignment,
leaving attribution behind for an unrelated later power cycle. Taking a
SCSI device reference does not serialize those state changes.
Protect powered_off and from_notify with ap->lock. Attribute a notification
only to the current powered-off physical device, and consume that
attribution atomically when EH completes power-on. Reset attribution when
a new power-off cycle starts. Keep PM calls and tray commands outside the
spinlock.
Let the PM core handle active or already-resuming SCSI devices instead of
using a lockless suspended-state check to gate notification attribution.
Track wake callbacks in progress, so an overlapping callback with no SCSI
device cannot clear attribution still needed by another callback. Clear
unconsumed attribution when the last synchronous wake callback returns,
including failed wakes.
Fixes: f064a20dded8 ("libata: move acpi notification code to zpodd")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-zpodd.c | 65 ++++++++++++++++++++++++++++----------
1 file changed, 48 insertions(+), 17 deletions(-)
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index c57c49316fbd..309b3769ae01 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -22,14 +22,15 @@ struct zpodd {
enum odd_mech_type mech_type; /* init during probe, RO afterwards */
struct ata_device *dev;
+ /* The following fields are protected by the ATA port lock. */
+ unsigned int notify_count; /* wake callbacks in progress */
+ bool from_notify; /* power-on requested by notification */
+ bool powered_off; /* powered off during suspend */
+
/* The following fields are synchronized by PM core. */
- bool from_notify; /* resumed as a result of
- * acpi wake notification */
bool zp_ready; /* ZP ready state */
unsigned long last_ready; /* last ZP ready timestamp */
bool zp_sampled; /* ZP ready state sampled */
- bool powered_off; /* ODD is powered off
- * during suspend */
};
static int eject_tray(struct ata_device *dev)
@@ -199,7 +200,9 @@ static struct scsi_device *zpodd_get_sdev(struct ata_device *dev)
*/
void zpodd_enable_run_wake(struct ata_device *dev)
{
+ struct ata_port *ap = dev->link->ap;
struct zpodd *zpodd = dev->zpodd;
+ unsigned long flags;
struct scsi_device *sdev = zpodd_get_sdev(dev);
if (sdev) {
@@ -207,16 +210,25 @@ void zpodd_enable_run_wake(struct ata_device *dev)
scsi_device_put(sdev);
}
+ spin_lock_irqsave(ap->lock, flags);
zpodd->powered_off = true;
+ zpodd->from_notify = false;
+ spin_unlock_irqrestore(ap->lock, flags);
acpi_pm_set_device_wakeup(&dev->tdev, true);
}
/* Disable runtime wake capability if it is enabled */
void zpodd_disable_run_wake(struct ata_device *dev)
{
+ struct ata_port *ap = dev->link->ap;
struct zpodd *zpodd = dev->zpodd;
+ unsigned long flags;
+ bool powered_off;
- if (zpodd->powered_off)
+ spin_lock_irqsave(ap->lock, flags);
+ powered_off = zpodd->powered_off;
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (powered_off)
acpi_pm_set_device_wakeup(&dev->tdev, false);
}
@@ -237,19 +249,24 @@ void zpodd_disable_run_wake(struct ata_device *dev)
*/
void zpodd_post_poweron(struct ata_device *dev)
{
+ struct ata_port *ap = dev->link->ap;
struct zpodd *zpodd = dev->zpodd;
+ unsigned long flags;
+ bool from_notify;
struct scsi_device *sdev;
- if (!zpodd->powered_off)
+ spin_lock_irqsave(ap->lock, flags);
+ if (!zpodd->powered_off) {
+ spin_unlock_irqrestore(ap->lock, flags);
return;
-
+ }
zpodd->powered_off = false;
+ from_notify = zpodd->from_notify;
+ zpodd->from_notify = false;
+ spin_unlock_irqrestore(ap->lock, flags);
- if (zpodd->from_notify) {
- zpodd->from_notify = false;
- if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
- eject_tray(dev);
- }
+ if (from_notify && zpodd->mech_type == ODD_MECH_TYPE_DRAWER)
+ eject_tray(dev);
zpodd->zp_sampled = false;
zpodd->zp_ready = false;
@@ -264,19 +281,33 @@ void zpodd_post_poweron(struct ata_device *dev)
static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
{
struct ata_device *ata_dev = context;
+ struct ata_port *ap = ata_dev->link->ap;
struct zpodd *zpodd = ata_dev->zpodd;
struct scsi_device *sdev;
+ unsigned long flags;
if (event != ACPI_NOTIFY_DEVICE_WAKE)
return;
- sdev = zpodd_get_sdev(ata_dev);
- if (!sdev)
- return;
- if (pm_runtime_suspended(&sdev->sdev_gendev)) {
+
+ /* Attribute the notification to the current physical power cycle. */
+ spin_lock_irqsave(ap->lock, flags);
+ zpodd->notify_count++;
+ if (zpodd->powered_off)
zpodd->from_notify = true;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ sdev = zpodd_get_sdev(ata_dev);
+ if (sdev) {
+ /* The PM core handles an already active or resuming device. */
pm_runtime_resume(&sdev->sdev_gendev);
+ scsi_device_put(sdev);
}
- scsi_device_put(sdev);
+
+ /* Do not discard attribution while another wake callback is active. */
+ spin_lock_irqsave(ap->lock, flags);
+ if (!--zpodd->notify_count)
+ zpodd->from_notify = false;
+ spin_unlock_irqrestore(ap->lock, flags);
}
static void ata_acpi_add_pm_notifier(struct ata_device *dev)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 06/15] ata: libata-core: pin SCSI devices during port detach
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (4 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 05/15] ata: libata-zpodd: serialize wake attribution with physical power-on Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 07/15] scsi: scsi_devinfo: preserve full-width quirk identifiers Phil Pemberton
` (8 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, stable
ata_port_detach() drops ap->lock before removing dev->sdev. Concurrent
sysfs deletion can release the device before scsi_remove_device() uses it.
Take a device reference and clear the slot under ap->lock, then remove
the saved device. Release the reference outside the lock because device
release can sleep.
Use get_device() to hold the reference during host-driver unload, when
scsi_device_get() can fail its module reference check. The SCSI core
uses the same approach for target removal.
Fixes: 84d76529c650 ("ata: libata-core: Fix port and device removal")
Link: https://lore.kernel.org/linux-ide/20260731220740.7E14E1F00ACF@smtp.kernel.org/
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-core.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 92233fb25051..5518525357ce 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -26,6 +26,7 @@
*/
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
@@ -6388,10 +6389,15 @@ static void ata_port_detach(struct ata_port *ap)
ata_for_each_link(link, ap, HOST_FIRST) {
ata_for_each_dev(dev, link, ALL) {
if (dev->sdev) {
+ struct scsi_device *sdev = dev->sdev;
+
+ /* The host driver may already be unloading. */
+ get_device(&sdev->sdev_gendev);
+ dev->sdev = NULL;
spin_unlock_irqrestore(ap->lock, flags);
- scsi_remove_device(dev->sdev);
+ scsi_remove_device(sdev);
+ put_device(&sdev->sdev_gendev);
spin_lock_irqsave(ap->lock, flags);
- dev->sdev = NULL;
}
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 07/15] scsi: scsi_devinfo: preserve full-width quirk identifiers
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (5 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 06/15] ata: libata-core: pin SCSI devices during port detach Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 08/15] scsi: sr: keep queue block sizes in sync with the medium Phil Pemberton
` (7 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, stable
strscpy_pad() reserves a byte for NUL termination, truncating quirk
identifiers that fill their fields. An eight-character vendor such as
MATSHITA loses its final character and fails to match. A sixteen-character
model loses its final character too; prefix matching then allows the
quirk to match other models with the same first fifteen characters.
Copy the full vendor and model fields. Pad shorter strings with NUL for
compatible entries and spaces for dynamic entries. Lookup uses bounded
strnlen() and memcmp(), so full-width identifiers need no terminator.
Shorter model names continue to match prefixes.
Fixes: 1b60c86dd992 ("scsi: devinfo: Replace strncpy() and manual pad")
Link: https://lore.kernel.org/linux-ide/20260611025249.D23191F00893@smtp.kernel.org/
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/scsi_devinfo.c | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 88a911b35c94..5e37461e61e8 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -289,14 +289,9 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length,
from_length = strlen(from);
- /*
- * null pad and null terminate if compatible
- * otherwise space pad
- */
- if (compatible)
- strscpy_pad(to, from, to_length);
- else
- memcpy_and_pad(to, to_length, from, from_length, ' ');
+ /* Full-width identifiers need all bytes; shorter ones are padded. */
+ memcpy_and_pad(to, to_length, from, from_length,
+ compatible ? '\0' : ' ');
if (from_length > to_length)
printk(KERN_WARNING "%s: %s string '%s' is too long\n",
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 08/15] scsi: sr: keep queue block sizes in sync with the medium
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (6 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 07/15] scsi: scsi_devinfo: preserve full-width quirk identifiers Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 09/15] scsi: sr: retry unfinished media revalidation on the next open Phil Pemberton
` (6 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
sr_probe() sets sdev->sector_size to 2048, but the request queue keeps its
512-byte default until revalidation finds ready media. Booting a COMPAQ
PD-1 LF-1195C with a PD inserted leaves the empty CD LUN with mismatched
block sizes. Later CD reads can fail with "unaligned transfer".
After initial revalidation, set the queue's block sizes to
sdev->sector_size if the logical size differs. Do this before registering
the disk, and unwind the probe if the update fails. Revalidate first so
ready media can supply the actual block size instead of the 2048-byte
guess.
Also set the physical block size and minimum I/O size when revalidating
media. The block layer raises these to the logical block size, but does
not lower them when a later medium has smaller sectors. Updating only
the logical size can therefore leave stale 2048-byte physical and minimum
I/O sizes after inserting 512-byte media or switching between media types.
sr does not discover separate physical or minimum I/O sizes, so keep both
in sync with the medium's reported sector size.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/sr.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index c36c54ecd354..28523b0eb659 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -488,6 +488,8 @@ static int sr_revalidate_disk(struct scsi_cd *cd)
lim = queue_limits_start_update(q);
lim.logical_block_size = sector_size;
+ lim.physical_block_size = sector_size;
+ lim.io_min = sector_size;
lim.features |= BLK_FEAT_ROTATIONAL;
return queue_limits_commit_update_frozen(q, &lim);
}
@@ -619,6 +621,7 @@ static void sr_release(struct cdrom_device_info *cdi)
static int sr_probe(struct scsi_device *sdev)
{
struct device *dev = &sdev->sdev_gendev;
+ struct queue_limits lim;
struct gendisk *disk;
struct scsi_cd *cd;
int minor, error;
@@ -701,6 +704,18 @@ static int sr_probe(struct scsi_device *sdev)
if (error)
goto unregister_cdrom;
+ /* If no medium was ready, match the queue to the driver's guess. */
+ if (queue_logical_block_size(sdev->request_queue) != sdev->sector_size) {
+ lim = queue_limits_start_update(sdev->request_queue);
+ lim.logical_block_size = sdev->sector_size;
+ lim.physical_block_size = sdev->sector_size;
+ lim.io_min = sdev->sector_size;
+ error = queue_limits_commit_update_frozen(sdev->request_queue,
+ &lim);
+ if (error)
+ goto unregister_cdrom;
+ }
+
error = device_add_disk(&sdev->sdev_gendev, disk, NULL);
if (error)
goto unregister_cdrom;
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 09/15] scsi: sr: retry unfinished media revalidation on the next open
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (7 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 08/15] scsi: sr: keep queue block sizes in sync with the medium Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 10/15] ata: libata-scsi: add atapi_max_lun module parameter Phil Pemberton
` (5 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
A media-change event can arrive while a new disc is spinning up.
Revalidation then returns before updating capacity and queue limits. If
there is no further event, later opens keep the old disc's capacity.
This occurs on a COMPAQ PD-1 LF-1195C after a CD to PD to CD swap.
Keep revalidation pending until it completes, including the queue-limits
update, and retry on the next open. This also covers a CD LUN that is
unready at probe. Access the flag during probe and serialized block opens.
For blocking opens, return -ENOMEDIUM if revalidation reports NOT READY
with ASC/ASCQ 04/01 (becoming ready), leaving the flag set. The drive can
become ready before cdrom_open() checks it again; failing this open
prevents access with stale capacity. sr_drive_status() already maps this
sense code to -ENOMEDIUM for CD-ROM data opens. Probe, nonblocking opens
and other sense results retain their existing behavior.
Initialize the sense header and only classify sense after a positive
SCSI command result. Host-side failures can return without filling the
header; preserve their existing retry behavior instead of interpreting
undefined or stale sense bytes as a becoming-ready response.
READ CAPACITY errors still use the existing fallback for audio CDs and
older drives. That fallback can retain stale capacity and clear the flag.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/sr.c | 29 ++++++++++++++++++++---------
drivers/scsi/sr.h | 2 ++
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 28523b0eb659..044f4c250d88 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -473,16 +473,24 @@ static blk_status_t sr_init_command(struct scsi_cmnd *SCpnt)
return BLK_STS_IOERR;
}
-static int sr_revalidate_disk(struct scsi_cd *cd)
+static int sr_revalidate_disk(struct scsi_cd *cd, bool fail_if_becoming_ready)
{
struct request_queue *q = cd->device->request_queue;
- struct scsi_sense_hdr sshdr;
+ struct scsi_sense_hdr sshdr = {};
struct queue_limits lim;
- int sector_size;
+ int sector_size, ret;
- /* if the unit is not ready, nothing more to do */
- if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
+ /* Keep revalidation pending if the medium is not ready yet. */
+ cd->needs_revalidate = true;
+ ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
+ if (ret) {
+ /* Do not let a later readiness check admit stale geometry. */
+ if (ret > 0 && fail_if_becoming_ready && scsi_sense_valid(&sshdr) &&
+ sshdr.sense_key == NOT_READY &&
+ sshdr.asc == 0x04 && sshdr.ascq == 0x01)
+ return -ENOMEDIUM;
return 0;
+ }
sr_cd_check(&cd->cdi);
sector_size = get_sectorsize(cd);
@@ -491,7 +499,10 @@ static int sr_revalidate_disk(struct scsi_cd *cd)
lim.physical_block_size = sector_size;
lim.io_min = sector_size;
lim.features |= BLK_FEAT_ROTATIONAL;
- return queue_limits_commit_update_frozen(q, &lim);
+ ret = queue_limits_commit_update_frozen(q, &lim);
+ if (!ret)
+ cd->needs_revalidate = false;
+ return ret;
}
static int sr_block_open(struct gendisk *disk, blk_mode_t mode)
@@ -504,8 +515,8 @@ static int sr_block_open(struct gendisk *disk, blk_mode_t mode)
return -ENXIO;
scsi_autopm_get_device(sdev);
- if (disk_check_media_change(disk)) {
- ret = sr_revalidate_disk(cd);
+ if (disk_check_media_change(disk) || cd->needs_revalidate) {
+ ret = sr_revalidate_disk(cd, !(mode & BLK_OPEN_NDELAY));
if (ret)
goto out;
}
@@ -700,7 +711,7 @@ static int sr_probe(struct scsi_device *sdev)
blk_pm_runtime_init(sdev->request_queue, dev);
dev_set_drvdata(dev, cd);
- error = sr_revalidate_disk(cd);
+ error = sr_revalidate_disk(cd, false);
if (error)
goto unregister_cdrom;
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
index 2d92f9cb6fec..b718ac87e82d 100644
--- a/drivers/scsi/sr.h
+++ b/drivers/scsi/sr.h
@@ -48,6 +48,8 @@ typedef struct scsi_cd {
bool ignore_get_event:1; /* GET_EVENT is unreliable, use TUR */
struct cdrom_device_info cdi;
+ /* Protected by disk->open_mutex after probe. */
+ bool needs_revalidate;
struct mutex lock;
struct gendisk *disk;
} Scsi_CD;
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 10/15] ata: libata-scsi: add atapi_max_lun module parameter
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (8 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 09/15] scsi: sr: retry unfinished media revalidation on the next open Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 11/15] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
` (4 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, Hannes Reinecke
libata sets shost->max_lun to 1, so SCSI scans stop at LUN 0. Panasonic
LF-1195C and COMPAQ PD-1 drives expose their PD media on LUN 1, and
Nakamichi MJ-x.y CD changers expose a LUN for each disc slot.
Add the atapi_max_lun module parameter to set the host's scan limit.
Keep the default of 1 and clamp the value to 1..8, covering SCSI-2 LUNs
0..7. Later patches enable automatic scanning of additional LUNs for
ATAPI devices marked BLIST_FORCELUN.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-core.c | 5 +++++
drivers/ata/libata-scsi.c | 2 +-
drivers/ata/libata.h | 1 +
include/linux/libata.h | 1 +
4 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 5518525357ce..f2f5ad727d20 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -123,6 +123,11 @@ int atapi_passthru16 = 1;
module_param(atapi_passthru16, int, 0444);
MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices (0=off, 1=on [default])");
+int atapi_max_lun = 1;
+module_param(atapi_max_lun, int, 0444);
+MODULE_PARM_DESC(atapi_max_lun,
+ "Number of LUNs to scan on ATAPI devices flagged BLIST_FORCELUN (1 [default] = LUN 0 only, 8 = all SCSI-2 LUNs 0..7)");
+
int libata_fua = 0;
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off [default], 1=on)");
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 8f9aa97a519d..3131d84204ed 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -5230,7 +5230,7 @@ int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *s
shost->transportt = &ata_scsi_transportt;
shost->unique_id = ap->print_id;
shost->max_id = 16;
- shost->max_lun = 1;
+ shost->max_lun = clamp(atapi_max_lun, 1, ATAPI_MAX_LUN);
shost->max_channel = 1;
shost->max_cmd_len = 32;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 48274691cd0e..aedc6b61196b 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -33,6 +33,7 @@ enum {
#define ATA_PORT_TYPE_NAME "ata_port"
extern int atapi_passthru16;
+extern int atapi_max_lun;
extern int libata_fua;
extern int libata_noacpi;
extern int libata_allow_tpm;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 48bde275968d..03511280800f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -180,6 +180,7 @@ enum {
ATA_SHORT_PAUSE = 16,
ATAPI_MAX_DRAIN = 16 << 10,
+ ATAPI_MAX_LUN = 8, /* SCSI-2 cap (LUN values 0..7) */
ATA_ALL_DEVICES = (1 << ATA_MAX_DEVICES) - 1,
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 11/15] ata: libata-scsi: convert dev->sdev to per-LUN array
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (9 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 10/15] ata: libata-scsi: add atapi_max_lun module parameter Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 12/15] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
` (3 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
Multi-LUN ATAPI devices share one ata_device and expose several SCSI
devices. Replace dev->sdev with an eight-slot array indexed by LUN and
update its callers. Single-LUN devices use slot 0; unused slots are NULL.
Reject out-of-range scan requests with -EINVAL.
Publish each slot only after the SCSI core accepts the device. Serialize
lookup and publication with the SCSI host scan mutex, and update the slot
under ap->lock. This keeps configuration failures invisible to libata and
prevents SCSI removal from racing publication.
Take references to pending slots under ap->lock before resuming or
rescanning them. Track pending resume and rescan work separately for each
LUN, so a deferred LUN does not repeat its siblings' completed work and a
deferred rescan does not repeat a successful resume callback. Distinguish
new EH requests from retries so completion cannot erase newer work.
Request runtime resume asynchronously for suspended LUNs and hold a PM
reference across retries. Waiting for runtime resume inside this worker
could deadlock with port suspend, which waits for the worker. Release PM
references on completion, cancellation, reinitialization, device disable
and removal. Release SCSI references outside the lock because device
release can sleep.
Iterate over every referenced LUN in the ZPODD callbacks. Wake attribution
remains shared by the physical ATA device and is armed once for the whole
callback, so sibling resumes cannot produce duplicate or delayed ejections.
Pass the referenced LUN-0 device to ata_scsi_assign_ofnode(). Concurrent
removal can clear its array slot while the reference is held.
Clear slots under ap->lock before removing devices. Schedule ATA detach
when the last populated LUN is destroyed.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-acpi.c | 2 +-
drivers/ata/libata-core.c | 17 +-
drivers/ata/libata-eh.c | 11 +-
drivers/ata/libata-scsi.c | 336 +++++++++++++++++++++++++++----------
drivers/ata/libata-zpodd.c | 46 +++--
drivers/ata/libata.h | 3 +
include/linux/libata.h | 15 +-
7 files changed, 311 insertions(+), 119 deletions(-)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index bee738b68b3f..6c98a26d9f81 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -156,7 +156,7 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
if (dev) {
spin_lock_irqsave(ap->lock, flags);
- sdev = dev->sdev;
+ sdev = ata_dev_scsi_device(dev, 0);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(ap->lock, flags);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index f2f5ad727d20..4df3518f7085 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5490,7 +5490,7 @@ static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg,
* The next resume will schedule the rescan again. So cancel any rescan
* that is not done yet.
*/
- cancel_delayed_work_sync(&ap->scsi_rescan_task);
+ ata_scsi_cancel_rescan(ap);
/*
* On some hardware, device fails to respond after spun down for
@@ -5675,6 +5675,9 @@ void ata_dev_init(struct ata_device *dev)
spin_lock_irqsave(ap->lock, flags);
dev->flags &= ~ATA_DFLAG_INIT_MASK;
dev->quirks = 0;
+ for (unsigned int lun = 0; lun < ATAPI_MAX_LUN; lun++)
+ ata_scsi_clear_pending(dev, lun);
+ dev->scsi_rescan_seq++;
spin_unlock_irqrestore(ap->lock, flags);
memset((void *)dev + ATA_DEVICE_CLEAR_BEGIN, 0,
@@ -6393,12 +6396,16 @@ static void ata_port_detach(struct ata_port *ap)
/* Remove scsi devices */
ata_for_each_link(link, ap, HOST_FIRST) {
ata_for_each_dev(dev, link, ALL) {
- if (dev->sdev) {
- struct scsi_device *sdev = dev->sdev;
+ int lun;
+
+ for (lun = ATAPI_MAX_LUN - 1; lun >= 0; lun--) {
+ struct scsi_device *sdev = dev->sdev[lun];
- /* The host driver may already be unloading. */
+ if (!sdev)
+ continue;
get_device(&sdev->sdev_gendev);
- dev->sdev = NULL;
+ ata_scsi_clear_pending(dev, lun);
+ dev->sdev[lun] = NULL;
spin_unlock_irqrestore(ap->lock, flags);
scsi_remove_device(sdev);
put_device(&sdev->sdev_gendev);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index c93c24b22d9c..25d335c33d0a 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -504,9 +504,18 @@ void ata_eh_release(struct ata_port *ap)
static void ata_eh_dev_disable(struct ata_device *dev)
{
+ struct ata_port *ap = dev->link->ap;
+ unsigned long flags;
+ int lun;
+
ata_acpi_on_disable(dev);
ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
+ spin_lock_irqsave(ap->lock, flags);
dev->class++;
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++)
+ ata_scsi_clear_pending(dev, lun);
+ dev->scsi_rescan_seq++;
+ spin_unlock_irqrestore(ap->lock, flags);
/*
* From now till the next successful probe, ering is used to
@@ -3360,7 +3369,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
ehc->i.flags |= ATA_EHI_SETMODE;
/* schedule the scsi_rescan_device() here */
- schedule_delayed_work(&ap->scsi_rescan_task, 0);
+ ata_scsi_schedule_rescan(dev);
} else if (dev->class == ATA_DEV_UNKNOWN &&
ehc->tries[dev->devno] &&
ata_class_enabled(ehc->classes[dev->devno])) {
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 3131d84204ed..efd0916f8180 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -33,6 +33,7 @@
#include <linux/unaligned.h>
#include <linux/ioprio.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include "libata.h"
#include "libata-transport.h"
@@ -1135,7 +1136,8 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim,
if (dev->flags & ATA_DFLAG_TRUSTED)
sdev->security_supported = 1;
- dev->sdev = sdev;
+ if (sdev->lun >= ATAPI_MAX_LUN)
+ return -EINVAL;
return 0;
}
@@ -1206,10 +1208,10 @@ EXPORT_SYMBOL_GPL(ata_scsi_sdev_configure);
*
* @sdev is about to be destroyed for hot/warm unplugging. If
* this unplugging was initiated by libata as indicated by NULL
- * dev->sdev, this function doesn't have to do anything.
+ * dev->sdev[], this function doesn't have to do anything.
* Otherwise, SCSI layer initiated warm-unplug is in progress.
- * Clear dev->sdev, schedule the device for ATA detach and invoke
- * EH.
+ * Clear the per-LUN slot; when the last populated LUN is destroyed,
+ * schedule ATA-level detach via EH.
*
* LOCKING:
* Defined by SCSI layer. We don't really care.
@@ -1224,11 +1226,24 @@ void ata_scsi_sdev_destroy(struct scsi_device *sdev)
spin_lock_irqsave(ap->lock, flags);
dev = __ata_scsi_find_dev(ap, sdev);
- if (dev && dev->sdev) {
- /* SCSI device already in CANCEL state, no need to offline it */
- dev->sdev = NULL;
- dev->flags |= ATA_DFLAG_DETACH;
- ata_port_schedule_eh(ap);
+ if (dev && sdev->lun < ATAPI_MAX_LUN &&
+ dev->sdev[sdev->lun] == sdev) {
+ int lun;
+ bool last;
+
+ ata_scsi_clear_pending(dev, sdev->lun);
+ dev->sdev[sdev->lun] = NULL;
+ last = true;
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ if (dev->sdev[lun]) {
+ last = false;
+ break;
+ }
+ }
+ if (last) {
+ dev->flags |= ATA_DFLAG_DETACH;
+ ata_port_schedule_eh(ap);
+ }
}
spin_unlock_irqrestore(ap->lock, flags);
@@ -3052,12 +3067,9 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
*
* If door lock fails, always clear sdev->locked to
* avoid this infinite loop.
- *
- * This may happen before SCSI scan is complete. Make
- * sure qc->dev->sdev isn't NULL before dereferencing.
*/
- if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev)
- qc->dev->sdev->locked = 0;
+ if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL)
+ qc->scsicmd->device->locked = 0;
if (cmd->result)
ata_scsi_qc_done(qc, false, 0);
@@ -5259,9 +5271,9 @@ int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *s
}
#ifdef CONFIG_OF
-static void ata_scsi_assign_ofnode(struct ata_device *dev, struct ata_port *ap)
+static void ata_scsi_assign_ofnode(struct scsi_device *sdev,
+ struct ata_device *dev, struct ata_port *ap)
{
- struct scsi_device *sdev = dev->sdev;
struct device *d = ap->host->dev;
struct device_node *np = d->of_node;
struct device_node *child;
@@ -5281,11 +5293,48 @@ static void ata_scsi_assign_ofnode(struct ata_device *dev, struct ata_port *ap)
}
}
#else
-static void ata_scsi_assign_ofnode(struct ata_device *dev, struct ata_port *ap)
+static void ata_scsi_assign_ofnode(struct scsi_device *sdev,
+ struct ata_device *dev, struct ata_port *ap)
{
}
#endif
+/* Publish fully configured SCSI devices against concurrent removal. */
+static void ata_scsi_publish_sdevs(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->link->ap;
+ struct Scsi_Host *shost = ap->scsi_host;
+ unsigned int channel = 0, id = 0;
+ unsigned long flags;
+ unsigned int lun, max_lun;
+
+ if (ata_is_host_link(dev->link))
+ id = dev->devno;
+ else
+ channel = dev->link->pmp;
+
+ /* Serialize lookup and publication with SCSI device removal. */
+ max_lun = min_t(unsigned int, shost->max_lun, ATAPI_MAX_LUN);
+ mutex_lock(&shost->scan_mutex);
+ for (lun = 0; lun < max_lun; lun++) {
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(shost, channel, id, lun);
+ if (!sdev)
+ continue;
+
+ spin_lock_irqsave(ap->lock, flags);
+ if (!dev->sdev[lun])
+ dev->sdev[lun] = sdev;
+ else
+ WARN_ON_ONCE(dev->sdev[lun] != sdev);
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ scsi_device_put(sdev);
+ }
+ mutex_unlock(&shost->scan_mutex);
+}
+
void ata_scsi_scan_host(struct ata_port *ap, int sync)
{
int tries = 5;
@@ -5299,7 +5348,7 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
struct scsi_device *sdev;
int channel = 0, id = 0;
- if (dev->sdev)
+ if (dev->sdev[0])
continue;
if (ata_is_host_link(link))
@@ -5309,13 +5358,12 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
sdev = __scsi_add_device(ap->scsi_host, channel, id, 0,
NULL);
- if (!IS_ERR(sdev)) {
- dev->sdev = sdev;
- ata_scsi_assign_ofnode(dev, ap);
- scsi_device_put(sdev);
- } else {
- dev->sdev = NULL;
- }
+ if (IS_ERR(sdev))
+ continue;
+
+ ata_scsi_publish_sdevs(dev);
+ ata_scsi_assign_ofnode(sdev, dev, ap);
+ scsi_device_put(sdev);
}
}
@@ -5325,7 +5373,7 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
*/
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ENABLED) {
- if (!dev->sdev)
+ if (!dev->sdev[0])
goto exit_loop;
}
}
@@ -5366,7 +5414,7 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
*
* This function is called from ata_eh_detach_dev() and is responsible for
* taking the SCSI device attached to @dev offline. This function is
- * called with host lock which protects dev->sdev against clearing.
+ * called with host lock which protects dev->sdev[] against clearing.
*
* LOCKING:
* spin_lock_irqsave(host lock)
@@ -5376,11 +5424,16 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
*/
bool ata_scsi_offline_dev(struct ata_device *dev)
{
- if (dev->sdev) {
- scsi_device_set_state(dev->sdev, SDEV_OFFLINE);
- return true;
+ bool found = false;
+ int lun;
+
+ for (lun = ATAPI_MAX_LUN - 1; lun >= 0; lun--) {
+ if (dev->sdev[lun]) {
+ scsi_device_set_state(dev->sdev[lun], SDEV_OFFLINE);
+ found = true;
+ }
}
- return false;
+ return found;
}
/**
@@ -5396,49 +5449,39 @@ bool ata_scsi_offline_dev(struct ata_device *dev)
static void ata_scsi_remove_dev(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
- struct scsi_device *sdev;
+ struct scsi_device *sdevs[ATAPI_MAX_LUN] = {};
unsigned long flags;
+ int lun;
- /* Alas, we need to grab scan_mutex to ensure SCSI device
- * state doesn't change underneath us and thus
- * scsi_device_get() always succeeds. The mutex locking can
- * be removed if there is __scsi_device_get() interface which
- * increments reference counts regardless of device state.
- */
mutex_lock(&ap->scsi_host->scan_mutex);
spin_lock_irqsave(ap->lock, flags);
- /* clearing dev->sdev is protected by host lock */
- sdev = dev->sdev;
- dev->sdev = NULL;
+ for (lun = ATAPI_MAX_LUN - 1; lun >= 0; lun--) {
+ struct scsi_device *sdev = dev->sdev[lun];
+
+ ata_scsi_clear_pending(dev, lun);
+ dev->sdev[lun] = NULL;
+ if (!sdev)
+ continue;
- if (sdev) {
- /* If user initiated unplug races with us, sdev can go
- * away underneath us after the host lock and
- * scan_mutex are released. Hold onto it.
- */
if (scsi_device_get(sdev) == 0) {
- /* The following ensures the attached sdev is
- * offline on return from ata_scsi_offline_dev()
- * regardless it wins or loses the race
- * against this function.
- */
scsi_device_set_state(sdev, SDEV_OFFLINE);
+ sdevs[lun] = sdev;
} else {
WARN_ON(1);
- sdev = NULL;
}
}
spin_unlock_irqrestore(ap->lock, flags);
mutex_unlock(&ap->scsi_host->scan_mutex);
- if (sdev) {
+ for (lun = ATAPI_MAX_LUN - 1; lun >= 0; lun--) {
+ if (!sdevs[lun])
+ continue;
ata_dev_info(dev, "detaching (SCSI %s)\n",
- dev_name(&sdev->sdev_gendev));
-
- scsi_remove_device(sdev);
- scsi_device_put(sdev);
+ dev_name(&sdevs[lun]->sdev_gendev));
+ scsi_remove_device(sdevs[lun]);
+ scsi_device_put(sdevs[lun]);
}
}
@@ -5475,9 +5518,12 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
*/
void ata_scsi_media_change_notify(struct ata_device *dev)
{
- if (dev->sdev)
- sdev_evt_send_simple(dev->sdev, SDEV_EVT_MEDIA_CHANGE,
- GFP_ATOMIC);
+ int lun;
+
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++)
+ if (dev->sdev[lun])
+ sdev_evt_send_simple(dev->sdev[lun],
+ SDEV_EVT_MEDIA_CHANGE, GFP_ATOMIC);
}
/**
@@ -5585,15 +5631,67 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
return rc;
}
+/* Called with ap->lock held, before clearing a SCSI device slot. */
+void ata_scsi_clear_pending(struct ata_device *dev, unsigned int lun)
+{
+ lockdep_assert_held(dev->link->ap->lock);
+
+ if (dev->sdev_rescan_pm & BIT(lun)) {
+ pm_runtime_put_noidle(&dev->sdev[lun]->sdev_gendev);
+ dev->sdev_rescan_pm &= ~BIT(lun);
+ }
+ dev->sdev_resume_pending &= ~BIT(lun);
+ dev->sdev_rescan_pending &= ~BIT(lun);
+}
+
+void ata_scsi_cancel_rescan(struct ata_port *ap)
+{
+ struct ata_link *link;
+ struct ata_device *dev;
+ unsigned long flags;
+ int lun;
+
+ cancel_delayed_work_sync(&ap->scsi_rescan_task);
+ spin_lock_irqsave(ap->lock, flags);
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++)
+ ata_scsi_clear_pending(dev, lun);
+ dev->scsi_rescan_seq++;
+ }
+ }
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
+/* Record a new revalidation separately from retries of the previous one. */
+void ata_scsi_schedule_rescan(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->link->ap;
+ unsigned long flags;
+ int lun;
+
+ spin_lock_irqsave(ap->lock, flags);
+ dev->scsi_rescan_seq++;
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ if (!dev->sdev[lun])
+ continue;
+ dev->sdev_rescan_pending |= BIT(lun);
+ if (dev->flags & ATA_DFLAG_RESUMING)
+ dev->sdev_resume_pending |= BIT(lun);
+ }
+ dev->flags &= ~ATA_DFLAG_RESUMING;
+ schedule_delayed_work(&ap->scsi_rescan_task, 0);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
/**
- * ata_scsi_dev_rescan - initiate scsi_rescan_device()
- * @work: Pointer to ATA port to perform scsi_rescan_device()
+ * ata_scsi_dev_rescan - resume and rescan pending SCSI LUNs
+ * @work: Pointer to ATA port to perform scsi_rescan_device()
*
- * After ATA pass thru (SAT) commands are executed successfully,
- * libata need to propagate the changes to SCSI layer.
+ * Propagate ATA revalidation to SCSI. Completed LUNs are not revisited on
+ * retry, and a deferred rescan does not repeat a completed resume callback.
*
- * LOCKING:
- * Kernel thread context (may sleep).
+ * LOCKING: Kernel thread context (may sleep).
*/
void ata_scsi_dev_rescan(struct work_struct *work)
{
@@ -5602,55 +5700,107 @@ void ata_scsi_dev_rescan(struct work_struct *work)
struct ata_link *link;
struct ata_device *dev;
unsigned long flags;
- bool do_resume;
- int ret = 0;
+ bool retry = false;
mutex_lock(&ap->scsi_scan_mutex);
spin_lock_irqsave(ap->lock, flags);
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ENABLED) {
- struct scsi_device *sdev = dev->sdev;
+ struct scsi_device *sdevs[ATAPI_MAX_LUN] = {};
+ unsigned int seq = dev->scsi_rescan_seq;
+ u8 resume = dev->sdev_resume_pending;
+ int lun;
- /*
- * If the port was suspended before this was scheduled,
- * bail out.
- */
if (ap->pflags & ATA_PFLAG_SUSPENDED)
goto unlock_ap;
- if (!sdev)
- continue;
- if (scsi_device_get(sdev))
- continue;
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ if (!(dev->sdev_rescan_pending & BIT(lun)))
+ continue;
+ if (!dev->sdev[lun] ||
+ scsi_device_get(dev->sdev[lun])) {
+ ata_scsi_clear_pending(dev, lun);
+ continue;
+ }
+ sdevs[lun] = dev->sdev[lun];
+ if (!(dev->sdev_rescan_pm & BIT(lun))) {
+ pm_runtime_get_noresume(&sdevs[lun]->sdev_gendev);
+ dev->sdev_rescan_pm |= BIT(lun);
+ }
+ }
- do_resume = dev->flags & ATA_DFLAG_RESUMING;
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ struct scsi_device *sdev = sdevs[lun];
+ bool resumed = false, put_pm = false;
+ int err;
- spin_unlock_irqrestore(ap->lock, flags);
- if (do_resume) {
- ret = scsi_resume_device(sdev);
- if (ret == -EWOULDBLOCK) {
+ if (!sdev)
+ continue;
+ if (dev->sdev[lun] != sdev ||
+ dev->scsi_rescan_seq != seq) {
+ spin_unlock_irqrestore(ap->lock, flags);
scsi_device_put(sdev);
- goto unlock_scan;
+ spin_lock_irqsave(ap->lock, flags);
+ continue;
}
- dev->flags &= ~ATA_DFLAG_RESUMING;
- }
- ret = scsi_rescan_device(sdev);
- scsi_device_put(sdev);
- spin_lock_irqsave(ap->lock, flags);
- if (ret)
- goto unlock_ap;
+ spin_unlock_irqrestore(ap->lock, flags);
+ /*
+ * Port suspend waits for this worker. Never wait for
+ * runtime resume here: request it asynchronously and
+ * retain the PM reference across retries instead.
+ */
+ err = pm_runtime_get_if_active(&sdev->sdev_gendev);
+ if (!err) {
+ err = pm_request_resume(&sdev->sdev_gendev);
+ if (err >= 0 || err == -EINPROGRESS ||
+ err == -EACCES)
+ err = -EWOULDBLOCK;
+ goto complete;
+ }
+ /* The pending-work reference already keeps it active. */
+ if (err > 0)
+ pm_runtime_put_noidle(&sdev->sdev_gendev);
+ if (resume & BIT(lun)) {
+ err = scsi_resume_device(sdev);
+ if (err == -EWOULDBLOCK)
+ goto complete;
+ resumed = true;
+ }
+ err = scsi_rescan_device(sdev);
+complete:
+ spin_lock_irqsave(ap->lock, flags);
+ /* Removal or a new EH request supersedes this pass. */
+ if (dev->sdev[lun] == sdev &&
+ dev->scsi_rescan_seq == seq) {
+ if (resumed)
+ dev->sdev_resume_pending &= ~BIT(lun);
+ if (err != -EWOULDBLOCK) {
+ dev->sdev_resume_pending &= ~BIT(lun);
+ dev->sdev_rescan_pending &= ~BIT(lun);
+ put_pm = dev->sdev_rescan_pm & BIT(lun);
+ dev->sdev_rescan_pm &= ~BIT(lun);
+ }
+ }
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (err && err != -EWOULDBLOCK)
+ sdev_printk(KERN_WARNING, sdev,
+ "rescan failed: %d\n", err);
+ if (put_pm)
+ pm_runtime_put_autosuspend(&sdev->sdev_gendev);
+ scsi_device_put(sdev);
+ spin_lock_irqsave(ap->lock, flags);
+ }
+ retry |= dev->sdev_rescan_pending != 0;
}
}
unlock_ap:
spin_unlock_irqrestore(ap->lock, flags);
-unlock_scan:
mutex_unlock(&ap->scsi_scan_mutex);
- /* Reschedule with a delay if scsi_rescan_device() returned an error */
- if (ret)
+ if (retry)
schedule_delayed_work(&ap->scsi_rescan_task,
msecs_to_jiffies(5));
}
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 309b3769ae01..6ff0a8ee11ba 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -114,7 +114,7 @@ static bool zpready(struct ata_device *dev)
return false;
sense_buf = dev->sector_buf;
- ret = atapi_eh_request_sense(dev, sense_buf, sense_key);
+ ret = atapi_eh_request_sense(dev, 0, sense_buf, sense_key);
if (ret)
return false;
@@ -175,14 +175,14 @@ bool zpodd_zpready(struct ata_device *dev)
}
/* Return a referenced SCSI device; the caller must drop it outside ap->lock. */
-static struct scsi_device *zpodd_get_sdev(struct ata_device *dev)
+static struct scsi_device *zpodd_get_sdev(struct ata_device *dev, unsigned int lun)
{
struct ata_port *ap = dev->link->ap;
struct scsi_device *sdev;
unsigned long flags;
spin_lock_irqsave(ap->lock, flags);
- sdev = dev->sdev;
+ sdev = dev->sdev[lun];
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(ap->lock, flags);
@@ -203,11 +203,15 @@ void zpodd_enable_run_wake(struct ata_device *dev)
struct ata_port *ap = dev->link->ap;
struct zpodd *zpodd = dev->zpodd;
unsigned long flags;
- struct scsi_device *sdev = zpodd_get_sdev(dev);
+ int lun;
- if (sdev) {
- sdev_disable_disk_events(sdev);
- scsi_device_put(sdev);
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ struct scsi_device *sdev = zpodd_get_sdev(dev, lun);
+
+ if (sdev) {
+ sdev_disable_disk_events(sdev);
+ scsi_device_put(sdev);
+ }
}
spin_lock_irqsave(ap->lock, flags);
@@ -253,7 +257,7 @@ void zpodd_post_poweron(struct ata_device *dev)
struct zpodd *zpodd = dev->zpodd;
unsigned long flags;
bool from_notify;
- struct scsi_device *sdev;
+ int lun;
spin_lock_irqsave(ap->lock, flags);
if (!zpodd->powered_off) {
@@ -271,10 +275,13 @@ void zpodd_post_poweron(struct ata_device *dev)
zpodd->zp_sampled = false;
zpodd->zp_ready = false;
- sdev = zpodd_get_sdev(dev);
- if (sdev) {
- sdev_enable_disk_events(sdev);
- scsi_device_put(sdev);
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ struct scsi_device *sdev = zpodd_get_sdev(dev, lun);
+
+ if (sdev) {
+ sdev_enable_disk_events(sdev);
+ scsi_device_put(sdev);
+ }
}
}
@@ -283,8 +290,8 @@ static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
struct ata_device *ata_dev = context;
struct ata_port *ap = ata_dev->link->ap;
struct zpodd *zpodd = ata_dev->zpodd;
- struct scsi_device *sdev;
unsigned long flags;
+ int lun;
if (event != ACPI_NOTIFY_DEVICE_WAKE)
return;
@@ -296,11 +303,14 @@ static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
zpodd->from_notify = true;
spin_unlock_irqrestore(ap->lock, flags);
- sdev = zpodd_get_sdev(ata_dev);
- if (sdev) {
- /* The PM core handles an already active or resuming device. */
- pm_runtime_resume(&sdev->sdev_gendev);
- scsi_device_put(sdev);
+ for (lun = 0; lun < ATAPI_MAX_LUN; lun++) {
+ struct scsi_device *sdev = zpodd_get_sdev(ata_dev, lun);
+
+ if (sdev) {
+ /* PM handles an already active or resuming device. */
+ pm_runtime_resume(&sdev->sdev_gendev);
+ scsi_device_put(sdev);
+ }
}
/* Do not discard attribution while another wake callback is active. */
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index aedc6b61196b..e86052d4b067 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -171,6 +171,9 @@ extern void ata_scsi_set_sense(struct ata_device *dev,
extern void ata_scsi_media_change_notify(struct ata_device *dev);
extern void ata_scsi_hotplug(struct work_struct *work);
extern void ata_scsi_dev_rescan(struct work_struct *work);
+void ata_scsi_schedule_rescan(struct ata_device *dev);
+void ata_scsi_clear_pending(struct ata_device *dev, unsigned int lun);
+void ata_scsi_cancel_rescan(struct ata_port *ap);
extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, u64 lun);
void ata_scsi_sdev_config(struct scsi_device *sdev);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 03511280800f..7cdad5096c06 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -733,7 +733,12 @@ struct ata_device {
unsigned int devno; /* 0 or 1 */
u64 quirks; /* List of broken features */
unsigned long flags; /* ATA_DFLAG_xxx */
- struct scsi_device *sdev; /* attached SCSI device */
+ struct scsi_device *sdev[ATAPI_MAX_LUN]; /* per-LUN SCSI devices */
+ /* SCSI rescan work, protected by the port lock. */
+ unsigned int scsi_rescan_seq;
+ u8 sdev_resume_pending;
+ u8 sdev_rescan_pending;
+ u8 sdev_rescan_pm;
void *private_data;
#ifdef CONFIG_ATA_ACPI
union acpi_object *gtf_cache;
@@ -1730,6 +1735,14 @@ static inline unsigned int ata_dev_absent(const struct ata_device *dev)
return ata_class_absent(dev->class);
}
+static inline struct scsi_device *
+ata_dev_scsi_device(struct ata_device *dev, unsigned int lun)
+{
+ if (WARN_ON_ONCE(lun >= ATAPI_MAX_LUN))
+ return NULL;
+ return dev->sdev[lun];
+}
+
/*
* link helpers
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 12/15] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (10 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 11/15] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 13/15] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
` (2 subsequent siblings)
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
Allow nonzero LUNs in __ata_scsi_find_dev(). Keep this lookup independent
of device class so removal can clear the per-LUN pointer after error
handling disables the device. Restrict nonzero LUN commands to ATAPI
in ata_scsi_find_dev().
Encode nonzero LUNs in CDB byte 1, bits 7:5, as required by older SCSI-2
ATAPI devices. Reject values above 7 with DID_ERROR. Check this encoding
limit directly, since SAS hosts can have much larger shost->max_lun
values. Preserve byte 1 for LUN 0, where modern MMC commands use the upper
bits for command-specific fields.
Pass the failed command's LUN to atapi_eh_request_sense() so error handling
reads sense from the correct unit. Internal reset and ZPODD commands
continue to use LUN 0.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-eh.c | 10 +++++++---
drivers/ata/libata-scsi.c | 25 ++++++++++++++++++++++---
drivers/ata/libata.h | 4 ++--
3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 25d335c33d0a..2345fe5eb606 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1544,6 +1544,7 @@ static bool ata_eh_request_sense(struct ata_queued_cmd *qc)
/**
* atapi_eh_request_sense - perform ATAPI REQUEST_SENSE
* @dev: device to perform REQUEST_SENSE to
+ * @lun: logical unit to request sense from
* @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long)
* @dfl_sense_key: default sense key to use
*
@@ -1556,14 +1557,16 @@ static bool ata_eh_request_sense(struct ata_queued_cmd *qc)
* RETURNS:
* 0 on success, AC_ERR_* mask on failure
*/
-unsigned int atapi_eh_request_sense(struct ata_device *dev,
- u8 *sense_buf, u8 dfl_sense_key)
+unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 lun,
+ u8 *sense_buf, u8 dfl_sense_key)
{
u8 cdb[ATAPI_CDB_LEN] =
{ REQUEST_SENSE, 0, 0, 0, SCSI_SENSE_BUFFERSIZE, 0 };
struct ata_port *ap = dev->link->ap;
struct ata_taskfile tf;
+ cdb[1] = lun << 5;
+
memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
/* initialize sense_buf with the error register,
@@ -1709,6 +1712,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc)
case ATA_DEV_ATAPI:
if (!ata_port_is_frozen(qc->ap)) {
tmp = atapi_eh_request_sense(qc->dev,
+ qc->scsicmd->device->lun,
qc->scsicmd->sense_buffer,
qc->result_tf.error >> 4);
if (!tmp)
@@ -3540,7 +3544,7 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
if (!err_mask || sense_key != UNIT_ATTENTION)
return 0;
- err_mask = atapi_eh_request_sense(dev, sense_buffer, sense_key);
+ err_mask = atapi_eh_request_sense(dev, 0, sense_buffer, sense_key);
if (err_mask) {
ata_dev_warn(dev, "failed to clear "
"UNIT ATTENTION (err_mask=0x%x)\n", err_mask);
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index efd0916f8180..faf408acf65e 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3113,6 +3113,21 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc)
memset(qc->cdb, 0, dev->cdb_len);
memcpy(qc->cdb, scmd->cmnd, scmd->cmd_len);
+ /*
+ * SCSI-2 CDB LUN encoding: bits 7:5 of byte 1 (3-bit field).
+ * Byte 1 is left untouched for LUN 0, since commands which use
+ * the full byte (e.g. MMC SECURITY PROTOCOL IN/OUT, where it is
+ * the 8-bit security protocol) must pass through unmodified.
+ */
+ if (unlikely(scmd->device->lun)) {
+ if (scmd->device->lun >= ATAPI_MAX_LUN) {
+ scmd->result = DID_ERROR << 16;
+ return 1;
+ }
+ qc->cdb[1] = (qc->cdb[1] & 0x1f) |
+ ((u8)scmd->device->lun << 5);
+ }
+
qc->complete_fn = atapi_qc_complete;
qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
@@ -3223,13 +3238,13 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
{
int devno;
- /* skip commands not addressed to targets we simulate */
+ /* Teardown must still find devices whose class was changed by EH. */
if (!sata_pmp_attached(ap)) {
- if (unlikely(scsidev->channel || scsidev->lun))
+ if (unlikely(scsidev->channel))
return NULL;
devno = scsidev->id;
} else {
- if (unlikely(scsidev->id || scsidev->lun))
+ if (unlikely(scsidev->id))
return NULL;
devno = scsidev->channel;
}
@@ -3264,6 +3279,10 @@ ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev)
if (unlikely(!dev || !ata_dev_enabled(dev)))
return NULL;
+ /* Only ATAPI devices can route commands to nonzero LUNs. */
+ if (scsidev->lun && dev->class != ATA_DEV_ATAPI)
+ return NULL;
+
return dev;
}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index e86052d4b067..457b1bd766c2 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -219,8 +219,8 @@ extern int ata_ering_map(struct ata_ering *ering,
void *arg);
enum scsi_disposition ata_eh_decide_disposition(struct ata_queued_cmd *qc);
extern unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key);
-extern unsigned int atapi_eh_request_sense(struct ata_device *dev,
- u8 *sense_buf, u8 dfl_sense_key);
+unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 lun,
+ u8 *sense_buf, u8 dfl_sense_key);
/* libata-pmp.c */
#ifdef CONFIG_SATA_PMP
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v10 13/15] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (11 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 12/15] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 16:38 ` sashiko-bot
2026-10-09 15:55 ` [PATCH v10 14/15] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
2026-10-09 15:55 ` [PATCH v10 15/15] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton
14 siblings, 1 reply; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
Some multi-LUN devices report unpopulated LUNs with PQ=0/PDT=0x1f.
SCSI scanning adds these as spurious "No Device" entries.
Add BLIST_NO_LUN_1F to set pdt_1f_for_no_lun from scsi_devinfo before
scsi_probe_and_add_lun() checks PDT=0x1f. Setting it here also covers
LUN 0.
Give pdt_1f_for_no_lun its own bool. Sharing bitfield storage with
expecting_lun_change allows scan-time writes and concurrent I/O updates
to overwrite each other.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/scsi_scan.c | 3 +++
include/scsi/scsi_device.h | 4 ++--
include/scsi/scsi_devinfo.h | 6 +++---
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 0f0f243c2561..e34824623294 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1298,6 +1298,9 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *shost,
* PDT=00h Direct-access device (floppy)
* PDT=1Fh none (no FDD connected to the requested logical unit)
*/
+ if (bflags & BLIST_NO_LUN_1F)
+ starget->pdt_1f_for_no_lun = 1;
+
if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) &&
(result[0] & 0x1f) == 0x1f &&
!scsi_is_wlun(lun)) {
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 8694eeadd753..a81a7c8cce18 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -360,13 +360,13 @@ struct scsi_target {
unsigned int single_lun:1; /* Indicates we should only
* allow I/O to one of the luns
* for the device at a time. */
- unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f
- * means no lun present. */
unsigned int no_report_luns:1; /* Don't use
* REPORT LUNS for scanning. */
unsigned int expecting_lun_change:1; /* A device has reported
* a 3F/0E UA, other devices on
* the same target will also. */
+ /* Keep scan-time writes separate from I/O-updated bitfields. */
+ bool pdt_1f_for_no_lun; /* PDT = 0x1f means no LUN. */
/* commands actually active on LLD. */
atomic_t target_busy;
atomic_t target_blocked;
diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h
index 1d79a3b536ce..6957b0705510 100644
--- a/include/scsi/scsi_devinfo.h
+++ b/include/scsi/scsi_devinfo.h
@@ -34,7 +34,8 @@
#define BLIST_NOSTARTONADD ((__force blist_flags_t)(1ULL << 12))
/* do not ask for VPD page size first on some broken targets */
#define BLIST_NO_VPD_SIZE ((__force blist_flags_t)(1ULL << 13))
-#define __BLIST_UNUSED_14 ((__force blist_flags_t)(1ULL << 14))
+/* PDT 0x1f with PQ 0 means no LUN present (e.g. some ATAPI multi-LUN) */
+#define BLIST_NO_LUN_1F ((__force blist_flags_t)(1ULL << 14))
#define __BLIST_UNUSED_15 ((__force blist_flags_t)(1ULL << 15))
#define __BLIST_UNUSED_16 ((__force blist_flags_t)(1ULL << 16))
/* try REPORT_LUNS even for SCSI-2 devs (if HBA supports more than 8 LUNs) */
@@ -77,8 +78,7 @@
#define __BLIST_HIGH_UNUSED (~(__BLIST_LAST_USED | \
(__force blist_flags_t) \
((__force __u64)__BLIST_LAST_USED - 1ULL)))
-#define __BLIST_UNUSED_MASK (__BLIST_UNUSED_14 | \
- __BLIST_UNUSED_15 | \
+#define __BLIST_UNUSED_MASK (__BLIST_UNUSED_15 | \
__BLIST_UNUSED_16 | \
__BLIST_UNUSED_24 | \
__BLIST_UNUSED_27 | \
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v10 13/15] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-10-09 15:55 ` [PATCH v10 13/15] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
@ 2026-10-09 16:38 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-09 16:38 UTC (permalink / raw)
To: Phil Pemberton; +Cc: linux-scsi, cassel, dlemoal, linux-ide
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] scsi: single_lun bitfield data race with expecting_lun_change
--
commit 15aed878e542873ad45c1f172fe5d87a9c1380de
Author: Phil Pemberton <philpem@philpem.me.uk>
scsi: add BLIST_NO_LUN_1F blacklist flag
This commit adds a new BLIST_NO_LUN_1F blacklist flag to prevent SCSI
scanning from adding spurious "No Device" entries for unpopulated LUNs
reporting PQ=0/PDT=0x1f. It also safely moves pdt_1f_for_no_lun to a
dedicated boolean to prevent scan-time writes from overwriting concurrent
I/O updates in shared bitfields.
> diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
> index 8694eeadd753e..a81a7c8cce18e 100644
> --- a/include/scsi/scsi_device.h
> +++ b/include/scsi/scsi_device.h
> @@ -360,13 +360,13 @@ struct scsi_target {
> unsigned int single_lun:1; /* Indicates we should only
> * allow I/O to one of the luns
> * for the device at a time. */
> - unsigned int pdt_1f_for_no_lun:1; /* PDT = 0x1f
> - * means no lun present. */
> unsigned int no_report_luns:1; /* Don't use
> * REPORT LUNS for scanning. */
> unsigned int expecting_lun_change:1; /* A device has reported
> * a 3F/0E UA, other devices on
> * the same target will also. */
[Severity: High]
Does leaving single_lun in the same bitfield block as expecting_lun_change
still leave us vulnerable to read-modify-write tearing?
While this patch correctly identifies the tearing risk and moves
pdt_1f_for_no_lun to its own bool, single_lun is also updated at scan time
in scsi_add_lun() under shost->scan_mutex:
drivers/scsi/scsi_scan.c:scsi_add_lun() {
...
if (*bflags & BLIST_SINGLELUN)
scsi_target(sdev)->single_lun = 1;
...
}
While LUN 1 (or later) is being scanned, LUN 0 is already visible and can
process I/O. If an I/O on LUN 0 triggers a Unit Attention or completes a
REPORT LUNS command, softirq block completion context updates
expecting_lun_change without holding scan_mutex:
drivers/scsi/scsi_error.c:scsi_report_lun_change() {
...
sdev->sdev_target->expecting_lun_change = 1;
...
}
Because both fields share the same memory word, the scan-time update to
single_lun can silently overwrite and erase the concurrent I/O update to
expecting_lun_change, leading to dropped I/O retries, spurious I/O errors,
or loss of single_lun serialization.
This race is explicitly triggered by follow-up patch 15 in this series,
which introduces a quirk setting BLIST_SINGLELUN for a multi-LUN device
(COMPAQ PD-1).
Should single_lun also be moved to a separate bool to completely resolve
this bitfield race?
> + /* Keep scan-time writes separate from I/O-updated bitfields. */
> + bool pdt_1f_for_no_lun; /* PDT = 0x1f means no LUN. */
> /* commands actually active on LLD. */
> atomic_t target_busy;
> atomic_t target_blocked;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261009155522.593045-1-philpem@philpem.me.uk?part=13
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v10 14/15] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (12 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 13/15] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
2026-10-09 16:44 ` sashiko-bot
2026-10-09 15:55 ` [PATCH v10 15/15] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton
14 siblings, 1 reply; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton
After adding LUN 0 of an ATAPI device marked BLIST_FORCELUN, scan the
target's remaining LUNs using scsi_scan_target(). The scan is bounded by
shost->max_lun, set through atapi_max_lun.
Publish the accepted secondary LUNs after the scan, using the SCSI scan
mutex and ap->lock helper introduced by the per-LUN conversion. This keeps
partially configured or failed SCSI devices out of dev->sdev[] and
serializes publication with device removal.
Also accept explicit user scans for LUNs below shost->max_lun. Scan the
selected ATAPI targets after error handling completes, using
SCSI_SCAN_MANUAL and scsi_scan_mutex to serialize with hotplug. This lets
users restore a deleted secondary LUN while LUN 0 remains attached.
BLIST_NO_LUN_1F lets the SCSI layer skip LUNs reported as absent with
PQ=0/PDT=0x1f. ata_scsi_dev_config() rejects LUNs outside dev->sdev[].
Assign the OF node before dropping the SCSI device reference, using the
referenced device so concurrent removal cannot invalidate the pointer.
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-scsi.c | 50 +++++++++++++++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 2 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index faf408acf65e..bba2db2f8428 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -26,6 +26,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport.h>
+#include <scsi/scsi_devinfo.h>
#include <linux/libata.h>
#include <linux/hdreg.h>
#include <linux/uaccess.h>
@@ -5381,6 +5382,20 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
continue;
ata_scsi_publish_sdevs(dev);
+
+ /*
+ * For multi-LUN ATAPI (BLIST_FORCELUN), trigger a
+ * sequential scan for this target. pdt_1f_for_no_lun,
+ * set during LUN 0 configure, ensures non-responding
+ * LUNs are silently skipped.
+ */
+ if (dev->class == ATA_DEV_ATAPI &&
+ sdev->sdev_bflags & BLIST_FORCELUN) {
+ scsi_scan_target(&ap->scsi_host->shost_gendev,
+ channel, id, SCAN_WILD_CARD,
+ SCSI_SCAN_RESCAN);
+ ata_scsi_publish_sdevs(dev);
+ }
ata_scsi_assign_ofnode(sdev, dev, ap);
scsi_device_put(sdev);
}
@@ -5591,7 +5606,7 @@ void ata_scsi_hotplug(struct work_struct *work)
* @lun: LUN to scan
*
* This function is called when user explicitly requests bus
- * scan. Set probe pending flag and invoke EH.
+ * scan. Set probe pending flag and invoke EH, then scan ATAPI LUNs.
*
* LOCKING:
* SCSI layer (we don't care)
@@ -5606,7 +5621,7 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned long flags;
int devno, rc = 0;
- if (lun != SCAN_WILD_CARD && lun)
+ if (lun != SCAN_WILD_CARD && lun >= shost->max_lun)
return -EINVAL;
if (!sata_pmp_attached(ap)) {
@@ -5647,6 +5662,37 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
} else
spin_unlock_irqrestore(ap->lock, flags);
+ if (!rc) {
+ struct ata_link *link;
+ struct ata_device *dev;
+
+ /* EH does not scan missing LUNs on an already attached target. */
+ mutex_lock(&ap->scsi_scan_mutex);
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ unsigned int scan_channel = 0, scan_id = 0;
+
+ if (dev->class != ATA_DEV_ATAPI)
+ continue;
+ if (ata_is_host_link(link))
+ scan_id = dev->devno;
+ else
+ scan_channel = link->pmp;
+ if (channel != SCAN_WILD_CARD &&
+ channel != scan_channel)
+ continue;
+ if (id != SCAN_WILD_CARD && id != scan_id)
+ continue;
+
+ scsi_scan_target(&shost->shost_gendev,
+ scan_channel, scan_id, lun,
+ SCSI_SCAN_MANUAL);
+ ata_scsi_publish_sdevs(dev);
+ }
+ }
+ mutex_unlock(&ap->scsi_scan_mutex);
+ }
+
return rc;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v10 14/15] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices
2026-10-09 15:55 ` [PATCH v10 14/15] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
@ 2026-10-09 16:44 ` sashiko-bot
0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-09 16:44 UTC (permalink / raw)
To: Phil Pemberton; +Cc: linux-scsi, cassel, dlemoal, linux-ide
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ata: libata-scsi: manual scan of ATAPI targets bypasses OF node assignment
--
commit 8bad316476ffbbf5d3a2fdb9796c051181202764
Author: Phil Pemberton <philpem@philpem.me.uk>
ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices
After adding LUN 0 of an ATAPI device marked BLIST_FORCELUN, scan the
target's remaining LUNs using scsi_scan_target(). The scan is bounded by
shost->max_lun, set through atapi_max_lun.
> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
> index faf408acf65ec..bba2db2f84284 100644
> --- a/drivers/ata/libata-scsi.c
> +++ b/drivers/ata/libata-scsi.c
[ ... ]
> @@ -5647,6 +5662,37 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
> } else
> spin_unlock_irqrestore(ap->lock, flags);
>
> + if (!rc) {
> + struct ata_link *link;
> + struct ata_device *dev;
> +
> + /* EH does not scan missing LUNs on an already attached target. */
> + mutex_lock(&ap->scsi_scan_mutex);
> + ata_for_each_link(link, ap, EDGE) {
> + ata_for_each_dev(dev, link, ENABLED) {
[ ... ]
> + scsi_scan_target(&shost->shost_gendev,
> + scan_channel, scan_id, lun,
> + SCSI_SCAN_MANUAL);
> + ata_scsi_publish_sdevs(dev);
^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: High]
Does this manual scan bypass OF node assignment and multi-LUN scanning for
newly attached ATAPI targets?
If a user initiates a manual scan via sysfs (for example, using
echo "- - -" > /sys/class/scsi_host/hostX/scan), ata_scsi_user_scan()
schedules and waits for EH. EH configures the device and queues
hotplug_task (a delayed work).
Because ata_scsi_user_scan() continues immediately, it usually beats the
deferred hotplug_task and populates dev->sdev[0] here via
ata_scsi_publish_sdevs().
When hotplug_task subsequently runs ata_scsi_scan_host(), it skips the
remaining initialization due to the guard check in ata_scsi_scan_host():
drivers/ata/libata-scsi.c:ata_scsi_scan_host() {
...
if (dev->sdev[0])
continue;
...
}
Will this entirely bypass ata_scsi_assign_ofnode() and the new BLIST_FORCELUN
multi-LUN scan logic for the target, potentially affecting device tree
bindings and leaving secondary LUNs inaccessible?
> + }
> + }
> + mutex_unlock(&ap->scsi_scan_mutex);
> + }
> +
> return rc;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261009155522.593045-1-philpem@philpem.me.uk?part=14
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v10 15/15] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk
2026-10-09 15:55 [PATCH v10 00/15] ata: add multi-LUN support for ATAPI devices Phil Pemberton
` (13 preceding siblings ...)
2026-10-09 15:55 ` [PATCH v10 14/15] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
@ 2026-10-09 15:55 ` Phil Pemberton
14 siblings, 0 replies; 18+ messages in thread
From: Phil Pemberton @ 2026-10-09 15:55 UTC (permalink / raw)
To: linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen, Hannes Reinecke,
Phil Pemberton, Martin K . Petersen
The COMPAQ PD-1 and Panasonic LF-1195C combine a CD-ROM on LUN 0 with
a phase-change rewritable (PD) drive on LUN 1.
Add a COMPAQ PD-1 quirk with BLIST_FORCELUN to enable multi-LUN scanning
and BLIST_SINGLELUN to serialize commands across LUNs. Set BLIST_NO_LUN_1F
to skip unpopulated LUNs, which report PQ=0/PDT=0x1f.
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Assisted-by: LLM
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/scsi_devinfo.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 5e37461e61e8..f9196b0d6f4d 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -150,6 +150,8 @@ static struct {
{"COMPAQ", "MSA1000", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD},
{"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD},
{"COMPAQ", "HSV110", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
+ {"COMPAQ", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN |
+ BLIST_NO_LUN_1F},
{"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN},
{"DEC", "HSG80", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD},
{"DELL", "PV660F", NULL, BLIST_SPARSELUN},
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread