* [PATCH 0/6] Cleanup and improve libata-scsi command emulation
@ 2024-10-22 2:45 Damien Le Moal
2024-10-22 2:45 ` [PATCH 1/6] ata: libata-scsi: Refactor ata_scsi_simulate() Damien Le Moal
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
The first 5 patches of this series refactor and cleanup the libata-scsi
code handling command emulation. There are no functional changes
introduced by these patches.
The last patch of the series (patch 6) improves the command emulation
by accurately handling the scsi command residual and transferring back
to the requester only the relevant data of a command reply.
Damien Le Moal (6):
ata: libata-scsi: Refactor ata_scsi_simulate()
ata: libata-scsi: Refactor ata_scsiop_read_cap()
ata: libata-scsi: Refactor ata_scsiop_maint_in()
ata: libata-scsi: Document all VPD page inquiry actors
ata: libata-scsi: Remove struct ata_scsi_args
ata: libata-scsi: Return residual for emulated SCSI commands
drivers/ata/libata-scsi.c | 503 +++++++++++++++++++++++---------------
1 file changed, 305 insertions(+), 198 deletions(-)
--
2.47.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/6] ata: libata-scsi: Refactor ata_scsi_simulate()
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-22 2:45 ` [PATCH 2/6] ata: libata-scsi: Refactor ata_scsiop_read_cap() Damien Le Moal
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
Factor out the code handling the INQUIRY command in ata_scsi_simulate()
using the function ata_scsi_rbuf_fill() with the new actor
ata_scsiop_inquiry(). This new actor function calls the existing actors
to handle the standard inquiry as well as extended inquiry (VPD page
access).
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 106 ++++++++++++++++++++++----------------
1 file changed, 63 insertions(+), 43 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index c97fc8dc270d..cc5bc47457d6 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1815,7 +1815,7 @@ static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
}
/**
- * ata_scsiop_inq_std - Simulate INQUIRY command
+ * ata_scsiop_inq_std - Simulate standard INQUIRY command
* @args: device IDENTIFY data / SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
@@ -2121,6 +2121,11 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
{
+ if (!(args->dev->flags & ATA_DFLAG_ZAC)) {
+ ata_scsi_set_invalid_field(args->dev, args->cmd, 2, 0xff);
+ return 1;
+ }
+
/*
* zbc-r05 SCSI Zoned Block device characteristics VPD page
*/
@@ -2145,6 +2150,11 @@ static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
u8 *desc = &rbuf[64];
int i;
+ if (!cpr_log) {
+ ata_scsi_set_invalid_field(args->dev, args->cmd, 2, 0xff);
+ return 1;
+ }
+
/* SCSI Concurrent Positioning Ranges VPD page: SBC-5 rev 1 or later */
rbuf[1] = 0xb9;
put_unaligned_be16(64 + (int)cpr_log->nr_cpr * 32 - 4, &rbuf[2]);
@@ -2159,6 +2169,57 @@ static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inquiry - Simulate INQUIRY command
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Returns data associated with an INQUIRY command output.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_inquiry(struct ata_scsi_args *args, u8 *rbuf)
+{
+ struct ata_device *dev = args->dev;
+ struct scsi_cmnd *cmd = args->cmd;
+ const u8 *scsicmd = cmd->cmnd;
+
+ /* is CmdDt set? */
+ if (scsicmd[1] & 2) {
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ return 1;
+ }
+
+ /* Is EVPD clear? */
+ if ((scsicmd[1] & 1) == 0)
+ return ata_scsiop_inq_std(args, rbuf);
+
+ switch (scsicmd[2]) {
+ case 0x00:
+ return ata_scsiop_inq_00(args, rbuf);
+ case 0x80:
+ return ata_scsiop_inq_80(args, rbuf);
+ case 0x83:
+ return ata_scsiop_inq_83(args, rbuf);
+ case 0x89:
+ return ata_scsiop_inq_89(args, rbuf);
+ case 0xb0:
+ return ata_scsiop_inq_b0(args, rbuf);
+ case 0xb1:
+ return ata_scsiop_inq_b1(args, rbuf);
+ case 0xb2:
+ return ata_scsiop_inq_b2(args, rbuf);
+ case 0xb6:
+ return ata_scsiop_inq_b6(args, rbuf);
+ case 0xb9:
+ return ata_scsiop_inq_b9(args, rbuf);
+ default:
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
+ return 1;
+ }
+}
+
/**
* modecpy - Prepare response for MODE SENSE
* @dest: output buffer
@@ -4263,48 +4324,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
switch(scsicmd[0]) {
case INQUIRY:
- if (scsicmd[1] & 2) /* is CmdDt set? */
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
- else switch (scsicmd[2]) {
- case 0x00:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00);
- break;
- case 0x80:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80);
- break;
- case 0x83:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
- break;
- case 0x89:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89);
- break;
- case 0xb0:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b0);
- break;
- case 0xb1:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b1);
- break;
- case 0xb2:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2);
- break;
- case 0xb6:
- if (dev->flags & ATA_DFLAG_ZAC)
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b6);
- else
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- case 0xb9:
- if (dev->cpr_log)
- ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b9);
- else
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- default:
- ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- break;
- }
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inquiry);
break;
case MODE_SENSE:
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/6] ata: libata-scsi: Refactor ata_scsiop_read_cap()
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
2024-10-22 2:45 ` [PATCH 1/6] ata: libata-scsi: Refactor ata_scsi_simulate() Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-22 2:45 ` [PATCH 3/6] ata: libata-scsi: Refactor ata_scsiop_maint_in() Damien Le Moal
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
Move the check for the scsi command service action being
SAI_READ_CAPACITY_16 from ata_scsi_simulate() into ata_scsiop_read_cap()
to simplify ata_scsi_simulate() for processing capacity reading commands
(READ_CAPACITY and SERVICE_ACTION_IN_16).
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 87 +++++++++++++++++++++------------------
1 file changed, 46 insertions(+), 41 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index cc5bc47457d6..8097cf318b04 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2579,6 +2579,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
{
struct ata_device *dev = args->dev;
+ u8 *scsicmd = args->cmd->cmnd;
u64 last_lba = dev->n_sectors - 1; /* LBA of the last block */
u32 sector_size; /* physical sector size in bytes */
u8 log2_per_phys;
@@ -2588,7 +2589,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
log2_per_phys = ata_id_log2_per_physical_sector(dev->id);
lowest_aligned = ata_id_logical_sector_offset(dev->id, log2_per_phys);
- if (args->cmd->cmnd[0] == READ_CAPACITY) {
+ if (scsicmd[0] == READ_CAPACITY) {
if (last_lba >= 0xffffffffULL)
last_lba = 0xffffffff;
@@ -2603,42 +2604,52 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
rbuf[5] = sector_size >> (8 * 2);
rbuf[6] = sector_size >> (8 * 1);
rbuf[7] = sector_size;
- } else {
- /* sector count, 64-bit */
- rbuf[0] = last_lba >> (8 * 7);
- rbuf[1] = last_lba >> (8 * 6);
- rbuf[2] = last_lba >> (8 * 5);
- rbuf[3] = last_lba >> (8 * 4);
- rbuf[4] = last_lba >> (8 * 3);
- rbuf[5] = last_lba >> (8 * 2);
- rbuf[6] = last_lba >> (8 * 1);
- rbuf[7] = last_lba;
- /* sector size */
- rbuf[ 8] = sector_size >> (8 * 3);
- rbuf[ 9] = sector_size >> (8 * 2);
- rbuf[10] = sector_size >> (8 * 1);
- rbuf[11] = sector_size;
-
- rbuf[12] = 0;
- rbuf[13] = log2_per_phys;
- rbuf[14] = (lowest_aligned >> 8) & 0x3f;
- rbuf[15] = lowest_aligned;
-
- if (ata_id_has_trim(args->id) &&
- !(dev->quirks & ATA_QUIRK_NOTRIM)) {
- rbuf[14] |= 0x80; /* LBPME */
-
- if (ata_id_has_zero_after_trim(args->id) &&
- dev->quirks & ATA_QUIRK_ZERO_AFTER_TRIM) {
- ata_dev_info(dev, "Enabling discard_zeroes_data\n");
- rbuf[14] |= 0x40; /* LBPRZ */
- }
+ return 0;
+ }
+
+ /*
+ * READ CAPACITY 16 command is defined as a service action
+ * (SERVICE_ACTION_IN_16 command).
+ */
+ if (scsicmd[0] != SERVICE_ACTION_IN_16 ||
+ (scsicmd[1] & 0x1f) != SAI_READ_CAPACITY_16) {
+ ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ return 1;
+ }
+
+ /* sector count, 64-bit */
+ rbuf[0] = last_lba >> (8 * 7);
+ rbuf[1] = last_lba >> (8 * 6);
+ rbuf[2] = last_lba >> (8 * 5);
+ rbuf[3] = last_lba >> (8 * 4);
+ rbuf[4] = last_lba >> (8 * 3);
+ rbuf[5] = last_lba >> (8 * 2);
+ rbuf[6] = last_lba >> (8 * 1);
+ rbuf[7] = last_lba;
+
+ /* sector size */
+ rbuf[ 8] = sector_size >> (8 * 3);
+ rbuf[ 9] = sector_size >> (8 * 2);
+ rbuf[10] = sector_size >> (8 * 1);
+ rbuf[11] = sector_size;
+
+ if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC)
+ rbuf[12] = (1 << 4); /* RC_BASIS */
+ rbuf[13] = log2_per_phys;
+ rbuf[14] = (lowest_aligned >> 8) & 0x3f;
+ rbuf[15] = lowest_aligned;
+
+ if (ata_id_has_trim(args->id) && !(dev->quirks & ATA_QUIRK_NOTRIM)) {
+ rbuf[14] |= 0x80; /* LBPME */
+
+ if (ata_id_has_zero_after_trim(args->id) &&
+ dev->quirks & ATA_QUIRK_ZERO_AFTER_TRIM) {
+ ata_dev_info(dev, "Enabling discard_zeroes_data\n");
+ rbuf[14] |= 0x40; /* LBPRZ */
}
- if (ata_id_zoned_cap(args->id) ||
- args->dev->class == ATA_DEV_ZAC)
- rbuf[12] = (1 << 4); /* RC_BASIS */
}
+
return 0;
}
@@ -4333,14 +4344,8 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
break;
case READ_CAPACITY:
- ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
- break;
-
case SERVICE_ACTION_IN_16:
- if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
- ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
- else
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
break;
case REPORT_LUNS:
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/6] ata: libata-scsi: Refactor ata_scsiop_maint_in()
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
2024-10-22 2:45 ` [PATCH 1/6] ata: libata-scsi: Refactor ata_scsi_simulate() Damien Le Moal
2024-10-22 2:45 ` [PATCH 2/6] ata: libata-scsi: Refactor ata_scsiop_read_cap() Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-22 2:45 ` [PATCH 4/6] ata: libata-scsi: Document all VPD page inquiry actors Damien Le Moal
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
Move the check for MI_REPORT_SUPPORTED_OPERATION_CODES from
ata_scsi_simulate() into ata_scsiop_maint_in() to simplify
ata_scsi_simulate() code.
Furthermore, since an rbuff fill actor function returning a non-zero
value causes no data to be returned for the command, directly return
an error (return 1) for invalid command formt after setting the invalid
field in cdb error.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 8097cf318b04..f9c70f650cfc 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3388,12 +3388,16 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
struct ata_device *dev = args->dev;
u8 *cdb = args->cmd->cmnd;
u8 supported = 0, cdlp = 0, rwcdlp = 0;
- unsigned int err = 0;
+
+ if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) {
+ ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ return 1;
+ }
if (cdb[2] != 1 && cdb[2] != 3) {
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
- err = 2;
- goto out;
+ ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ return 1;
}
switch (cdb[3]) {
@@ -3461,11 +3465,12 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
default:
break;
}
-out:
+
/* One command format */
rbuf[0] = rwcdlp;
rbuf[1] = cdlp | supported;
- return err;
+
+ return 0;
}
/**
@@ -4377,10 +4382,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
break;
case MAINTENANCE_IN:
- if ((scsicmd[1] & 0x1f) == MI_REPORT_SUPPORTED_OPERATION_CODES)
- ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
- else
- ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
+ ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
break;
/* all other commands */
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/6] ata: libata-scsi: Document all VPD page inquiry actors
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
` (2 preceding siblings ...)
2024-10-22 2:45 ` [PATCH 3/6] ata: libata-scsi: Refactor ata_scsiop_maint_in() Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-22 2:45 ` [PATCH 5/6] ata: libata-scsi: Remove struct ata_scsi_args Damien Le Moal
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
Add the missing kdoc comments for the ata_scsiop_inq_XX functions used
to emulate access to VPD pages.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 54 +++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index f9c70f650cfc..880a1e20a8dd 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2052,6 +2052,16 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inq_b0 - Simulate INQUIRY VPD page B0, Block Limits
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B0h (Block Limits).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
{
struct ata_device *dev = args->dev;
@@ -2092,6 +2102,17 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inq_b1 - Simulate INQUIRY VPD page B1, Block Device
+ * Characteristics
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B1h (Block Device Characteristics).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
{
int form_factor = ata_id_form_factor(args->id);
@@ -2109,6 +2130,17 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inq_b2 - Simulate INQUIRY VPD page B2, Logical Block
+ * Provisioning
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B2h (Logical Block Provisioning).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
{
/* SCSI Thin Provisioning VPD page: SBC-3 rev 22 or later */
@@ -2119,6 +2151,17 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inq_b6 - Simulate INQUIRY VPD page B6, Zoned Block Device
+ * Characteristics
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B2h (Zoned Block Device Characteristics).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
{
if (!(args->dev->flags & ATA_DFLAG_ZAC)) {
@@ -2144,6 +2187,17 @@ static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+/**
+ * ata_scsiop_inq_b9 - Simulate INQUIRY VPD page B9, Concurrent Positioning
+ * Ranges
+ * @args: device IDENTIFY data / SCSI command of interest.
+ * @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ * Return data for the VPD page B9h (Concurrent Positioning Ranges).
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host lock)
+ */
static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
{
struct ata_cpr_log *cpr_log = args->dev->cpr_log;
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/6] ata: libata-scsi: Remove struct ata_scsi_args
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
` (3 preceding siblings ...)
2024-10-22 2:45 ` [PATCH 4/6] ata: libata-scsi: Document all VPD page inquiry actors Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-22 2:45 ` [PATCH 6/6] ata: libata-scsi: Return residual for emulated SCSI commands Damien Le Moal
2024-10-25 8:15 ` [PATCH 0/6] Cleanup and improve libata-scsi command emulation Niklas Cassel
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
The data structure struct ata_scsi_args is used to pass the target ATA
device, the SCSI command to simulate and the device identification data
to ata_scsi_rbuf_fill() and to its actor function. This method of
passing information does not improve the code in any way and in fact
increases the number of pointer dereferences for no gains.
Drop this data structure by modifying the interface of
ata_scsi_rbuf_fill() and its actor function to take an ATA device and a
SCSI command as argument.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 241 ++++++++++++++++++++------------------
1 file changed, 127 insertions(+), 114 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 880a1e20a8dd..4593258d2b6a 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1772,15 +1772,10 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
return SCSI_MLQUEUE_HOST_BUSY;
}
-struct ata_scsi_args {
- struct ata_device *dev;
- u16 *id;
- struct scsi_cmnd *cmd;
-};
-
/**
* ata_scsi_rbuf_fill - wrapper for SCSI command simulators
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @actor: Callback hook for desired SCSI command simulator
*
* Takes care of the hard work of simulating a SCSI command...
@@ -1793,30 +1788,30 @@ struct ata_scsi_args {
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
- unsigned int (*actor)(struct ata_scsi_args *args, u8 *rbuf))
+static void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd,
+ unsigned int (*actor)(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf))
{
unsigned int rc;
- struct scsi_cmnd *cmd = args->cmd;
unsigned long flags;
spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE);
- rc = actor(args, ata_scsi_rbuf);
- if (rc == 0)
+ rc = actor(dev, cmd, ata_scsi_rbuf);
+ if (rc == 0) {
sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd),
ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE);
+ cmd->result = SAM_STAT_GOOD;
+ }
spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
-
- if (rc == 0)
- cmd->result = SAM_STAT_GOOD;
}
/**
* ata_scsiop_inq_std - Simulate standard INQUIRY command
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns standard device identification data associated
@@ -1825,7 +1820,8 @@ static void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_std(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
static const u8 versions[] = {
0x00,
@@ -1866,30 +1862,30 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
* Set the SCSI Removable Media Bit (RMB) if the ATA removable media
* device bit (obsolete since ATA-8 ACS) is set.
*/
- if (ata_id_removable(args->id))
+ if (ata_id_removable(dev->id))
hdr[1] |= (1 << 7);
- if (args->dev->class == ATA_DEV_ZAC) {
+ if (dev->class == ATA_DEV_ZAC) {
hdr[0] = TYPE_ZBC;
hdr[2] = 0x7; /* claim SPC-5 version compatibility */
}
- if (args->dev->flags & ATA_DFLAG_CDL)
+ if (dev->flags & ATA_DFLAG_CDL)
hdr[2] = 0xd; /* claim SPC-6 version compatibility */
memcpy(rbuf, hdr, sizeof(hdr));
memcpy(&rbuf[8], "ATA ", 8);
- ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16);
+ ata_id_string(dev->id, &rbuf[16], ATA_ID_PROD, 16);
/* From SAT, use last 2 words from fw rev unless they are spaces */
- ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV + 2, 4);
+ ata_id_string(dev->id, &rbuf[32], ATA_ID_FW_REV + 2, 4);
if (strncmp(&rbuf[32], " ", 4) == 0)
- ata_id_string(args->id, &rbuf[32], ATA_ID_FW_REV, 4);
+ ata_id_string(dev->id, &rbuf[32], ATA_ID_FW_REV, 4);
if (rbuf[32] == 0 || rbuf[32] == ' ')
memcpy(&rbuf[32], "n/a ", 4);
- if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC)
+ if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
memcpy(rbuf + 58, versions_zbc, sizeof(versions_zbc));
else
memcpy(rbuf + 58, versions, sizeof(versions));
@@ -1899,7 +1895,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_00 - Simulate INQUIRY VPD page 0, list of pages
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns list of inquiry VPD pages available.
@@ -1907,7 +1904,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_00(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
int i, num_pages = 0;
static const u8 pages[] = {
@@ -1924,7 +1922,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
for (i = 0; i < sizeof(pages); i++) {
if (pages[i] == 0xb6 &&
- !(args->dev->flags & ATA_DFLAG_ZAC))
+ !(dev->flags & ATA_DFLAG_ZAC))
continue;
rbuf[num_pages + 4] = pages[i];
num_pages++;
@@ -1935,7 +1933,8 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_80 - Simulate INQUIRY VPD page 80, device serial number
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns ATA device serial number.
@@ -1943,7 +1942,8 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_80(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
static const u8 hdr[] = {
0,
@@ -1953,14 +1953,15 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
};
memcpy(rbuf, hdr, sizeof(hdr));
- ata_id_string(args->id, (unsigned char *) &rbuf[4],
+ ata_id_string(dev->id, (unsigned char *) &rbuf[4],
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
return 0;
}
/**
* ata_scsiop_inq_83 - Simulate INQUIRY VPD page 83, device identity
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields two logical unit device identification designators:
@@ -1971,7 +1972,8 @@ static unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_83(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
const int sat_model_serial_desc_len = 68;
int num;
@@ -1983,7 +1985,7 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
rbuf[num + 0] = 2;
rbuf[num + 3] = ATA_ID_SERNO_LEN;
num += 4;
- ata_id_string(args->id, (unsigned char *) rbuf + num,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num,
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
num += ATA_ID_SERNO_LEN;
@@ -1995,21 +1997,21 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
num += 4;
memcpy(rbuf + num, "ATA ", 8);
num += 8;
- ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_PROD,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num, ATA_ID_PROD,
ATA_ID_PROD_LEN);
num += ATA_ID_PROD_LEN;
- ata_id_string(args->id, (unsigned char *) rbuf + num, ATA_ID_SERNO,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num, ATA_ID_SERNO,
ATA_ID_SERNO_LEN);
num += ATA_ID_SERNO_LEN;
- if (ata_id_has_wwn(args->id)) {
+ if (ata_id_has_wwn(dev->id)) {
/* SAT defined lu world wide name */
/* piv=0, assoc=lu, code_set=binary, designator=NAA */
rbuf[num + 0] = 1;
rbuf[num + 1] = 3;
rbuf[num + 3] = ATA_ID_WWN_LEN;
num += 4;
- ata_id_string(args->id, (unsigned char *) rbuf + num,
+ ata_id_string(dev->id, (unsigned char *) rbuf + num,
ATA_ID_WWN, ATA_ID_WWN_LEN);
num += ATA_ID_WWN_LEN;
}
@@ -2019,7 +2021,8 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_89 - Simulate INQUIRY VPD page 89, ATA info
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields SAT-specified ATA VPD page.
@@ -2027,7 +2030,8 @@ static unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_89(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
rbuf[1] = 0x89; /* our page code */
rbuf[2] = (0x238 >> 8); /* page size fixed at 238h */
@@ -2048,13 +2052,14 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
rbuf[56] = ATA_CMD_ID_ATA;
- memcpy(&rbuf[60], &args->id[0], 512);
+ memcpy(&rbuf[60], &dev->id[0], 512);
return 0;
}
/**
* ata_scsiop_inq_b0 - Simulate INQUIRY VPD page B0, Block Limits
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Return data for the VPD page B0h (Block Limits).
@@ -2062,9 +2067,9 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_b0(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
u16 min_io_sectors;
rbuf[1] = 0xb0;
@@ -2077,7 +2082,7 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
* logical than physical sector size we need to figure out what the
* latter is.
*/
- min_io_sectors = 1 << ata_id_log2_per_physical_sector(args->id);
+ min_io_sectors = 1 << ata_id_log2_per_physical_sector(dev->id);
put_unaligned_be16(min_io_sectors, &rbuf[6]);
/*
@@ -2089,7 +2094,7 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
* that we support some form of unmap - in thise case via WRITE SAME
* with the unmap bit set.
*/
- if (ata_id_has_trim(args->id)) {
+ if (ata_id_has_trim(dev->id)) {
u64 max_blocks = 65535 * ATA_MAX_TRIM_RNUM;
if (dev->quirks & ATA_QUIRK_MAX_TRIM_128M)
@@ -2105,7 +2110,8 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_b1 - Simulate INQUIRY VPD page B1, Block Device
* Characteristics
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Return data for the VPD page B1h (Block Device Characteristics).
@@ -2113,11 +2119,12 @@ static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_b1(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- int form_factor = ata_id_form_factor(args->id);
- int media_rotation_rate = ata_id_rotation_rate(args->id);
- u8 zoned = ata_id_zoned_cap(args->id);
+ int form_factor = ata_id_form_factor(dev->id);
+ int media_rotation_rate = ata_id_rotation_rate(dev->id);
+ u8 zoned = ata_id_zoned_cap(dev->id);
rbuf[1] = 0xb1;
rbuf[3] = 0x3c;
@@ -2133,7 +2140,8 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_b2 - Simulate INQUIRY VPD page B2, Logical Block
* Provisioning
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Return data for the VPD page B2h (Logical Block Provisioning).
@@ -2141,7 +2149,8 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_b2(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
/* SCSI Thin Provisioning VPD page: SBC-3 rev 22 or later */
rbuf[1] = 0xb2;
@@ -2154,7 +2163,8 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_b6 - Simulate INQUIRY VPD page B6, Zoned Block Device
* Characteristics
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Return data for the VPD page B2h (Zoned Block Device Characteristics).
@@ -2162,10 +2172,11 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_b6(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- if (!(args->dev->flags & ATA_DFLAG_ZAC)) {
- ata_scsi_set_invalid_field(args->dev, args->cmd, 2, 0xff);
+ if (!(dev->flags & ATA_DFLAG_ZAC)) {
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
return 1;
}
@@ -2178,11 +2189,11 @@ static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
/*
* URSWRZ bit is only meaningful for host-managed ZAC drives
*/
- if (args->dev->zac_zoned_cap & 1)
+ if (dev->zac_zoned_cap & 1)
rbuf[4] |= 1;
- put_unaligned_be32(args->dev->zac_zones_optimal_open, &rbuf[8]);
- put_unaligned_be32(args->dev->zac_zones_optimal_nonseq, &rbuf[12]);
- put_unaligned_be32(args->dev->zac_zones_max_open, &rbuf[16]);
+ put_unaligned_be32(dev->zac_zones_optimal_open, &rbuf[8]);
+ put_unaligned_be32(dev->zac_zones_optimal_nonseq, &rbuf[12]);
+ put_unaligned_be32(dev->zac_zones_max_open, &rbuf[16]);
return 0;
}
@@ -2190,7 +2201,8 @@ static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inq_b9 - Simulate INQUIRY VPD page B9, Concurrent Positioning
* Ranges
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Return data for the VPD page B9h (Concurrent Positioning Ranges).
@@ -2198,14 +2210,15 @@ static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inq_b9(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_cpr_log *cpr_log = args->dev->cpr_log;
+ struct ata_cpr_log *cpr_log = dev->cpr_log;
u8 *desc = &rbuf[64];
int i;
if (!cpr_log) {
- ata_scsi_set_invalid_field(args->dev, args->cmd, 2, 0xff);
+ ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
return 1;
}
@@ -2225,7 +2238,8 @@ static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_inquiry - Simulate INQUIRY command
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Returns data associated with an INQUIRY command output.
@@ -2233,10 +2247,9 @@ static unsigned int ata_scsiop_inq_b9(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_inquiry(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_inquiry(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- struct scsi_cmnd *cmd = args->cmd;
const u8 *scsicmd = cmd->cmnd;
/* is CmdDt set? */
@@ -2247,27 +2260,27 @@ static unsigned int ata_scsiop_inquiry(struct ata_scsi_args *args, u8 *rbuf)
/* Is EVPD clear? */
if ((scsicmd[1] & 1) == 0)
- return ata_scsiop_inq_std(args, rbuf);
+ return ata_scsiop_inq_std(dev, cmd, rbuf);
switch (scsicmd[2]) {
case 0x00:
- return ata_scsiop_inq_00(args, rbuf);
+ return ata_scsiop_inq_00(dev, cmd, rbuf);
case 0x80:
- return ata_scsiop_inq_80(args, rbuf);
+ return ata_scsiop_inq_80(dev, cmd, rbuf);
case 0x83:
- return ata_scsiop_inq_83(args, rbuf);
+ return ata_scsiop_inq_83(dev, cmd, rbuf);
case 0x89:
- return ata_scsiop_inq_89(args, rbuf);
+ return ata_scsiop_inq_89(dev, cmd, rbuf);
case 0xb0:
- return ata_scsiop_inq_b0(args, rbuf);
+ return ata_scsiop_inq_b0(dev, cmd, rbuf);
case 0xb1:
- return ata_scsiop_inq_b1(args, rbuf);
+ return ata_scsiop_inq_b1(dev, cmd, rbuf);
case 0xb2:
- return ata_scsiop_inq_b2(args, rbuf);
+ return ata_scsiop_inq_b2(dev, cmd, rbuf);
case 0xb6:
- return ata_scsiop_inq_b6(args, rbuf);
+ return ata_scsiop_inq_b6(dev, cmd, rbuf);
case 0xb9:
- return ata_scsiop_inq_b9(args, rbuf);
+ return ata_scsiop_inq_b9(dev, cmd, rbuf);
default:
ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
return 1;
@@ -2494,7 +2507,8 @@ static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)
/**
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate MODE SENSE commands. Assume this is invoked for direct
@@ -2504,10 +2518,10 @@ static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_mode_sense(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- u8 *scsicmd = args->cmd->cmnd, *p = rbuf;
+ u8 *scsicmd = cmd->cmnd, *p = rbuf;
static const u8 sat_blk_desc[] = {
0, 0, 0, 0, /* number of blocks: sat unspecified */
0,
@@ -2572,17 +2586,17 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
break;
case CACHE_MPAGE:
- p += ata_msense_caching(args->id, p, page_control == 1);
+ p += ata_msense_caching(dev->id, p, page_control == 1);
break;
case CONTROL_MPAGE:
- p += ata_msense_control(args->dev, p, spg, page_control == 1);
+ p += ata_msense_control(dev, p, spg, page_control == 1);
break;
case ALL_MPAGES:
p += ata_msense_rw_recovery(p, page_control == 1);
- p += ata_msense_caching(args->id, p, page_control == 1);
- p += ata_msense_control(args->dev, p, spg, page_control == 1);
+ p += ata_msense_caching(dev->id, p, page_control == 1);
+ p += ata_msense_control(dev, p, spg, page_control == 1);
break;
default: /* invalid page code */
@@ -2611,18 +2625,19 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
return 0;
invalid_fld:
- ata_scsi_set_invalid_field(dev, args->cmd, fp, bp);
+ ata_scsi_set_invalid_field(dev, cmd, fp, bp);
return 1;
saving_not_supp:
- ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x39, 0x0);
+ ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x39, 0x0);
/* "Saving parameters not supported" */
return 1;
}
/**
* ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate READ CAPACITY commands.
@@ -2630,10 +2645,10 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* None.
*/
-static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- u8 *scsicmd = args->cmd->cmnd;
+ u8 *scsicmd = cmd->cmnd;
u64 last_lba = dev->n_sectors - 1; /* LBA of the last block */
u32 sector_size; /* physical sector size in bytes */
u8 log2_per_phys;
@@ -2668,7 +2683,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
*/
if (scsicmd[0] != SERVICE_ACTION_IN_16 ||
(scsicmd[1] & 0x1f) != SAI_READ_CAPACITY_16) {
- ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
return 1;
}
@@ -2688,16 +2703,16 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
rbuf[10] = sector_size >> (8 * 1);
rbuf[11] = sector_size;
- if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC)
+ if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC)
rbuf[12] = (1 << 4); /* RC_BASIS */
rbuf[13] = log2_per_phys;
rbuf[14] = (lowest_aligned >> 8) & 0x3f;
rbuf[15] = lowest_aligned;
- if (ata_id_has_trim(args->id) && !(dev->quirks & ATA_QUIRK_NOTRIM)) {
+ if (ata_id_has_trim(dev->id) && !(dev->quirks & ATA_QUIRK_NOTRIM)) {
rbuf[14] |= 0x80; /* LBPME */
- if (ata_id_has_zero_after_trim(args->id) &&
+ if (ata_id_has_zero_after_trim(dev->id) &&
dev->quirks & ATA_QUIRK_ZERO_AFTER_TRIM) {
ata_dev_info(dev, "Enabling discard_zeroes_data\n");
rbuf[14] |= 0x40; /* LBPRZ */
@@ -2709,7 +2724,8 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
/**
* ata_scsiop_report_luns - Simulate REPORT LUNS command
- * @args: device IDENTIFY data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Simulate REPORT LUNS command.
@@ -2717,7 +2733,8 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_report_luns(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */
@@ -3429,7 +3446,8 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
/**
* ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
- * @args: device MAINTENANCE_IN data / SCSI command of interest.
+ * @dev: Target device.
+ * @cmd: SCSI command of interest.
* @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
*
* Yields a subset to satisfy scsi_report_opcode()
@@ -3437,20 +3455,20 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
* LOCKING:
* spin_lock_irqsave(host lock)
*/
-static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
+static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
+ struct scsi_cmnd *cmd, u8 *rbuf)
{
- struct ata_device *dev = args->dev;
- u8 *cdb = args->cmd->cmnd;
+ u8 *cdb = cmd->cmnd;
u8 supported = 0, cdlp = 0, rwcdlp = 0;
if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) {
- ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
return 1;
}
if (cdb[2] != 1 && cdb[2] != 3) {
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
- ata_scsi_set_invalid_field(dev, args->cmd, 1, 0xff);
+ ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
return 1;
}
@@ -4384,31 +4402,26 @@ EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);
void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
{
- struct ata_scsi_args args;
const u8 *scsicmd = cmd->cmnd;
u8 tmp8;
- args.dev = dev;
- args.id = dev->id;
- args.cmd = cmd;
-
switch(scsicmd[0]) {
case INQUIRY:
- ata_scsi_rbuf_fill(&args, ata_scsiop_inquiry);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry);
break;
case MODE_SENSE:
case MODE_SENSE_10:
- ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense);
break;
case READ_CAPACITY:
case SERVICE_ACTION_IN_16:
- ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap);
break;
case REPORT_LUNS:
- ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns);
break;
case REQUEST_SENSE:
@@ -4436,7 +4449,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
break;
case MAINTENANCE_IN:
- ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
+ ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in);
break;
/* all other commands */
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/6] ata: libata-scsi: Return residual for emulated SCSI commands
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
` (4 preceding siblings ...)
2024-10-22 2:45 ` [PATCH 5/6] ata: libata-scsi: Remove struct ata_scsi_args Damien Le Moal
@ 2024-10-22 2:45 ` Damien Le Moal
2024-10-25 8:15 ` [PATCH 0/6] Cleanup and improve libata-scsi command emulation Niklas Cassel
6 siblings, 0 replies; 8+ messages in thread
From: Damien Le Moal @ 2024-10-22 2:45 UTC (permalink / raw)
To: linux-ide, Niklas Cassel
The function ata_scsi_rbuf_fill() used to fill the reply buffer of
emulated SCSI commands always copies the ATA reply buffer
(ata_scsi_rbuf) up to the size of the SCSI command buffer (the transfer
length for the command), even if the reply is shorter than the SCSI
command buffer. This leads to issuers of the SCSI command to always get
a result without any residual (resid is always 0) despite the
potentially shorter reply for the command.
Modify all fill actors used by ata_scsi_rbuf_fill() to return the number
of bytes filled for the reply and 0 in case of error. Using this value,
add a call to scsi_set_resid() in ata_scsi_rbuf_fill() to set the
correct residual for the SCSI command when the reply length is shorter
than the command buffer.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/ata/libata-scsi.c | 81 +++++++++++++++++++++++----------------
1 file changed, 47 insertions(+), 34 deletions(-)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 4593258d2b6a..556f7b417f3b 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1792,17 +1792,19 @@ static void ata_scsi_rbuf_fill(struct ata_device *dev, struct scsi_cmnd *cmd,
unsigned int (*actor)(struct ata_device *dev,
struct scsi_cmnd *cmd, u8 *rbuf))
{
- unsigned int rc;
unsigned long flags;
+ unsigned int len;
spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
memset(ata_scsi_rbuf, 0, ATA_SCSI_RBUF_SIZE);
- rc = actor(dev, cmd, ata_scsi_rbuf);
- if (rc == 0) {
+ len = actor(dev, cmd, ata_scsi_rbuf);
+ if (len) {
sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd),
ata_scsi_rbuf, ATA_SCSI_RBUF_SIZE);
cmd->result = SAM_STAT_GOOD;
+ if (scsi_bufflen(cmd) > len)
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - len);
}
spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
@@ -1890,7 +1892,11 @@ static unsigned int ata_scsiop_inq_std(struct ata_device *dev,
else
memcpy(rbuf + 58, versions, sizeof(versions));
- return 0;
+ /*
+ * Include all 8 possible version descriptors, even if not all of
+ * them are popoulated.
+ */
+ return 96;
}
/**
@@ -1928,7 +1934,8 @@ static unsigned int ata_scsiop_inq_00(struct ata_device *dev,
num_pages++;
}
rbuf[3] = num_pages; /* number of supported VPD pages */
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -1955,7 +1962,8 @@ static unsigned int ata_scsiop_inq_80(struct ata_device *dev,
memcpy(rbuf, hdr, sizeof(hdr));
ata_id_string(dev->id, (unsigned char *) &rbuf[4],
ATA_ID_SERNO, ATA_ID_SERNO_LEN);
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2016,7 +2024,8 @@ static unsigned int ata_scsiop_inq_83(struct ata_device *dev,
num += ATA_ID_WWN_LEN;
}
rbuf[3] = num - 4; /* page len (assume less than 256 bytes) */
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2053,7 +2062,8 @@ static unsigned int ata_scsiop_inq_89(struct ata_device *dev,
rbuf[56] = ATA_CMD_ID_ATA;
memcpy(&rbuf[60], &dev->id[0], 512);
- return 0;
+
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2104,7 +2114,7 @@ static unsigned int ata_scsiop_inq_b0(struct ata_device *dev,
put_unaligned_be32(1, &rbuf[28]);
}
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2134,7 +2144,7 @@ static unsigned int ata_scsiop_inq_b1(struct ata_device *dev,
if (zoned)
rbuf[8] = (zoned << 4);
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2157,7 +2167,7 @@ static unsigned int ata_scsiop_inq_b2(struct ata_device *dev,
rbuf[3] = 0x4;
rbuf[5] = 1 << 6; /* TPWS */
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2177,7 +2187,7 @@ static unsigned int ata_scsiop_inq_b6(struct ata_device *dev,
{
if (!(dev->flags & ATA_DFLAG_ZAC)) {
ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- return 1;
+ return 0;
}
/*
@@ -2195,7 +2205,7 @@ static unsigned int ata_scsiop_inq_b6(struct ata_device *dev,
put_unaligned_be32(dev->zac_zones_optimal_nonseq, &rbuf[12]);
put_unaligned_be32(dev->zac_zones_max_open, &rbuf[16]);
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2219,7 +2229,7 @@ static unsigned int ata_scsiop_inq_b9(struct ata_device *dev,
if (!cpr_log) {
ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- return 1;
+ return 0;
}
/* SCSI Concurrent Positioning Ranges VPD page: SBC-5 rev 1 or later */
@@ -2233,7 +2243,7 @@ static unsigned int ata_scsiop_inq_b9(struct ata_device *dev,
put_unaligned_be64(cpr_log->cpr[i].num_lbas, &desc[16]);
}
- return 0;
+ return get_unaligned_be16(&rbuf[2]) + 4;
}
/**
@@ -2255,7 +2265,7 @@ static unsigned int ata_scsiop_inquiry(struct ata_device *dev,
/* is CmdDt set? */
if (scsicmd[1] & 2) {
ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- return 1;
+ return 0;
}
/* Is EVPD clear? */
@@ -2283,7 +2293,7 @@ static unsigned int ata_scsiop_inquiry(struct ata_device *dev,
return ata_scsiop_inq_b9(dev, cmd, rbuf);
default:
ata_scsi_set_invalid_field(dev, cmd, 2, 0xff);
- return 1;
+ return 0;
}
}
@@ -2614,24 +2624,27 @@ static unsigned int ata_scsiop_mode_sense(struct ata_device *dev,
rbuf[3] = sizeof(sat_blk_desc);
memcpy(rbuf + 4, sat_blk_desc, sizeof(sat_blk_desc));
}
- } else {
- put_unaligned_be16(p - rbuf - 2, &rbuf[0]);
- rbuf[3] |= dpofua;
- if (ebd) {
- rbuf[7] = sizeof(sat_blk_desc);
- memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc));
- }
+
+ return rbuf[0] + 1;
+ }
+
+ put_unaligned_be16(p - rbuf - 2, &rbuf[0]);
+ rbuf[3] |= dpofua;
+ if (ebd) {
+ rbuf[7] = sizeof(sat_blk_desc);
+ memcpy(rbuf + 8, sat_blk_desc, sizeof(sat_blk_desc));
}
- return 0;
+
+ return get_unaligned_be16(&rbuf[0]) + 2;
invalid_fld:
ata_scsi_set_invalid_field(dev, cmd, fp, bp);
- return 1;
+ return 0;
saving_not_supp:
ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x39, 0x0);
/* "Saving parameters not supported" */
- return 1;
+ return 0;
}
/**
@@ -2674,7 +2687,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
rbuf[6] = sector_size >> (8 * 1);
rbuf[7] = sector_size;
- return 0;
+ return 8;
}
/*
@@ -2684,7 +2697,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
if (scsicmd[0] != SERVICE_ACTION_IN_16 ||
(scsicmd[1] & 0x1f) != SAI_READ_CAPACITY_16) {
ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- return 1;
+ return 0;
}
/* sector count, 64-bit */
@@ -2719,7 +2732,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_device *dev,
}
}
- return 0;
+ return 16;
}
/**
@@ -2738,7 +2751,7 @@ static unsigned int ata_scsiop_report_luns(struct ata_device *dev,
{
rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */
- return 0;
+ return 16;
}
/*
@@ -3463,13 +3476,13 @@ static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) {
ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- return 1;
+ return 0;
}
if (cdb[2] != 1 && cdb[2] != 3) {
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
ata_scsi_set_invalid_field(dev, cmd, 1, 0xff);
- return 1;
+ return 0;
}
switch (cdb[3]) {
@@ -3542,7 +3555,7 @@ static unsigned int ata_scsiop_maint_in(struct ata_device *dev,
rbuf[0] = rwcdlp;
rbuf[1] = cdlp | supported;
- return 0;
+ return 4;
}
/**
--
2.47.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 0/6] Cleanup and improve libata-scsi command emulation
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
` (5 preceding siblings ...)
2024-10-22 2:45 ` [PATCH 6/6] ata: libata-scsi: Return residual for emulated SCSI commands Damien Le Moal
@ 2024-10-25 8:15 ` Niklas Cassel
6 siblings, 0 replies; 8+ messages in thread
From: Niklas Cassel @ 2024-10-25 8:15 UTC (permalink / raw)
To: linux-ide, Damien Le Moal
On Tue, 22 Oct 2024 11:45:31 +0900, Damien Le Moal wrote:
> The first 5 patches of this series refactor and cleanup the libata-scsi
> code handling command emulation. There are no functional changes
> introduced by these patches.
>
> The last patch of the series (patch 6) improves the command emulation
> by accurately handling the scsi command residual and transferring back
> to the requester only the relevant data of a command reply.
>
> [...]
Applied to libata/linux.git (for-6.13), thanks!
[1/6] ata: libata-scsi: Refactor ata_scsi_simulate()
https://git.kernel.org/libata/linux/c/b055e3be
[2/6] ata: libata-scsi: Refactor ata_scsiop_read_cap()
https://git.kernel.org/libata/linux/c/44bdde15
[3/6] ata: libata-scsi: Refactor ata_scsiop_maint_in()
https://git.kernel.org/libata/linux/c/4ab7bb97
[4/6] ata: libata-scsi: Document all VPD page inquiry actors
https://git.kernel.org/libata/linux/c/47000e84
[5/6] ata: libata-scsi: Remove struct ata_scsi_args
https://git.kernel.org/libata/linux/c/2365278e
[6/6] ata: libata-scsi: Return residual for emulated SCSI commands
https://git.kernel.org/libata/linux/c/5251ae22
Kind regards,
Niklas
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-10-25 8:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-22 2:45 [PATCH 0/6] Cleanup and improve libata-scsi command emulation Damien Le Moal
2024-10-22 2:45 ` [PATCH 1/6] ata: libata-scsi: Refactor ata_scsi_simulate() Damien Le Moal
2024-10-22 2:45 ` [PATCH 2/6] ata: libata-scsi: Refactor ata_scsiop_read_cap() Damien Le Moal
2024-10-22 2:45 ` [PATCH 3/6] ata: libata-scsi: Refactor ata_scsiop_maint_in() Damien Le Moal
2024-10-22 2:45 ` [PATCH 4/6] ata: libata-scsi: Document all VPD page inquiry actors Damien Le Moal
2024-10-22 2:45 ` [PATCH 5/6] ata: libata-scsi: Remove struct ata_scsi_args Damien Le Moal
2024-10-22 2:45 ` [PATCH 6/6] ata: libata-scsi: Return residual for emulated SCSI commands Damien Le Moal
2024-10-25 8:15 ` [PATCH 0/6] Cleanup and improve libata-scsi command emulation Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).