* [PATCH v6 1/6] ata: libata-scsi: add atapi_max_lun module parameter
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-08 21:34 ` [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
` (4 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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
Until now libata has hard-coded shost->max_lun = 1 for every ATA host,
so the SCSI layer never scans past LUN 0. This blocks support for
the small handful of multi-LUN ATAPI devices (Panasonic LF-1195C and
COMPAQ PD-1 PD/CD combos export CD on LUN 0 and PD on LUN 1; old
Nakamichi MJ-x.y CD changers expose one LUN per disc slot, up to 7).
Introduce a libata module parameter, atapi_max_lun, that controls the
upper bound of the per-host SCSI LUN scan. Default is 1, preserving
current behaviour exactly: out-of-the-box only LUN 0 is scanned.
Range is clamped to 1..ATAPI_MAX_LUN (8, the SCSI-2 ceiling).
Subsequent patches gate actual LUN>0 probing on BLIST_FORCELUN, so a
device must both be on the SCSI device list (or carry the appropriate
quirk) and run on a host whose atapi_max_lun has been raised before
any extra LUNs are scanned.
Reviewed-by: Hannes Reinecke <hare@suse.de>
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 e76d15411e2a..4408b1fb48c7 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -122,6 +122,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,
+ "Maximum LUN to scan on ATAPI devices flagged BLIST_FORCELUN (1 [default] .. 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 f44612e269a4..32c6a0e497cf 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -4627,7 +4627,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 b5423b6e97de..96d804d02b99 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 5c085ef4eda7..3e33ee30628d 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -131,6 +131,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] 15+ messages in thread* [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
2026-06-08 21:34 ` [PATCH v6 1/6] ata: libata-scsi: add atapi_max_lun module parameter Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-09 7:22 ` Hannes Reinecke
2026-06-08 21:34 ` [PATCH v6 3/6] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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 (PD/CD combos, CD changers) share a single
ata_device but expose multiple scsi_devices. The previous single
dev->sdev pointer could only track one LUN, making all other LUNs
invisible to code that operates on sdevs: port detach, suspend/resume,
ACPI uevent, ZPODD, media change notification, and EH teardown.
Replace the scalar struct scsi_device *sdev with a fixed-size array
dev->sdev[ATAPI_MAX_LUN] indexed by LUN number, where ATAPI_MAX_LUN
is 8 (the SCSI-2 ceiling, LUN values 0..7). Add a companion field
dev->nr_luns recording the number of valid entries -- defaults to 1
during ata_dev_init() and is bumped during multi-LUN probe -- so the
common single-LUN case iterates one slot, not eight.
Add an inline helper ata_dev_scsi_device(dev, lun) that returns
dev->sdev[lun] guarded by a WARN_ON_ONCE(lun >= dev->nr_luns) bounds
check. Use it for the hardcoded LUN-0 references in libata-acpi
(uevent kobj), libata-zpodd (disk events, wake notify), and the
door-lock and OF-node paths in libata-scsi.
Key changes per call site:
- ata_scsi_dev_config: assign sdev to dev->sdev[sdev->lun]
- ata_scsi_sdev_destroy: clear dev->sdev[sdev->lun]; only trigger
ATA-level detach when LUN 0 is destroyed, since removing a higher
LUN should not tear down the underlying ATA device
- ata_port_detach: iterate dev->nr_luns slots (high->low)
- ata_scsi_offline_dev: iterate dev->nr_luns slots
- ata_scsi_remove_dev: snapshot and remove all LUN slots, then
scsi_remove_device each one outside the lock
- ata_scsi_media_change_notify: send event to all populated LUNs
- ata_scsi_dev_rescan: resume and rescan each populated LUN
- ACPI, ZPODD, ofnode, door-lock: use ata_dev_scsi_device(dev, 0)
For single-LUN devices (the vast majority) only dev->sdev[0] is ever
populated and dev->nr_luns stays at 1, so existing call paths see no
change in behaviour.
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-acpi.c | 6 +-
drivers/ata/libata-core.c | 11 ++-
drivers/ata/libata-scsi.c | 151 +++++++++++++++++++------------------
drivers/ata/libata-zpodd.c | 6 +-
include/linux/libata.h | 11 ++-
5 files changed, 103 insertions(+), 82 deletions(-)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 4433f626246b..8af35d0b1053 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -153,8 +153,10 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
char *envp[] = { event_string, NULL };
if (dev) {
- if (dev->sdev)
- kobj = &dev->sdev->sdev_gendev.kobj;
+ struct scsi_device *sdev = ata_dev_scsi_device(dev, 0);
+
+ if (sdev)
+ kobj = &sdev->sdev_gendev.kobj;
} else
kobj = &ap->dev->kobj;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 4408b1fb48c7..1cb159d9dbc7 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5564,6 +5564,7 @@ void ata_dev_init(struct ata_device *dev)
dev->pio_mask = UINT_MAX;
dev->mwdma_mask = UINT_MAX;
dev->udma_mask = UINT_MAX;
+ dev->nr_luns = 1;
}
/**
@@ -6275,11 +6276,15 @@ 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) {
+ int lun;
+
+ for (lun = dev->nr_luns - 1; lun >= 0; lun--) {
+ if (!dev->sdev[lun])
+ continue;
spin_unlock_irqrestore(ap->lock, flags);
- scsi_remove_device(dev->sdev);
+ scsi_remove_device(dev->sdev[lun]);
spin_lock_irqsave(ap->lock, flags);
- dev->sdev = NULL;
+ dev->sdev[lun] = NULL;
}
}
}
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 32c6a0e497cf..7c3d31dc49a1 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1131,7 +1131,7 @@ 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;
+ dev->sdev[sdev->lun] = sdev;
return 0;
}
@@ -1202,10 +1202,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 LUN (LUN 0) is destroyed,
+ * schedule ATA-level detach via EH.
*
* LOCKING:
* Defined by SCSI layer. We don't really care.
@@ -1220,11 +1220,12 @@ 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 && dev->sdev[sdev->lun] == sdev) {
+ dev->sdev[sdev->lun] = NULL;
+ if (sdev->lun == 0) {
+ dev->flags |= ATA_DFLAG_DETACH;
+ ata_port_schedule_eh(ap);
+ }
}
spin_unlock_irqrestore(ap->lock, flags);
@@ -2911,10 +2912,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
* avoid this infinite loop.
*
* This may happen before SCSI scan is complete. Make
- * sure qc->dev->sdev isn't NULL before dereferencing.
+ * sure the LUN-0 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) {
+ struct scsi_device *sdev =
+ ata_dev_scsi_device(qc->dev, 0);
+
+ if (sdev)
+ sdev->locked = 0;
+ }
ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
return;
@@ -4658,7 +4664,7 @@ 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)
{
- struct scsi_device *sdev = dev->sdev;
+ struct scsi_device *sdev = ata_dev_scsi_device(dev, 0);
struct device *d = ap->host->dev;
struct device_node *np = d->of_node;
struct device_node *child;
@@ -4696,7 +4702,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))
@@ -4707,11 +4713,11 @@ 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;
+ dev->sdev[0] = sdev;
ata_scsi_assign_ofnode(dev, ap);
scsi_device_put(sdev);
} else {
- dev->sdev = NULL;
+ dev->sdev[0] = NULL;
}
}
}
@@ -4722,7 +4728,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;
}
}
@@ -4763,7 +4769,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)
@@ -4773,11 +4779,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 = dev->nr_luns - 1; lun >= 0; lun--) {
+ if (dev->sdev[lun]) {
+ scsi_device_set_state(dev->sdev[lun], SDEV_OFFLINE);
+ found = true;
+ }
}
- return false;
+ return found;
}
/**
@@ -4793,49 +4804,38 @@ 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 = dev->nr_luns - 1; lun >= 0; lun--) {
+ struct scsi_device *sdev = dev->sdev[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 = dev->nr_luns - 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]);
}
}
@@ -4872,9 +4872,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 < dev->nr_luns; lun++)
+ if (dev->sdev[lun])
+ sdev_evt_send_simple(dev->sdev[lun],
+ SDEV_EVT_MEDIA_CHANGE, GFP_ATOMIC);
}
/**
@@ -5007,37 +5010,39 @@ void ata_scsi_dev_rescan(struct work_struct *work)
ata_for_each_link(link, ap, EDGE) {
ata_for_each_dev(dev, link, ENABLED) {
- struct scsi_device *sdev = dev->sdev;
+ 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;
-
do_resume = dev->flags & ATA_DFLAG_RESUMING;
- spin_unlock_irqrestore(ap->lock, flags);
- if (do_resume) {
- ret = scsi_resume_device(sdev);
- if (ret == -EWOULDBLOCK) {
- scsi_device_put(sdev);
- goto unlock_scan;
+ for (lun = 0; lun < dev->nr_luns; lun++) {
+ struct scsi_device *sdev = dev->sdev[lun];
+
+ if (!sdev)
+ continue;
+ if (scsi_device_get(sdev))
+ continue;
+
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (do_resume) {
+ ret = scsi_resume_device(sdev);
+ if (ret == -EWOULDBLOCK) {
+ scsi_device_put(sdev);
+ goto unlock_scan;
+ }
}
- 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;
}
- ret = scsi_rescan_device(sdev);
- scsi_device_put(sdev);
- spin_lock_irqsave(ap->lock, flags);
- if (ret)
- goto unlock_ap;
+ if (do_resume)
+ dev->flags &= ~ATA_DFLAG_RESUMING;
}
}
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index 414e7c63bd85..dca774d8ec05 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -185,7 +185,7 @@ void zpodd_enable_run_wake(struct ata_device *dev)
{
struct zpodd *zpodd = dev->zpodd;
- sdev_disable_disk_events(dev->sdev);
+ sdev_disable_disk_events(ata_dev_scsi_device(dev, 0));
zpodd->powered_off = true;
acpi_pm_set_device_wakeup(&dev->tdev, true);
@@ -233,14 +233,14 @@ void zpodd_post_poweron(struct ata_device *dev)
zpodd->zp_sampled = false;
zpodd->zp_ready = false;
- sdev_enable_disk_events(dev->sdev);
+ sdev_enable_disk_events(ata_dev_scsi_device(dev, 0));
}
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 device *dev = &ata_dev_scsi_device(ata_dev, 0)->sdev_gendev;
if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
zpodd->from_notify = true;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 3e33ee30628d..5db8a2e3f051 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -722,7 +722,8 @@ 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 */
+ unsigned int nr_luns; /* valid entries in sdev[] */
void *private_data;
#ifdef CONFIG_ATA_ACPI
union acpi_object *gtf_cache;
@@ -1715,6 +1716,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 >= dev->nr_luns))
+ return NULL;
+ return dev->sdev[lun];
+}
+
/*
* link helpers
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array
2026-06-08 21:34 ` [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
@ 2026-06-09 7:22 ` Hannes Reinecke
2026-06-10 23:14 ` Phil Pemberton
0 siblings, 1 reply; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-09 7:22 UTC (permalink / raw)
To: Phil Pemberton, linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen
On 6/8/26 23:34, Phil Pemberton wrote:
> Multi-LUN ATAPI devices (PD/CD combos, CD changers) share a single
> ata_device but expose multiple scsi_devices. The previous single
> dev->sdev pointer could only track one LUN, making all other LUNs
> invisible to code that operates on sdevs: port detach, suspend/resume,
> ACPI uevent, ZPODD, media change notification, and EH teardown.
>
> Replace the scalar struct scsi_device *sdev with a fixed-size array
> dev->sdev[ATAPI_MAX_LUN] indexed by LUN number, where ATAPI_MAX_LUN
> is 8 (the SCSI-2 ceiling, LUN values 0..7). Add a companion field
> dev->nr_luns recording the number of valid entries -- defaults to 1
> during ata_dev_init() and is bumped during multi-LUN probe -- so the
> common single-LUN case iterates one slot, not eight.
>
> Add an inline helper ata_dev_scsi_device(dev, lun) that returns
> dev->sdev[lun] guarded by a WARN_ON_ONCE(lun >= dev->nr_luns) bounds
> check. Use it for the hardcoded LUN-0 references in libata-acpi
> (uevent kobj), libata-zpodd (disk events, wake notify), and the
> door-lock and OF-node paths in libata-scsi.
>
> Key changes per call site:
> - ata_scsi_dev_config: assign sdev to dev->sdev[sdev->lun]
> - ata_scsi_sdev_destroy: clear dev->sdev[sdev->lun]; only trigger
> ATA-level detach when LUN 0 is destroyed, since removing a higher
> LUN should not tear down the underlying ATA device
> - ata_port_detach: iterate dev->nr_luns slots (high->low)
> - ata_scsi_offline_dev: iterate dev->nr_luns slots
> - ata_scsi_remove_dev: snapshot and remove all LUN slots, then
> scsi_remove_device each one outside the lock
> - ata_scsi_media_change_notify: send event to all populated LUNs
> - ata_scsi_dev_rescan: resume and rescan each populated LUN
> - ACPI, ZPODD, ofnode, door-lock: use ata_dev_scsi_device(dev, 0)
>
> For single-LUN devices (the vast majority) only dev->sdev[0] is ever
> populated and dev->nr_luns stays at 1, so existing call paths see no
> change in behaviour.
>
> Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
> ---
> drivers/ata/libata-acpi.c | 6 +-
> drivers/ata/libata-core.c | 11 ++-
> drivers/ata/libata-scsi.c | 151 +++++++++++++++++++------------------
> drivers/ata/libata-zpodd.c | 6 +-
> include/linux/libata.h | 11 ++-
> 5 files changed, 103 insertions(+), 82 deletions(-)
>
> diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
> index 4433f626246b..8af35d0b1053 100644
> --- a/drivers/ata/libata-acpi.c
> +++ b/drivers/ata/libata-acpi.c
> @@ -153,8 +153,10 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev,
> char *envp[] = { event_string, NULL };
>
> if (dev) {
> - if (dev->sdev)
> - kobj = &dev->sdev->sdev_gendev.kobj;
> + struct scsi_device *sdev = ata_dev_scsi_device(dev, 0);
> +
> + if (sdev)
> + kobj = &sdev->sdev_gendev.kobj;
> } else
> kobj = &ap->dev->kobj;
>
> diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
> index 4408b1fb48c7..1cb159d9dbc7 100644
> --- a/drivers/ata/libata-core.c
> +++ b/drivers/ata/libata-core.c
> @@ -5564,6 +5564,7 @@ void ata_dev_init(struct ata_device *dev)
> dev->pio_mask = UINT_MAX;
> dev->mwdma_mask = UINT_MAX;
> dev->udma_mask = UINT_MAX;
> + dev->nr_luns = 1;
> }
>
> /**
> @@ -6275,11 +6276,15 @@ 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) {
> + int lun;
> +
> + for (lun = dev->nr_luns - 1; lun >= 0; lun--) {
> + if (!dev->sdev[lun])
> + continue;
> spin_unlock_irqrestore(ap->lock, flags);
> - scsi_remove_device(dev->sdev);
> + scsi_remove_device(dev->sdev[lun]);
> spin_lock_irqsave(ap->lock, flags);
> - dev->sdev = NULL;
> + dev->sdev[lun] = NULL;
As pointed out by sashiko, this is racy.
Please move 'dev->sdev[lun] = NULL' before unlock, and hold
'sdev' in a temporary variable.
Maybe even make this a separate patch, then this patch can be kept
as just the interface change.
> }
> }
> }
> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
> index 32c6a0e497cf..7c3d31dc49a1 100644
> --- a/drivers/ata/libata-scsi.c
> +++ b/drivers/ata/libata-scsi.c
> @@ -1131,7 +1131,7 @@ 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;
> + dev->sdev[sdev->lun] = sdev;
> return 0;
> }
>
> @@ -1202,10 +1202,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 LUN (LUN 0) is destroyed,
> + * schedule ATA-level detach via EH.
> *
> * LOCKING:
> * Defined by SCSI layer. We don't really care.
> @@ -1220,11 +1220,12 @@ 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 && dev->sdev[sdev->lun] == sdev) {
> + dev->sdev[sdev->lun] = NULL;
> + if (sdev->lun == 0) {
> + dev->flags |= ATA_DFLAG_DETACH;
> + ata_port_schedule_eh(ap);
> + }
> }
> spin_unlock_irqrestore(ap->lock, flags);
>
> @@ -2911,10 +2912,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
> * avoid this infinite loop.
> *
> * This may happen before SCSI scan is complete. Make
> - * sure qc->dev->sdev isn't NULL before dereferencing.
> + * sure the LUN-0 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) {
> + struct scsi_device *sdev =
> + ata_dev_scsi_device(qc->dev, 0);
> +
> + if (sdev)
> + sdev->locked = 0;
> + }
>
> ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
> return;
> @@ -4658,7 +4664,7 @@ 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)
> {
> - struct scsi_device *sdev = dev->sdev;
> + struct scsi_device *sdev = ata_dev_scsi_device(dev, 0);
> struct device *d = ap->host->dev;
> struct device_node *np = d->of_node;
> struct device_node *child;
> @@ -4696,7 +4702,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))
> @@ -4707,11 +4713,11 @@ 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;
> + dev->sdev[0] = sdev;
> ata_scsi_assign_ofnode(dev, ap);
> scsi_device_put(sdev);
> } else {
> - dev->sdev = NULL;
> + dev->sdev[0] = NULL;
> }
> }
> }
> @@ -4722,7 +4728,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;
> }
> }
> @@ -4763,7 +4769,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)
> @@ -4773,11 +4779,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 = dev->nr_luns - 1; lun >= 0; lun--) {
> + if (dev->sdev[lun]) {
> + scsi_device_set_state(dev->sdev[lun], SDEV_OFFLINE);
> + found = true;
> + }
> }
> - return false;
> + return found;
> }
>
> /**
> @@ -4793,49 +4804,38 @@ 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 = dev->nr_luns - 1; lun >= 0; lun--) {
> + struct scsi_device *sdev = dev->sdev[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 = dev->nr_luns - 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]);
> }> }
>
Wouldn't it be simpler to have another mutex under 'dev' to protect
'dev->sdev[]' ?
That would get us out of this mess, and we could do away with the
temporary adev array.
> @@ -4872,9 +4872,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 < dev->nr_luns; lun++)
> + if (dev->sdev[lun])
> + sdev_evt_send_simple(dev->sdev[lun],
> + SDEV_EVT_MEDIA_CHANGE, GFP_ATOMIC);
> }
I guess the iteration need to be protected somehow, either by
taking 'ap->lock' or with the dedicated mutex from the above
comments.
>
> /**
> @@ -5007,37 +5010,39 @@ void ata_scsi_dev_rescan(struct work_struct *work)
>
> ata_for_each_link(link, ap, EDGE) {
> ata_for_each_dev(dev, link, ENABLED) {
> - struct scsi_device *sdev = dev->sdev;
> + 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;
> -
> do_resume = dev->flags & ATA_DFLAG_RESUMING;
>
> - spin_unlock_irqrestore(ap->lock, flags);
> - if (do_resume) {
> - ret = scsi_resume_device(sdev);
> - if (ret == -EWOULDBLOCK) {
> - scsi_device_put(sdev);
> - goto unlock_scan;
> + for (lun = 0; lun < dev->nr_luns; lun++) {
> + struct scsi_device *sdev = dev->sdev[lun];
> +
> + if (!sdev)
> + continue;
> + if (scsi_device_get(sdev))
> + continue;
> +
> + spin_unlock_irqrestore(ap->lock, flags);
> + if (do_resume) {
> + ret = scsi_resume_device(sdev);
> + if (ret == -EWOULDBLOCK) {
> + scsi_device_put(sdev);
> + goto unlock_scan;
> + }
> }> - 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;
> }
> - ret = scsi_rescan_device(sdev);
> - scsi_device_put(sdev);
> - spin_lock_irqsave(ap->lock, flags);
>
> - if (ret)
> - goto unlock_ap;
> + if (do_resume)
> + dev->flags &= ~ATA_DFLAG_RESUMING;
> }
> }
>
> diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
> index 414e7c63bd85..dca774d8ec05 100644
> --- a/drivers/ata/libata-zpodd.c
> +++ b/drivers/ata/libata-zpodd.c
> @@ -185,7 +185,7 @@ void zpodd_enable_run_wake(struct ata_device *dev)
> {
> struct zpodd *zpodd = dev->zpodd;
>
> - sdev_disable_disk_events(dev->sdev);
> + sdev_disable_disk_events(ata_dev_scsi_device(dev, 0));
I _think_ we should call this for every LUN.
>
> zpodd->powered_off = true;
> acpi_pm_set_device_wakeup(&dev->tdev, true);
> @@ -233,14 +233,14 @@ void zpodd_post_poweron(struct ata_device *dev)
> zpodd->zp_sampled = false;
> zpodd->zp_ready = false;
>
> - sdev_enable_disk_events(dev->sdev);
> + sdev_enable_disk_events(ata_dev_scsi_device(dev, 0));
Same here.
> }
>
> 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 device *dev = &ata_dev_scsi_device(ata_dev, 0)->sdev_gendev;
And here.
>
> if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
> zpodd->from_notify = true;
> diff --git a/include/linux/libata.h b/include/linux/libata.h
> index 3e33ee30628d..5db8a2e3f051 100644
> --- a/include/linux/libata.h
> +++ b/include/linux/libata.h
> @@ -722,7 +722,8 @@ 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 */
> + unsigned int nr_luns; /* valid entries in sdev[] */
> void *private_data;
> #ifdef CONFIG_ATA_ACPI
> union acpi_object *gtf_cache;
> @@ -1715,6 +1716,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 >= dev->nr_luns))
> + return NULL;
> + return dev->sdev[lun];
> +}
> +
> /*
> * link helpers
> */
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array
2026-06-09 7:22 ` Hannes Reinecke
@ 2026-06-10 23:14 ` Phil Pemberton
0 siblings, 0 replies; 15+ messages in thread
From: Phil Pemberton @ 2026-06-10 23:14 UTC (permalink / raw)
To: Hannes Reinecke, linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen
On 09/06/2026 08:22, Hannes Reinecke wrote:
>> + for (lun = dev->nr_luns - 1; lun >= 0; lun--) {
>> + if (!dev->sdev[lun])
>> + continue;
>> spin_unlock_irqrestore(ap->lock, flags);
>> - scsi_remove_device(dev->sdev);
>> + scsi_remove_device(dev->sdev[lun]);
>> spin_lock_irqsave(ap->lock, flags);
>> - dev->sdev = NULL;
>> + dev->sdev[lun] = NULL;
>
> As pointed out by sashiko, this is racy.
> Please move 'dev->sdev[lun] = NULL' before unlock, and hold
> 'sdev' in a temporary variable.
>
> Maybe even make this a separate patch, then this patch can be kept
> as just the interface change.
The race was introduced by the patch itself and only exists because v6
got the NULL placement wrong. I think splitting it out would leave patch
2 transiently broken, which would make bisecting harder not easier.
IMHO a series where every commit is correct is more useful than one with
a known unfixed bug. The fix is only 3 lines and I've kept it in patch 2
where the cause and correction sit near each other and are easy to review.
I'm happy to split it if you feel strongly about it - or was this
thinking out loud?
>> - scsi_remove_device(sdev);
>> - scsi_device_put(sdev);
>> + dev_name(&sdevs[lun]->sdev_gendev));
>> + scsi_remove_device(sdevs[lun]);
>> + scsi_device_put(sdevs[lun]);
> > }> }
> Wouldn't it be simpler to have another mutex under 'dev' to protect
> 'dev->sdev[]' ?
> That would get us out of this mess, and we could do away with the
> temporary adev array.
I looked at that, but dev->sdev[] is also accessed from IRQ context:
ata_scsi_media_change_notify runs under ap->lock with IRQs disabled, and
so does ata_scsi_offline_dev. A mutex would only protect sleepable
paths, leaving the IRQ paths unprotected. That feels worse to me than
the current consistent use of ap->lock for all accesses.
The snapshot approach in ata_scsi_remove_dev is boilerplate but it is
correct.
If you think a mutex-based redesign is the way to go, I'm happy to look
at it as a follow-up.
>> @@ -4872,9 +4872,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 < dev->nr_luns; lun++)
>> + if (dev->sdev[lun])
>> + sdev_evt_send_simple(dev->sdev[lun],
>> + SDEV_EVT_MEDIA_CHANGE, GFP_ATOMIC);
>> }
>
> I guess the iteration need to be protected somehow, either by
> taking 'ap->lock' or with the dedicated mutex from the above
> comments.
Both call sites are inside sata_async_notification(). That's documented
as "LOCKING: spin_lock_irqsave(host lock)", so ap->lock is already held
and the iteration is protected - QED.
>> diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
>> index 414e7c63bd85..dca774d8ec05 100644
>> --- a/drivers/ata/libata-zpodd.c
>> +++ b/drivers/ata/libata-zpodd.c
>> @@ -185,7 +185,7 @@ void zpodd_enable_run_wake(struct ata_device *dev)
>> {
>> struct zpodd *zpodd = dev->zpodd;
>> - sdev_disable_disk_events(dev->sdev);
>> + sdev_disable_disk_events(ata_dev_scsi_device(dev, 0));
>
> I _think_ we should call this for every LUN.
Done in v7, all three ZPODD functions (enable_run_wake, post_poweron,
wake_dev) iterate over all LUN slots.
Thanks,
--
Phil.
philpem@philpem.me.uk
https://www.philpem.me.uk/
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 3/6] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
2026-06-08 21:34 ` [PATCH v6 1/6] ata: libata-scsi: add atapi_max_lun module parameter Phil Pemberton
2026-06-08 21:34 ` [PATCH v6 2/6] ata: libata-scsi: convert dev->sdev to per-LUN array Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-09 7:24 ` Hannes Reinecke
2026-06-08 21:34 ` [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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
Two changes are required to route commands to ATAPI LUNs other than 0:
1. __ata_scsi_find_dev(): The existing code rejects any scsi_device
with a non-zero LUN, returning NULL and dropping the command on
the floor. Hoist a non-zero LUN early-exit ahead of the original
channel/id checks: when scsidev->lun is non-zero, allow it through
only if the underlying ata_device is ATAPI class. The original
LUN-0 path is left structurally unchanged.
2. atapi_xlat(): Older ATAPI devices (SCSI-2 era) expect the LUN in
CDB byte 1 bits 7:5 rather than relying on transport-level LUN
addressing. Encode scmd->device->lun into those bits, preserving
the existing command-specific bits in 4:0. This is required by
both the Panasonic PD/CD combos and Nakamichi CD changers.
The SCSI layer caps the LUN at shost->max_lun, so a value beyond
the device's nr_luns should never reach this point; guard with
WARN_ON_ONCE() and return AC_ERR_INVALID if it does, since the
3-bit CDB field cannot represent it.
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-scsi.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 7c3d31dc49a1..2d714efc855f 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2953,6 +2953,15 @@ 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).
+ * The SCSI layer caps the LUN at shost->max_lun (<= ATAPI_MAX_LUN),
+ * so this should never trip; warn and reject if it does.
+ */
+ if (WARN_ON_ONCE(scmd->device->lun >= dev->nr_luns))
+ return AC_ERR_INVALID;
+ 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;
@@ -3063,6 +3072,29 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
{
int devno;
+ /*
+ * Non-zero LUN is only legal for ATAPI devices, since they can
+ * legitimately expose more than one LUN (PD/CD combos, CD changers).
+ * Handle that case up front so the LUN-0 path below stays unchanged.
+ */
+ if (unlikely(scsidev->lun)) {
+ struct ata_device *dev;
+
+ if (!sata_pmp_attached(ap)) {
+ if (unlikely(scsidev->channel))
+ return NULL;
+ devno = scsidev->id;
+ } else {
+ if (unlikely(scsidev->id))
+ return NULL;
+ devno = scsidev->channel;
+ }
+ dev = ata_find_dev(ap, devno);
+ if (!dev || dev->class != ATA_DEV_ATAPI)
+ return NULL;
+ return dev;
+ }
+
/* skip commands not addressed to targets we simulate */
if (!sata_pmp_attached(ap)) {
if (unlikely(scsidev->channel || scsidev->lun))
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v6 3/6] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI
2026-06-08 21:34 ` [PATCH v6 3/6] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
@ 2026-06-09 7:24 ` Hannes Reinecke
0 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-09 7:24 UTC (permalink / raw)
To: Phil Pemberton, linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen
On 6/8/26 23:34, Phil Pemberton wrote:
> Two changes are required to route commands to ATAPI LUNs other than 0:
>
> 1. __ata_scsi_find_dev(): The existing code rejects any scsi_device
> with a non-zero LUN, returning NULL and dropping the command on
> the floor. Hoist a non-zero LUN early-exit ahead of the original
> channel/id checks: when scsidev->lun is non-zero, allow it through
> only if the underlying ata_device is ATAPI class. The original
> LUN-0 path is left structurally unchanged.
>
> 2. atapi_xlat(): Older ATAPI devices (SCSI-2 era) expect the LUN in
> CDB byte 1 bits 7:5 rather than relying on transport-level LUN
> addressing. Encode scmd->device->lun into those bits, preserving
> the existing command-specific bits in 4:0. This is required by
> both the Panasonic PD/CD combos and Nakamichi CD changers.
>
> The SCSI layer caps the LUN at shost->max_lun, so a value beyond
> the device's nr_luns should never reach this point; guard with
> WARN_ON_ONCE() and return AC_ERR_INVALID if it does, since the
> 3-bit CDB field cannot represent it.
>
> Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
> ---
> drivers/ata/libata-scsi.c | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
` (2 preceding siblings ...)
2026-06-08 21:34 ` [PATCH v6 3/6] ata: libata-scsi: route non-zero LUN commands for multi-LUN ATAPI Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-09 7:24 ` Hannes Reinecke
2026-06-16 1:09 ` Martin K. Petersen
2026-06-08 21:34 ` [PATCH v6 5/6] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
2026-06-08 21:34 ` [PATCH v6 6/6] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton
5 siblings, 2 replies; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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 respond to INQUIRY on unpopulated LUNs with
PQ=0 / PDT=0x1f instead of the standard PQ=3. The SCSI scan layer
normally adds such devices (PQ=0 means "connected"), producing
spurious "No Device" entries.
The scsi_target field pdt_1f_for_no_lun already exists to suppress
this, but was previously only set by the USB UFI driver.
Add BLIST_NO_LUN_1F so the flag can be set per-device from
scsi_devinfo, and wire it up in scsi_add_lun() to set
starget->pdt_1f_for_no_lun from the blacklist flags. This runs
during LUN 0 processing, before the sequential LUN scan probes
higher LUNs.
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/scsi/scsi_scan.c | 2 ++
include/scsi/scsi_devinfo.h | 6 +++---
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index ef22a4228b85..bfbbf9be05d2 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1069,6 +1069,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
transport_configure_device(&sdev->sdev_gendev);
sdev->sdev_bflags = *bflags;
+ if (sdev->sdev_bflags & BLIST_NO_LUN_1F)
+ sdev->sdev_target->pdt_1f_for_no_lun = 1;
if (scsi_device_is_pseudo_dev(sdev))
return SCSI_SCAN_LUN_PRESENT;
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] 15+ messages in thread* Re: [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-06-08 21:34 ` [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
@ 2026-06-09 7:24 ` Hannes Reinecke
2026-06-16 1:09 ` Martin K. Petersen
1 sibling, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-09 7:24 UTC (permalink / raw)
To: Phil Pemberton, linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen
On 6/8/26 23:34, Phil Pemberton wrote:
> Some multi-LUN devices respond to INQUIRY on unpopulated LUNs with
> PQ=0 / PDT=0x1f instead of the standard PQ=3. The SCSI scan layer
> normally adds such devices (PQ=0 means "connected"), producing
> spurious "No Device" entries.
>
> The scsi_target field pdt_1f_for_no_lun already exists to suppress
> this, but was previously only set by the USB UFI driver.
>
> Add BLIST_NO_LUN_1F so the flag can be set per-device from
> scsi_devinfo, and wire it up in scsi_add_lun() to set
> starget->pdt_1f_for_no_lun from the blacklist flags. This runs
> during LUN 0 processing, before the sequential LUN scan probes
> higher LUNs.
>
> Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
> ---
> drivers/scsi/scsi_scan.c | 2 ++
> include/scsi/scsi_devinfo.h | 6 +++---
> 2 files changed, 5 insertions(+), 3 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-06-08 21:34 ` [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
2026-06-09 7:24 ` Hannes Reinecke
@ 2026-06-16 1:09 ` Martin K. Petersen
2026-06-17 12:23 ` Niklas Cassel
1 sibling, 1 reply; 15+ messages in thread
From: Martin K. Petersen @ 2026-06-16 1:09 UTC (permalink / raw)
To: Phil Pemberton
Cc: linux-ide, linux-scsi, linux-kernel, Damien Le Moal,
Niklas Cassel, James E . J . Bottomley, Martin K . Petersen,
Hannes Reinecke
Phil,
> Some multi-LUN devices respond to INQUIRY on unpopulated LUNs with
> PQ=0 / PDT=0x1f instead of the standard PQ=3. The SCSI scan layer
> normally adds such devices (PQ=0 means "connected"), producing
> spurious "No Device" entries.
>
> The scsi_target field pdt_1f_for_no_lun already exists to suppress
> this, but was previously only set by the USB UFI driver.
>
> Add BLIST_NO_LUN_1F so the flag can be set per-device from
> scsi_devinfo, and wire it up in scsi_add_lun() to set
> starget->pdt_1f_for_no_lun from the blacklist flags. This runs
> during LUN 0 processing, before the sequential LUN scan probes
> higher LUNs.
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
--
Martin K. Petersen
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag
2026-06-16 1:09 ` Martin K. Petersen
@ 2026-06-17 12:23 ` Niklas Cassel
0 siblings, 0 replies; 15+ messages in thread
From: Niklas Cassel @ 2026-06-17 12:23 UTC (permalink / raw)
To: Martin K. Petersen
Cc: Phil Pemberton, linux-ide, linux-scsi, linux-kernel,
Damien Le Moal, James E . J . Bottomley, Hannes Reinecke
Hello Martin,
On Mon, Jun 15, 2026 at 09:09:20PM -0400, Martin K. Petersen wrote:
>
> Phil,
>
> > Some multi-LUN devices respond to INQUIRY on unpopulated LUNs with
> > PQ=0 / PDT=0x1f instead of the standard PQ=3. The SCSI scan layer
> > normally adds such devices (PQ=0 means "connected"), producing
> > spurious "No Device" entries.
> >
> > The scsi_target field pdt_1f_for_no_lun already exists to suppress
> > this, but was previously only set by the USB UFI driver.
> >
> > Add BLIST_NO_LUN_1F so the flag can be set per-device from
> > scsi_devinfo, and wire it up in scsi_add_lun() to set
> > starget->pdt_1f_for_no_lun from the blacklist flags. This runs
> > during LUN 0 processing, before the sequential LUN scan probes
> > higher LUNs.
>
> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Thank you for reviewing!
There was already a v7 out when your sent your review:
https://lore.kernel.org/linux-ide/20260611024356.2769320-1-philpem@philpem.me.uk/T/#t
It looks like this patch [4/6] was modified in v7.
Patch [6/6] appears unmodified.
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 5/6] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
` (3 preceding siblings ...)
2026-06-08 21:34 ` [PATCH v6 4/6] scsi: add BLIST_NO_LUN_1F blacklist flag Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-09 7:25 ` Hannes Reinecke
2026-06-08 21:34 ` [PATCH v6 6/6] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton
5 siblings, 1 reply; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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 LUN 0 is added for an ATAPI device, check its BLIST_FORCELUN
flag. If set, bump dev->nr_luns to the host's max_lun so the LUN
routing in atapi_xlat() accepts the probe INQUIRYs, then call
scsi_scan_target() with SCAN_WILD_CARD to trigger the SCSI layer's
built-in sequential LUN scan for that target only. This probes
LUNs 1..shost->max_lun, driven by the libata atapi_max_lun module
parameter.
Devices without BLIST_FORCELUN (the vast majority of ATAPI devices)
are left with only LUN 0 -- no sequential scan is triggered, so
single-LUN devices like the iHAS124 DVD writer are completely
unaffected.
Non-responding LUNs (PQ=0/PDT=0x1f) are silently skipped by
scsi_probe_and_add_lun() when BLIST_NO_LUN_1F is set on the device
via scsi_devinfo.
Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
---
drivers/ata/libata-scsi.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 2d714efc855f..a6f5557014c7 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>
@@ -4745,12 +4746,30 @@ 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[0] = sdev;
- ata_scsi_assign_ofnode(dev, ap);
+ /*
+ * For multi-LUN ATAPI (BLIST_FORCELUN), bump
+ * dev->nr_luns to the host max so the LUN
+ * routing in atapi_xlat() accepts the probe
+ * INQUIRYs to LUN > 0, then trigger the
+ * sequential scan. pdt_1f_for_no_lun, set
+ * during LUN 0 configure, ensures
+ * non-responding LUNs are silently skipped;
+ * dev->sdev[] is populated by
+ * ata_scsi_dev_config() during the scan.
+ */
+ if (dev->class == ATA_DEV_ATAPI &&
+ sdev->sdev_bflags & BLIST_FORCELUN) {
+ dev->nr_luns = ap->scsi_host->max_lun;
+ scsi_scan_target(
+ &ap->scsi_host->shost_gendev,
+ channel, id, SCAN_WILD_CARD,
+ SCSI_SCAN_RESCAN);
+ }
scsi_device_put(sdev);
- } else {
- dev->sdev[0] = NULL;
}
+
+ if (dev->sdev[0])
+ ata_scsi_assign_ofnode(dev, ap);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH v6 5/6] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices
2026-06-08 21:34 ` [PATCH v6 5/6] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
@ 2026-06-09 7:25 ` Hannes Reinecke
0 siblings, 0 replies; 15+ messages in thread
From: Hannes Reinecke @ 2026-06-09 7:25 UTC (permalink / raw)
To: Phil Pemberton, linux-ide, linux-scsi
Cc: linux-kernel, Damien Le Moal, Niklas Cassel,
James E . J . Bottomley, Martin K . Petersen
On 6/8/26 23:34, Phil Pemberton wrote:
> After LUN 0 is added for an ATAPI device, check its BLIST_FORCELUN
> flag. If set, bump dev->nr_luns to the host's max_lun so the LUN
> routing in atapi_xlat() accepts the probe INQUIRYs, then call
> scsi_scan_target() with SCAN_WILD_CARD to trigger the SCSI layer's
> built-in sequential LUN scan for that target only. This probes
> LUNs 1..shost->max_lun, driven by the libata atapi_max_lun module
> parameter.
>
> Devices without BLIST_FORCELUN (the vast majority of ATAPI devices)
> are left with only LUN 0 -- no sequential scan is triggered, so
> single-LUN devices like the iHAS124 DVD writer are completely
> unaffected.
>
> Non-responding LUNs (PQ=0/PDT=0x1f) are silently skipped by
> scsi_probe_and_add_lun() when BLIST_NO_LUN_1F is set on the device
> via scsi_devinfo.
>
> Signed-off-by: Phil Pemberton <philpem@philpem.me.uk>
> ---
> drivers/ata/libata-scsi.c | 27 +++++++++++++++++++++++----
> 1 file changed, 23 insertions(+), 4 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v6 6/6] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk
2026-06-08 21:34 [PATCH v6 0/6] ata: libata-scsi: multi-LUN ATAPI device support Phil Pemberton
` (4 preceding siblings ...)
2026-06-08 21:34 ` [PATCH v6 5/6] ata: libata-scsi: probe additional LUNs for multi-LUN ATAPI devices Phil Pemberton
@ 2026-06-08 21:34 ` Phil Pemberton
2026-06-16 1:08 ` Martin K. Petersen
5 siblings, 1 reply; 15+ messages in thread
From: Phil Pemberton @ 2026-06-08 21:34 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
The COMPAQ PD-1 (OEM Panasonic/Matsushita LF-1195C) is a PD/CD combo
drive that exposes two ATAPI LUNs: LUN 0 is a CD-ROM (TYPE_ROM),
LUN 1 is a 650 MB PD (TYPE_DISK).
Add it to the SCSI device list with:
- BLIST_FORCELUN: tells the SCSI layer to scan past LUN 0
- BLIST_SINGLELUN: serialises commands across the two LUNs, since
the drive has a single transport and cannot handle concurrent
operations on both
- BLIST_NO_LUN_1F: the drive returns PQ=0/PDT=0x1f for unpopulated
LUNs instead of PQ=3; this flag tells scsi_probe_and_add_lun()
to silently skip them
The INQUIRY strings as reported by the device are:
Vendor: "COMPAQ " (T10 format, space-padded)
Product: "PD-1"
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
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 68a992494b12..bfc2cbd43897 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] 15+ messages in thread* Re: [PATCH v6 6/6] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk
2026-06-08 21:34 ` [PATCH v6 6/6] scsi: scsi_devinfo: add COMPAQ PD-1 multi-LUN ATAPI device quirk Phil Pemberton
@ 2026-06-16 1:08 ` Martin K. Petersen
0 siblings, 0 replies; 15+ messages in thread
From: Martin K. Petersen @ 2026-06-16 1:08 UTC (permalink / raw)
To: Phil Pemberton
Cc: linux-ide, linux-scsi, linux-kernel, Damien Le Moal,
Niklas Cassel, James E . J . Bottomley, Martin K . Petersen,
Hannes Reinecke
Phil,
> The COMPAQ PD-1 (OEM Panasonic/Matsushita LF-1195C) is a PD/CD combo
> drive that exposes two ATAPI LUNs: LUN 0 is a CD-ROM (TYPE_ROM),
> LUN 1 is a 650 MB PD (TYPE_DISK).
>
> Add it to the SCSI device list with:
> - BLIST_FORCELUN: tells the SCSI layer to scan past LUN 0
> - BLIST_SINGLELUN: serialises commands across the two LUNs, since
> the drive has a single transport and cannot handle concurrent
> operations on both
> - BLIST_NO_LUN_1F: the drive returns PQ=0/PDT=0x1f for unpopulated
> LUNs instead of PQ=3; this flag tells scsi_probe_and_add_lun()
> to silently skip them
>
> The INQUIRY strings as reported by the device are:
> Vendor: "COMPAQ " (T10 format, space-padded)
> Product: "PD-1"
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
--
Martin K. Petersen
^ permalink raw reply [flat|nested] 15+ messages in thread