From 13d5478d9bf19778a1bf9816a82b2cf8f9e8f49e Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 21:22:43 +0900 Subject: [PATCH 01/27] ata: libata: rename ata_dev_is_zac() The helper function ata_dev_is_zac() checks if a device is a ZAC class device (host managed zoned disk) or if it is a host aware zoned disk, that is, a regular ATA disk that supports the zoned capabilities. So the name of this helper function is confusing as it hints at the first case only. Rename this helper function to ata_dev_is_zoned() to avoid confusions and better reflect the two cases tested. Use this helper in ata_scsiop_inq_std(), ata_scsiop_read_cap() and ata_scsi_report_supported_opcodes() instead of having the same tests open coded. While at it, to stay consistent with this renaming, also rename ata_dev_config_zac() to ata_dev_config_zoned(). Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-core.c | 6 +++--- drivers/ata/libata-scsi.c | 11 +++++------ drivers/ata/libata.h | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bdc88cf74709..5121faf9738e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2488,7 +2488,7 @@ static void ata_dev_config_sense_reporting(struct ata_device *dev) } } -static void ata_dev_config_zac(struct ata_device *dev) +static void ata_dev_config_zoned(struct ata_device *dev) { unsigned int err_mask; u8 *identify_buf = dev->sector_buf; @@ -2497,7 +2497,7 @@ static void ata_dev_config_zac(struct ata_device *dev) dev->zac_zones_optimal_nonseq = U32_MAX; dev->zac_zones_max_open = U32_MAX; - if (!ata_dev_is_zac(dev)) + if (!ata_dev_is_zoned(dev)) return; if (!ata_identify_page_supported(dev, ATA_LOG_ZONED_INFORMATION)) { @@ -3111,7 +3111,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_config_fua(dev); ata_dev_config_devslp(dev); ata_dev_config_sense_reporting(dev); - ata_dev_config_zac(dev); + ata_dev_config_zoned(dev); ata_dev_config_trusted(dev); ata_dev_config_cpr(dev); ata_dev_config_cdl(dev); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5868526301a2..be7143df6a01 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2023,7 +2023,7 @@ static unsigned int ata_scsiop_inq_std(struct ata_device *dev, if (rbuf[32] == 0 || rbuf[32] == ' ') memcpy(&rbuf[32], "n/a ", 4); - if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC) + if (ata_dev_is_zoned(dev)) memcpy(rbuf + 58, versions_zbc, sizeof(versions_zbc)); else memcpy(rbuf + 58, versions, sizeof(versions)); @@ -2063,7 +2063,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_device *dev, }; for (i = 0; i < sizeof(pages); i++) { - if (pages[i] == 0xb6 && !ata_dev_is_zac(dev)) + if (pages[i] == 0xb6 && !ata_dev_is_zoned(dev)) continue; rbuf[num_pages + 4] = pages[i]; num_pages++; @@ -2320,7 +2320,7 @@ static unsigned int ata_scsiop_inq_b2(struct ata_device *dev, static unsigned int ata_scsiop_inq_b6(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf) { - if (!ata_dev_is_zac(dev)) { + if (!ata_dev_is_zoned(dev)) { ata_scsi_set_invalid_field(dev, cmd, 2, 0xff); return 0; } @@ -2851,7 +2851,7 @@ static unsigned int ata_scsiop_read_cap(struct ata_device *dev, rbuf[10] = sector_size >> (8 * 1); rbuf[11] = sector_size; - if (ata_id_zoned_cap(dev->id) || dev->class == ATA_DEV_ZAC) + if (ata_dev_is_zoned(dev)) rbuf[12] = (1 << 4); /* RC_BASIS */ rbuf[13] = log2_per_phys; rbuf[14] = (lowest_aligned >> 8) & 0x3f; @@ -3659,8 +3659,7 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, break; case ZBC_IN: case ZBC_OUT: - if (ata_id_zoned_cap(dev->id) || - dev->class == ATA_DEV_ZAC) + if (ata_dev_is_zoned(dev)) supported = 3; break; case SECURITY_PROTOCOL_IN: diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 700627596ce1..39494ab206a2 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -44,7 +44,7 @@ static inline bool ata_sstatus_online(u32 sstatus) return (sstatus & 0xf) == 0x3; } -static inline bool ata_dev_is_zac(struct ata_device *dev) +static inline bool ata_dev_is_zoned(struct ata_device *dev) { /* Host managed device or host aware device */ return dev->class == ATA_DEV_ZAC || From 1128f29f99a6a67d9f88cb5eab8ce5a8793da3b7 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 18:02:52 +0900 Subject: [PATCH 02/27] ata: libata-scsi: refactor ata_scsi_report_supported_opcodes() ata_scsi_report_supported_opcodes() is very limited in functionality as it lacks support for the all command format and also does not handle correctly commands that have a service action. In preparation for adding these missing features, refactor how ata_scsi_report_supported_opcodes() operates to make modifications and extensions easier. To do so, introduce the array of supported commands ata_supported_cmds. This array entries are of type struct ata_scsi_cmd. This structure stores the operation code, CDB length, and the service action of a supported SCSI command that libata SAT can translate or emulate. Since some service actions (e.g. ZI_REPORT_ZONES) can have a value of 0, the field sa_valid of struct ata_scsi_cmd is used to indicate if the sa field is valid, or if it should be ignored. The helper function ata_scsi_get_supported_cmd() is implemented to search for a particular command by opcode in this array. This function is used in ata_scsi_cmd_is_supported() together with a struct ata_scsi_cmd_support to check based on the target device features if the specified command is supported. ata_scsi_cmd_is_supported() is used as the main function in ata_scsi_report_supported_opcodes() to determine if a particular command is supported and fill the command reply rbuf as needed. In the case of a command that is not supported, the support field is set to 1 as specified in SPC, indicating that the command is not supported. Of note is that the old ata_scsi_report_supported_opcodes() code did not handle the VARIABLE_LENGTH_CMD/ATA_32 command which is supported and translated by libata-scsi. The ata_supported_cmds array includes this command. As commented in the code, WRITE_SAME_16 is left out of the array of supported command ata_scsi_write_same_xlat() prevents the use of this command as a passthrough command. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 229 +++++++++++++++++++++++++++----------- 1 file changed, 161 insertions(+), 68 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index be7143df6a01..196adb684433 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3593,12 +3593,166 @@ invalid_opcode: return 1; } +struct ata_scsi_cmd { + u8 op; + u8 cdb_len; + bool sa_valid; + u16 sa; +}; + +/* + * Array of commands supported with translation or emulation, sorted in + * ascending opcode and service action order. All of these commands are + * processed either in ata_xlat_func() or in ata_scsi_simulate(); + * + * Note: commands that are not fully supported may be left out of this array + * so that they are not reported as supported for passthrough but still + * available through the block layer. For now, this includes the following + * commands: + * - WRITE_SAME_16: ata_scsi_write_same_xlat() forbids passthrough commands + */ +static const struct ata_scsi_cmd ata_supported_cmds[] = { + { .op = TEST_UNIT_READY, .cdb_len = 6 }, + { .op = REZERO_UNIT, .cdb_len = 6 }, + { .op = REQUEST_SENSE, .cdb_len = 6 }, + { .op = READ_6, .cdb_len = 6 }, + { .op = WRITE_6, .cdb_len = 6 }, + { .op = SEEK_6, .cdb_len = 6 }, + { .op = INQUIRY, .cdb_len = 6 }, + { .op = MODE_SELECT, .cdb_len = 6 }, + { .op = MODE_SENSE, .cdb_len = 6 }, + { .op = START_STOP, .cdb_len = 6 }, + { .op = SEND_DIAGNOSTIC, .cdb_len = 6 }, + { .op = READ_CAPACITY, .cdb_len = 10 }, + { .op = READ_10, .cdb_len = 10 }, + { .op = WRITE_10, .cdb_len = 10 }, + { .op = SEEK_10, .cdb_len = 10 }, + { .op = VERIFY, .cdb_len = 10 }, + { .op = SYNCHRONIZE_CACHE, .cdb_len = 10 }, + { .op = MODE_SELECT_10, .cdb_len = 10 }, + { .op = MODE_SENSE_10, .cdb_len = 10 }, + { + .op = VARIABLE_LENGTH_CMD, .cdb_len = 32, + .sa_valid = true, + .sa = ATA_32 + }, + { .op = ATA_16, .cdb_len = 16 }, + { .op = READ_16, .cdb_len = 16 }, + { .op = WRITE_16, .cdb_len = 16 }, + { .op = VERIFY_16, .cdb_len = 16 }, + { .op = SYNCHRONIZE_CACHE_16, .cdb_len = 16 }, + { + .op = ZBC_OUT, .cdb_len = 16, + .sa_valid = true, + .sa = ZO_CLOSE_ZONE + }, + { + .op = ZBC_OUT, .cdb_len = 16, + .sa_valid = true, + .sa = ZO_FINISH_ZONE + }, + { + .op = ZBC_OUT, .cdb_len = 16, + .sa_valid = true, + .sa = ZO_OPEN_ZONE + }, + { + .op = ZBC_OUT, .cdb_len = 16, + .sa_valid = true, + .sa = ZO_RESET_WRITE_POINTER + }, + { + .op = ZBC_IN, .cdb_len = 16, + .sa_valid = true, + .sa = ZI_REPORT_ZONES + }, + { + .op = SERVICE_ACTION_IN_16, .cdb_len = 16, + .sa_valid = true, + .sa = SAI_READ_CAPACITY_16 + }, + { .op = REPORT_LUNS, .cdb_len = 12 }, + { .op = ATA_12, .cdb_len = 12 }, + { .op = SECURITY_PROTOCOL_IN, .cdb_len = 12 }, + { + .op = MAINTENANCE_IN, .cdb_len = 12, + .sa_valid = true, + .sa = MI_REPORT_SUPPORTED_OPERATION_CODES + }, + { .op = SECURITY_PROTOCOL_OUT, .cdb_len = 12 }, +}; + +static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op) +{ + const struct ata_scsi_cmd *cmd; + int i; + + for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) { + cmd = &ata_supported_cmds[i]; + if (cmd->op == op) + return cmd; + } + + return NULL; +} + +struct ata_scsi_cmd_support { + u8 cdlp; + u8 rwcdlp; +}; + +static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, + struct ata_scsi_cmd_support *sup) +{ + const struct ata_scsi_cmd *cmd; + + /* First, see if we support the command. */ + cmd = ata_scsi_get_supported_cmd(op); + if (!cmd) + return false; + + /* Now refine the support report depending on the device features. */ + memset(sup, 0, sizeof(*sup)); + switch (op) { + case READ_16: + if (dev->flags & ATA_DFLAG_CDL) { + /* + * CDL read descriptors map to the T2A page, that is, + * rwcdlp = 0x01 and cdlp = 0x01 + */ + sup->rwcdlp = 0x01; + sup->cdlp = 0x01; + } + break; + case WRITE_16: + if (dev->flags & ATA_DFLAG_CDL) { + /* + * CDL write descriptors map to the T2B page, that is, + * rwcdlp = 0x01 and cdlp = 0x02 + */ + sup->rwcdlp = 0x01; + sup->cdlp = 0x02; + } + break; + case ZBC_IN: + case ZBC_OUT: + return ata_dev_is_zoned(dev); + case SECURITY_PROTOCOL_IN: + case SECURITY_PROTOCOL_OUT: + return dev->flags & ATA_DFLAG_TRUSTED; + default: + break; + } + + return true; +} + static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf) { + struct ata_scsi_cmd_support sup; u8 *cdb = cmd->cmnd; - u8 supported = 0, cdlp = 0, rwcdlp = 0; if (cdb[2] != 1 && cdb[2] != 3) { ata_dev_warn(dev, "invalid command format %d\n", cdb[2]); @@ -3606,74 +3760,13 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, return 0; } - switch (cdb[3]) { - case INQUIRY: - case MODE_SENSE: - case MODE_SENSE_10: - case READ_CAPACITY: - case SERVICE_ACTION_IN_16: - case REPORT_LUNS: - case REQUEST_SENSE: - case SYNCHRONIZE_CACHE: - case SYNCHRONIZE_CACHE_16: - case REZERO_UNIT: - case SEEK_6: - case SEEK_10: - case TEST_UNIT_READY: - case SEND_DIAGNOSTIC: - case MAINTENANCE_IN: - case READ_6: - case READ_10: - case WRITE_6: - case WRITE_10: - case ATA_12: - case ATA_16: - case VERIFY: - case VERIFY_16: - case MODE_SELECT: - case MODE_SELECT_10: - case START_STOP: - supported = 3; - break; - case READ_16: - supported = 3; - if (dev->flags & ATA_DFLAG_CDL) { - /* - * CDL read descriptors map to the T2A page, that is, - * rwcdlp = 0x01 and cdlp = 0x01 - */ - rwcdlp = 0x01; - cdlp = 0x01 << 3; - } - break; - case WRITE_16: - supported = 3; - if (dev->flags & ATA_DFLAG_CDL) { - /* - * CDL write descriptors map to the T2B page, that is, - * rwcdlp = 0x01 and cdlp = 0x02 - */ - rwcdlp = 0x01; - cdlp = 0x02 << 3; - } - break; - case ZBC_IN: - case ZBC_OUT: - if (ata_dev_is_zoned(dev)) - supported = 3; - break; - case SECURITY_PROTOCOL_IN: - case SECURITY_PROTOCOL_OUT: - if (dev->flags & ATA_DFLAG_TRUSTED) - supported = 3; - break; - default: - break; - } - /* One command format */ - rbuf[0] = rwcdlp; - rbuf[1] = cdlp | supported; + if (ata_scsi_cmd_is_supported(dev, cdb[3], &sup)) { + rbuf[0] = sup.rwcdlp; + rbuf[1] = (sup.cdlp << 3) | 0x03; + } else { + rbuf[1] = 0x01; + } return 4; } From eac22d76d9ac941cd93d7750338e304d0574d8b4 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 18:12:48 +0900 Subject: [PATCH 03/27] ata: libata-scsi: improve service action support in ata_scsi_report_supported_opcodes() ata_scsi_report_supported_opcodes() is ignoring the service action specified in the SCSI command CDB, but the one command format must take this field into consideration. With the reporting options field set to 1, the REPORT SUPPORTED OPERATION CODES command must be failed if the specified opcode to check has service actions, while reporting option 3 must match supported opcodes together with the specified service action. Stop ignoring the service action by passing it to ata_scsi_cmd_is_supported() and searching for commands in the array of supported commands (ata_supported_cmds) using both the command opcode and service action. Introduce the helper function ata_scsi_supported_cmd_use_sa() to determine if a particular command has service actions and use this function to fail a REPORT SUPPORTED OPERATION CODES command if such command is specified with reporting options 1. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 41 ++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 196adb684433..39323d39fd75 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3682,7 +3682,21 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = { { .op = SECURITY_PROTOCOL_OUT, .cdb_len = 12 }, }; -static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op) +static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op, u16 sa) +{ + const struct ata_scsi_cmd *cmd; + int i; + + for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) { + cmd = &ata_supported_cmds[i]; + if (cmd->op == op && cmd->sa == sa) + return cmd; + } + + return NULL; +} + +static bool ata_scsi_supported_cmd_use_sa(u8 op) { const struct ata_scsi_cmd *cmd; int i; @@ -3690,10 +3704,10 @@ static const struct ata_scsi_cmd *ata_scsi_get_supported_cmd(u8 op) for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) { cmd = &ata_supported_cmds[i]; if (cmd->op == op) - return cmd; + return cmd->sa_valid; } - return NULL; + return false; } struct ata_scsi_cmd_support { @@ -3701,13 +3715,13 @@ struct ata_scsi_cmd_support { u8 rwcdlp; }; -static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, +static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, struct ata_scsi_cmd_support *sup) { const struct ata_scsi_cmd *cmd; /* First, see if we support the command. */ - cmd = ata_scsi_get_supported_cmd(op); + cmd = ata_scsi_get_supported_cmd(op, sa); if (!cmd) return false; @@ -3753,15 +3767,28 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, { struct ata_scsi_cmd_support sup; u8 *cdb = cmd->cmnd; + u16 sa = 0; - if (cdb[2] != 1 && cdb[2] != 3) { + switch (cdb[2]) { + case 1: + /* One command format with command support data, ignore sa. */ + if (ata_scsi_supported_cmd_use_sa(cdb[3])) { + ata_scsi_set_invalid_field(dev, cmd, 3, 0xff); + return 0; + } + break; + case 3: + /* One command format */ + sa = get_unaligned_be16(&cdb[4]); + break; + default: ata_dev_warn(dev, "invalid command format %d\n", cdb[2]); ata_scsi_set_invalid_field(dev, cmd, 2, 0xff); return 0; } /* One command format */ - if (ata_scsi_cmd_is_supported(dev, cdb[3], &sup)) { + if (ata_scsi_cmd_is_supported(dev, cdb[3], sa, &sup)) { rbuf[0] = sup.rwcdlp; rbuf[1] = (sup.cdlp << 3) | 0x03; } else { From bff44d253f4b65789ffe56f2ad1a56d7fdc0a24e Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 25 Jun 2026 09:48:02 +0900 Subject: [PATCH 04/27] ata: libata-scsi: support reporting options 2 in REPORT SUPPORTED OPERATION CODES Add support for the reporting options value 2 of the in REPORT SUPPORTED OPERATION CODES command in ata_scsi_report_supported_opcodes(). With this reporting option, the specified opcode to check must have a service action. Use ata_scsi_supported_cmd_has_sa() to check for this and fill the command reply buffer using the one command format based on the command support. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 39323d39fd75..265236b0493d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3777,6 +3777,13 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, return 0; } break; + case 2: + /* One command format, must have sa. */ + if (!ata_scsi_supported_cmd_use_sa(cdb[3])) { + ata_scsi_set_invalid_field(dev, cmd, 3, 0xff); + return 0; + } + fallthrough; case 3: /* One command format */ sa = get_unaligned_be16(&cdb[4]); From 81ab2321c1467959767cde8bd23a582d203263ab Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 18:26:44 +0900 Subject: [PATCH 05/27] ata: libata-scsi: support the all command format for reporting supported commands Add support to ata_scsi_report_supported_opcodes() for the all command format indicated with a reporting option of 0. The function ata_scsi_report_all_supported_opcodes() is introduced to implement this support. This function operates by testing all commands of the ata_supported_cmds array and testing them using ata_scsi_cmd_is_supported(), filling rbuf as it loops through all the commands that libata can emulate or translate. With this change, sg_opcodes /dev/sdX is able to list all commands supported by libata and a device. An example below is shown for a SATA disk also supporting CDL: # sg_opcodes /dev/sda ATA WDC WUH722626AL WZ41 Peripheral device type: disk Opcode Service CDB RWCDLP, Name (hex) action(h) size CDLP ----------------------------------------------- 00 6 0,0 Test Unit Ready 01 6 0,0 Rezero Unit 03 6 0,0 Request Sense 08 6 0,0 Read(6) 0a 6 0,0 Write(6) 0b 6 0,0 Seek(6) 12 6 0,0 Inquiry 15 6 0,0 Mode select(6) 1a 6 0,0 Mode sense(6) 1b 6 0,0 Start stop unit 1d 6 0,0 Send diagnostic 25 10 0,0 Read capacity(10) 28 10 0,0 Read(10) 2a 10 0,0 Write(10) 2b 10 0,0 Seek(10) 2f 10 0,0 Verify(10) 35 10 0,0 Synchronize cache(10) 55 10 0,0 Mode select(10) 5a 10 0,0 Mode sense(10) 7f 1ff0 32 0,0 ATA pass-through(32) 85 16 0,0 ATA pass-through(16) 88 16 1,1 Read(16) 8a 16 1,2 Write(16) 8f 16 0,0 Verify(16) 91 16 0,0 Synchronize cache(16) 9e 10 16 0,0 Read capacity(16) a0 12 0,0 Report luns a1 12 0,0 ATA pass-through(12) a3 c 12 0,0 Report supported operation codes Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 265236b0493d..e48915a9f81b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3761,6 +3761,42 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, return true; } +static unsigned int +ata_scsi_report_all_supported_opcodes(struct ata_device *dev, u8 *rbuf) +{ + struct ata_scsi_cmd_support sup; + const struct ata_scsi_cmd *cmd; + unsigned int len = 4; + u8 *buf = &rbuf[len]; + int i; + + for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) { + if (len > ATA_SCSI_RBUF_SIZE - 8) + break; + + cmd = &ata_supported_cmds[i]; + + /* All command format */ + if (!ata_scsi_cmd_is_supported(dev, cmd->op, cmd->sa, &sup)) + continue; + + buf[0] = cmd->op; + put_unaligned_be16(cmd->sa, &buf[2]); + buf[5] = (sup.rwcdlp << 6) | (sup.cdlp << 2); + if (cmd->sa_valid) + buf[5] |= 0x01; + put_unaligned_be16(cmd->cdb_len, &buf[6]); + + /* CTDP == 0 */ + len += 8; + buf += 8; + } + + put_unaligned_be32(len - 4, &rbuf[0]); + + return len; +} + static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, struct scsi_cmnd *cmd, u8 *rbuf) @@ -3770,6 +3806,9 @@ static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, u16 sa = 0; switch (cdb[2]) { + case 0: + /* All command format */ + return ata_scsi_report_all_supported_opcodes(dev, rbuf); case 1: /* One command format with command support data, ignore sa. */ if (ata_scsi_supported_cmd_use_sa(cdb[3])) { From 79cce911e623c0baa0fde307ce3a434e084b881a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 2 Jul 2026 12:59:58 +0200 Subject: [PATCH 06/27] ata: libata-scsi: fix DSM TRIM for sector sizes larger than 2048 bytes ata_scsi_write_same_xlat() translates a SCSI WRITE SAME command with the UNMAP bit set into an ATA DATA SET MANAGEMENT TRIM command. The TRIM descriptor is built by ata_format_dsm_trim_descr() into the 2048-byte ata_scsi_rbuf staging buffer, and the number of bytes copied is compared against the logical sector size by the caller: size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); if (size != len) /* len == sdp->sector_size */ goto invalid_param_len; ata_format_dsm_trim_descr() clamps the copy length to ATA_SCSI_RBUF_SIZE (2048). On a device whose logical sector size exceeds that (e.g. a 4Kn device, where sector_size == 4096) the function can never return more than 2048, while the caller expects it to return sector_size. The comparison therefore always fails, so every TRIM is rejected with "Parameter list length error" and WARN_ON() splats on each attempt. TRIM / discard is thus completely broken on such devices. The descriptor was incorrectly sized from the logical sector size. A DSM TRIM payload is a list of 512-byte pages, each holding up to ATA_MAX_TRIM_RNUM (64) LBA Range Entries, and is independent of the logical sector size. The Block Limits VPD page already advertises a single such page as the maximum WRITE SAME length (65535 * ATA_MAX_TRIM_RNUM logical blocks), so the block layer never sends a request that needs more than one page. Emit exactly one 512-byte page, independent of the logical sector size, and transfer only that page (COUNT == 1). For a 512-byte-sector device this is unchanged; devices with larger logical sectors now work instead of failing every TRIM. Reviewed-by: Hannes Reinecke Fixes: ef2d7392c4ec ("libata: SCT Write Same / DSM Trim") Cc: stable@vger.kernel.org Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-scsi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e48915a9f81b..97c959ef5114 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3450,17 +3450,13 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, u64 sector, u32 count) { - struct scsi_device *sdp = cmd->device; - size_t len = sdp->sector_size; + size_t len = ATA_SECT_SIZE; size_t r; __le64 *buf; u32 i = 0; unsigned long flags; - WARN_ON(len > ATA_SCSI_RBUF_SIZE); - - if (len > ATA_SCSI_RBUF_SIZE) - len = ATA_SCSI_RBUF_SIZE; + BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE); spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); buf = ((void *)ata_scsi_rbuf); @@ -3495,13 +3491,11 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) { struct ata_taskfile *tf = &qc->tf; struct scsi_cmnd *scmd = qc->scsicmd; - struct scsi_device *sdp = scmd->device; - size_t len = sdp->sector_size; struct ata_device *dev = qc->dev; const u8 *cdb = scmd->cmnd; u64 block; u32 n_block; - const u32 trmax = len >> 3; + const u32 trmax = ATA_MAX_TRIM_RNUM; u32 size; u16 fp; u8 bp = 0xff; @@ -3545,13 +3539,13 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_param_len; /* - * size must match sector size in bytes - * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count) - * is defined as number of 512 byte blocks to be transferred. + * The TRIM descriptor is a single 512-byte page, which is the maximum + * WRITE SAME length advertised in the Block Limits VPD page. For DATA + * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of + * 512-byte blocks to be transferred. */ - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != len) + if (size != ATA_SECT_SIZE) goto invalid_param_len; if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { @@ -3577,6 +3571,12 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) ATA_TFLAG_WRITE; ata_qc_set_pc_nbytes(qc); + /* + * The DSM TRIM payload is a single 512-byte page, which may be smaller + * than the WRITE SAME data-out buffer (one logical block); only + * transfer that page so the length matches the COUNT field. + */ + qc->nbytes = size; return 0; From e64e6b5dc86758c14ed28a6e85bf5d1b78146ed5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 2 Jul 2026 12:59:59 +0200 Subject: [PATCH 07/27] ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND DSM TRIM currently always emits a single 512-byte page of LBA Range Entries (ATA_MAX_TRIM_RNUM == 64 ranges), regardless of how many pages the device can accept in one DATA SET MANAGEMENT command. The maximum is reported by MAX PAGES PER DSM COMMAND (IDENTIFY DEVICE word 105). Honour it: size the TRIM descriptor as a whole number of 512-byte pages, bounded by that limit and by the logical sector size (the WRITE SAME data-out buffer is a single logical block). Build and transfer only as many pages as the request needs, and set the DSM COUNT field, qc->nbytes and the maximum WRITE SAME length in the Block Limits VPD page accordingly. Build the descriptor straight into the WRITE SAME data-out buffer using an atomic sg_miter mapping, instead of staging it in the shared ata_scsi_rbuf and copying it out. This removes the global ata_scsi_rbuf_lock and a memcpy from the TRIM path. While commit 9379e6b8e0f9 ("libata: Safely overwrite attached page in WRITE SAME xlat") replaced direct access to the data-out buffer with an intermediate step that writes the entries in the ata_scsi_rbuf buffer, this solution writes to the data-out buffer using sg_miter, which maps each segment with kmap_atomic (SG_MITER_ATOMIC), so it's highmem- and multi-segment-safe, and it's usable from the non-sleeping command-submission path (unlike the page_address() access that ata_scsi_rbuf originally replaced). A 512-byte-sector device still uses a single page, so its behaviour is unchanged. Add ata_id_dsm_max_pages() to read IDENTIFY DEVICE word 105. Reviewed-by: Hannes Reinecke Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-scsi.c | 135 ++++++++++++++++++++++++++------------ include/linux/ata.h | 13 ++++ 2 files changed, 107 insertions(+), 41 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 97c959ef5114..5cddb63a6bc6 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2201,6 +2201,39 @@ static unsigned int ata_scsiop_inq_89(struct ata_device *dev, return get_unaligned_be16(&rbuf[2]) + 4; } +/** + * ata_dsm_trim_pages - maximum DSM TRIM payload for a device, in 512-byte pages + * @dev: ATA device the DATA SET MANAGEMENT TRIM command will be sent to + * + * A DATA SET MANAGEMENT TRIM payload is a list of 512-byte pages, each holding + * up to ATA_MAX_TRIM_RNUM (64) LBA Range Entries; the format is page-based and + * unrelated to the logical sector size. + * + * The logical sector size still bounds it, though: the descriptor is written + * directly into the WRITE SAME data-out buffer, which sd sizes to a single + * logical block, so it can hold at most sector_size / 512 pages. + * + * Return: the maximum number of 512-byte pages a single translated WRITE SAME + * command may send to @dev (never less than one), that is the smaller of: + * - MAX PAGES PER DSM COMMAND (IDENTIFY DEVICE word 105), when the device + * reports a non-zero limit; and + * - the logical sector size expressed in 512-byte pages (see above). + */ +static unsigned int ata_dsm_trim_pages(struct ata_device *dev) +{ + unsigned int sector_size = ata_id_logical_sector_size(dev->id); + unsigned int max_pages = ata_id_dsm_max_pages(dev->id); + unsigned int pages = sector_size / ATA_SECT_SIZE; + + /* If the device does not specify a limit, assume only a single page. */ + if (!max_pages) + max_pages = 1; + + pages = min_not_zero(pages, max_pages); + + return pages; +} + /** * ata_scsiop_inq_b0 - Simulate INQUIRY VPD page B0, Block Limits * @dev: Target device. @@ -2240,7 +2273,8 @@ static unsigned int ata_scsiop_inq_b0(struct ata_device *dev, * with the unmap bit set. */ if (ata_id_has_trim(dev->id)) { - u64 max_blocks = 65535 * ATA_MAX_TRIM_RNUM; + unsigned int max_pages = ata_dsm_trim_pages(dev); + u64 max_blocks = max_pages * ATA_MAX_TRIM_RNUM * (u64)U16_MAX; if (dev->quirks & ATA_QUIRK_MAX_TRIM_128M) max_blocks = 128 << (20 - SECTOR_SHIFT); @@ -3429,14 +3463,14 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) /** * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim * @cmd: SCSI command being translated - * @trmax: Maximum number of entries that will fit in sector_size bytes. + * @size: DSM TRIM payload size in bytes (a multiple of 512) * @sector: Starting sector * @count: Total Range of request in logical sectors * * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted * descriptor. * - * Upto 64 entries of the format: + * The payload is a list of @size / 8 entries of the format: * 63:48 Range Length * 47:0 LBA * @@ -3445,35 +3479,45 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) * * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET * - * Return: Number of bytes copied into sglist. + * The descriptor is written straight into the WRITE SAME data-out buffer; + * ata_dsm_trim_pages() guarantees @size does not exceed that buffer (one + * logical block). An atomic sg_miter mapping is used so this works from the + * command submission path and, unlike page_address(), copes with a high + * memory payload. + * + * Return: Number of bytes written into the data-out buffer. */ -static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, +static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, size_t size, u64 sector, u32 count) { - size_t len = ATA_SECT_SIZE; - size_t r; - __le64 *buf; - u32 i = 0; - unsigned long flags; + struct sg_mapping_iter miter; + size_t offset = 0; - BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE); + sg_miter_start(&miter, scsi_sglist(cmd), scsi_sg_count(cmd), + SG_MITER_TO_SG | SG_MITER_ATOMIC); + while (offset < size && sg_miter_next(&miter)) { + __le64 *buf = miter.addr; + size_t chunk = min_t(size_t, miter.length, size - offset); + unsigned int n = chunk / sizeof(__le64); + unsigned int i; - spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); - buf = ((void *)ata_scsi_rbuf); - memset(buf, 0, len); - while (i < trmax) { - u64 entry = sector | - ((u64)(count > 0xffff ? 0xffff : count) << 48); - buf[i++] = __cpu_to_le64(entry); - if (count <= 0xffff) - break; - count -= 0xffff; - sector += 0xffff; + for (i = 0; i < n; i++) { + u64 entry = 0; + + if (count) { + u32 rlen = min_t(u32, count, 0xffff); + + entry = sector | ((u64)rlen << 48); + sector += rlen; + count -= rlen; + } + buf[i] = cpu_to_le64(entry); + } + offset += n * sizeof(__le64); } - r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len); - spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags); + sg_miter_stop(&miter); - return r; + return offset; } /** @@ -3493,10 +3537,11 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) struct scsi_cmnd *scmd = qc->scsicmd; struct ata_device *dev = qc->dev; const u8 *cdb = scmd->cmnd; + unsigned int max_pages = ata_dsm_trim_pages(dev); + unsigned int n_pages; + size_t size; u64 block; u32 n_block; - const u32 trmax = ATA_MAX_TRIM_RNUM; - u32 size; u16 fp; u8 bp = 0xff; u8 unmap = cdb[1] & 0x8; @@ -3526,7 +3571,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_fld; } /* If the request is too large the cmd is invalid */ - if (n_block > 0xffff * trmax) { + if (n_block > max_pages * ATA_MAX_TRIM_RNUM * (u64)U16_MAX) { fp = 2; goto invalid_fld; } @@ -3539,31 +3584,39 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_param_len; /* - * The TRIM descriptor is a single 512-byte page, which is the maximum - * WRITE SAME length advertised in the Block Limits VPD page. For DATA - * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of - * 512-byte blocks to be transferred. + * The DATA SET MANAGEMENT TRIM payload is a whole number of 512-byte + * pages (each holding up to ATA_MAX_TRIM_RNUM LBA Range Entries), + * independent of the logical sector size. Only use as many pages as + * are needed to describe the request, capped at max_pages. */ - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != ATA_SECT_SIZE) + n_pages = DIV_ROUND_UP(DIV_ROUND_UP(n_block, U16_MAX), + ATA_MAX_TRIM_RNUM); + n_pages = clamp(n_pages, 1U, max_pages); + size = (size_t)n_pages * ATA_SECT_SIZE; + + if (ata_format_dsm_trim_descr(scmd, size, block, n_block) != size) goto invalid_param_len; + /* + * For DATA SET MANAGEMENT TRIM the COUNT field (aka nsect) is the + * number of 512-byte pages to be transferred. + */ if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { /* Newer devices support queued TRIM commands */ tf->protocol = ATA_PROT_NCQ; tf->command = ATA_CMD_FPDMA_SEND; tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f; tf->nsect = qc->hw_tag << 3; - tf->hob_feature = (size / 512) >> 8; - tf->feature = size / 512; + tf->hob_feature = n_pages >> 8; + tf->feature = n_pages; tf->auxiliary = 1; } else { tf->protocol = ATA_PROT_DMA; tf->hob_feature = 0; tf->feature = ATA_DSM_TRIM; - tf->hob_nsect = (size / 512) >> 8; - tf->nsect = size / 512; + tf->hob_nsect = n_pages >> 8; + tf->nsect = n_pages; tf->command = ATA_CMD_DSM; } @@ -3572,9 +3625,9 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) ata_qc_set_pc_nbytes(qc); /* - * The DSM TRIM payload is a single 512-byte page, which may be smaller - * than the WRITE SAME data-out buffer (one logical block); only - * transfer that page so the length matches the COUNT field. + * The DSM TRIM payload (size) may be smaller than the WRITE SAME + * data-out buffer (one logical block); only transfer the pages that + * were actually built so the transfer length matches the COUNT field. */ qc->nbytes = size; diff --git a/include/linux/ata.h b/include/linux/ata.h index 8fd48bcb2a46..ac5616a9668b 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -75,6 +75,7 @@ enum { ATA_ID_HW_CONFIG = 93, ATA_ID_SPG = 98, ATA_ID_LBA_CAPACITY_2 = 100, + ATA_ID_MAX_PAGES_PER_DSM = 105, ATA_ID_SECTOR_SIZE = 106, ATA_ID_WWN = 108, ATA_ID_LOGICAL_SECTOR_SIZE = 117, /* and 118 */ @@ -928,6 +929,18 @@ static inline bool ata_id_has_trim(const u16 *id) return false; } +static inline u16 ata_id_dsm_max_pages(const u16 *id) +{ + /* + * IDENTIFY DEVICE word 105: MAX PAGES PER DSM COMMAND. Maximum number + * of 512-byte pages of LBA Range Entries the device accepts in a + * single DATA SET MANAGEMENT command. Zero means the device does not + * specify a limit. The field is reserved unless TRIM is supported, so + * callers must gate on ata_id_has_trim(). + */ + return id[ATA_ID_MAX_PAGES_PER_DSM]; +} + static inline bool ata_id_has_zero_after_trim(const u16 *id) { /* DSM supported, deterministic read, and read zero after trim set */ From 7c8b3f6578bb874ba9d334aef0e51fe931ce6c04 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sun, 14 Jun 2026 14:39:39 +0900 Subject: [PATCH 08/27] scsi: scsi_debug: move ASC and ASCQ definitions to scsi_proto.h The scsi_debug driver internally defines lots of SCSI additional sense codes (ASC) and additional sense code qualifiers (ASCQ). Move these definitions to include/scsi/scsi_proto.h so that they can be reused elsewhere in the SCSI and ATA code. This also makes the scsi_debug.c file a little smaller. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 46 ----------------------------------- include/scsi/scsi_proto.h | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 9d1c9c41d0f9..4a95e6bae38b 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -67,52 +67,6 @@ static const char *sdebug_version_date = "20210520"; #define MY_NAME "scsi_debug" -/* Additional Sense Code (ASC) */ -#define NO_ADDITIONAL_SENSE 0x0 -#define OVERLAP_ATOMIC_COMMAND_ASC 0x0 -#define OVERLAP_ATOMIC_COMMAND_ASCQ 0x23 -#define FILEMARK_DETECTED_ASCQ 0x1 -#define EOP_EOM_DETECTED_ASCQ 0x2 -#define BEGINNING_OF_P_M_DETECTED_ASCQ 0x4 -#define EOD_DETECTED_ASCQ 0x5 -#define LOGICAL_UNIT_NOT_READY 0x4 -#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 -#define UNRECOVERED_READ_ERR 0x11 -#define PARAMETER_LIST_LENGTH_ERR 0x1a -#define INVALID_OPCODE 0x20 -#define LBA_OUT_OF_RANGE 0x21 -#define INVALID_FIELD_IN_CDB 0x24 -#define INVALID_FIELD_IN_PARAM_LIST 0x26 -#define WRITE_PROTECTED 0x27 -#define UA_READY_ASC 0x28 -#define UA_RESET_ASC 0x29 -#define UA_CHANGED_ASC 0x2a -#define TOO_MANY_IN_PARTITION_ASC 0x3b -#define TARGET_CHANGED_ASC 0x3f -#define LUNS_CHANGED_ASCQ 0x0e -#define INSUFF_RES_ASC 0x55 -#define INSUFF_RES_ASCQ 0x3 -#define POWER_ON_RESET_ASCQ 0x0 -#define POWER_ON_OCCURRED_ASCQ 0x1 -#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */ -#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */ -#define CAPACITY_CHANGED_ASCQ 0x9 -#define SAVING_PARAMS_UNSUP 0x39 -#define TRANSPORT_PROBLEM 0x4b -#define THRESHOLD_EXCEEDED 0x5d -#define LOW_POWER_COND_ON 0x5e -#define MISCOMPARE_VERIFY_ASC 0x1d -#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */ -#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 -#define WRITE_ERROR_ASC 0xc -#define UNALIGNED_WRITE_ASCQ 0x4 -#define WRITE_BOUNDARY_ASCQ 0x5 -#define READ_INVDATA_ASCQ 0x6 -#define READ_BOUNDARY_ASCQ 0x7 -#define ATTEMPT_ACCESS_GAP 0x9 -#define INSUFF_ZONE_ASCQ 0xe -/* see drivers/scsi/sense_codes.h */ - /* Additional Sense Code Qualifier (ASCQ) */ #define ACK_NAK_TO 0x3 diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index f64385cde5b9..965cde7ebc5b 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -233,6 +233,57 @@ enum sam_status { #define MISCOMPARE 0x0e #define COMPLETED 0x0f +/* + * Additional Sense Codes (ASC). + */ +#define NO_ADDITIONAL_SENSE 0x00 +#define OVERLAP_ATOMIC_COMMAND_ASC 0x00 +#define LOGICAL_UNIT_NOT_READY 0x04 +#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8 +#define WRITE_ERROR_ASC 0x0c +#define UNRECOVERED_READ_ERR 0x11 +#define PARAMETER_LIST_LENGTH_ERR 0x1a +#define MISCOMPARE_VERIFY_ASC 0x1d +#define INVALID_OPCODE 0x20 +#define LBA_OUT_OF_RANGE 0x21 +#define INVALID_FIELD_IN_CDB 0x24 +#define INVALID_FIELD_IN_PARAM_LIST 0x26 +#define WRITE_PROTECTED 0x27 +#define UA_READY_ASC 0x28 +#define UA_RESET_ASC 0x29 +#define UA_CHANGED_ASC 0x2a +#define TOO_MANY_IN_PARTITION_ASC 0x3b +#define TARGET_CHANGED_ASC 0x3f +#define SAVING_PARAMS_UNSUP 0x39 +#define TRANSPORT_PROBLEM 0x4b +#define INSUFF_RES_ASC 0x55 +#define LOW_POWER_COND_ON 0x5e +#define THRESHOLD_EXCEEDED 0x5d + +/* + * Additional Sense Code Qualifiers (ASCQ). + */ +#define POWER_ON_RESET_ASCQ 0x00 +#define MODE_CHANGED_ASCQ 0x01 /* mode parameters changed */ +#define FILEMARK_DETECTED_ASCQ 0x01 +#define POWER_ON_OCCURRED_ASCQ 0x01 +#define MICROCODE_CHANGED_ASCQ 0x01 /* with TARGET_CHANGED_ASC */ +#define BUS_RESET_ASCQ 0x02 /* scsi bus reset occurred */ +#define EOP_EOM_DETECTED_ASCQ 0x02 +#define INSUFF_RES_ASCQ 0x03 +#define BEGINNING_OF_P_M_DETECTED_ASCQ 0x04 +#define UNALIGNED_WRITE_ASCQ 0x04 +#define EOD_DETECTED_ASCQ 0x05 +#define WRITE_BOUNDARY_ASCQ 0x05 +#define READ_INVDATA_ASCQ 0x06 +#define READ_BOUNDARY_ASCQ 0x07 +#define CAPACITY_CHANGED_ASCQ 0x09 +#define ATTEMPT_ACCESS_GAP 0x09 +#define LUNS_CHANGED_ASCQ 0x0e +#define INSUFF_ZONE_ASCQ 0x0e +#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16 +#define OVERLAP_ATOMIC_COMMAND_ASCQ 0x23 + /* * DEVICE TYPES * Please keep them in 0x%02x format for $MODALIAS to work From 2a0adbb80a2f7c5d33baea981dacb8ee956b8677 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 22 Jun 2026 16:40:07 +0900 Subject: [PATCH 09/27] scsi: define depopulation capabilities related service actions Add to include/scsi/scsi_proto.h the definition of the four service actions of the SERVICE ACTION IN (16) command for the storage element depopulation and restoration capabilities, as defined in the SBC5 and ZBC2 specifications. These are: - SAI_GET_PHYSICAL_ELEMENT_STATUS (GET PHYSICAL ELEMENT STATUS command) - SAI_REMOVE_ELEMENT_AND_TRUNCATE (REMOVE ELEMENT AND TRUNCATE command) - SAI_RESTORE_ELEMENTS_AND_REBUILD (RESTORE ELEMENTS AND REBUILD command) - SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES (REMOVE ELEMENT AND MODIFY ZONES command) The physical element types and physical element health values reported by the GET PHYSICAL ELEMENT STATUS command are also defined. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- include/scsi/scsi_proto.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index 965cde7ebc5b..c6b4a4dc8d9c 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -129,6 +129,10 @@ #define SAI_GET_LBA_STATUS 0x12 #define SAI_REPORT_REFERRALS 0x13 #define SAI_GET_STREAM_STATUS 0x16 +#define SAI_GET_PHYSICAL_ELEMENT_STATUS 0x17 +#define SAI_REMOVE_ELEMENT_AND_TRUNCATE 0x18 +#define SAI_RESTORE_ELEMENTS_AND_REBUILD 0x19 +#define SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES 0x1a /* values for maintenance in */ #define MI_REPORT_IDENTIFYING_INFORMATION 0x05 #define MI_REPORT_TARGET_PGS 0x0a @@ -464,6 +468,25 @@ enum zbc_zone_alignment_method { ZBC_CONSTANT_ZONE_START_OFFSET = 0x8, }; +/* SCSI physical element types */ +enum scsi_phys_element_type { + SCSI_PHYS_ELEM_TYPE_ALL_ACCESS_STORAGE = 0x1, + SCSI_PHYS_ELEM_TYPE_FRAC_ACCESS_STORAGE = 0x2, +}; + +/* SCSI physical element health. */ +enum scsi_phys_element_health { + SCSI_PHYS_ELEM_HEALTH_NOT_REPORTED = 0x00, + SCSI_PHYS_ELEM_HEALTH_WITHIN_SPEC_LIMITS = 0x01, + SCSI_PHYS_ELEM_HEALTH_AT_SPEC_LIMITS = 0x64, + SCSI_PHYS_ELEM_HEALTH_OUTSIDE_SPEC_LIMITS = 0x65, + SCSI_PHYS_ELEM_HEALTH_DEPOP_REVOKE_ERR = 0xFB, + SCSI_PHYS_ELEM_HEALTH_DEPOP_REVOKE_IN_PROGRESS = 0xFC, + SCSI_PHYS_ELEM_HEALTH_DEPOP_ERR = 0xFD, + SCSI_PHYS_ELEM_HEALTH_DEPOP_IN_PROGRESS = 0xFE, + SCSI_PHYS_ELEM_HEALTH_DEPOP_OK = 0xFF, +}; + /* Version descriptor values for INQUIRY */ enum scsi_version_descriptor { SCSI_VERSION_DESCRIPTOR_FCP4 = 0x0a40, From ee1badfde360e3b95289ed359b4416f0fbb66eb1 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 6 Jul 2026 12:27:39 +0900 Subject: [PATCH 10/27] ata: libata: improve the definition of device flags The flags field of struct ata_device has the unsigned long type. Define all the ATA_DFLAG_XXX flags using a 1UL bit shift to match this type, thus avoiding flags to become signed values (e.g. for bit 31 flag). To avoid all other values defined in the same enum as the ATA_DFLAG_XXX flags to implicitly also become unsigned long values, move the device flags definition to a separate enum. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen --- include/linux/libata.h | 85 ++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/include/linux/libata.h b/include/linux/libata.h index 96e626d6a7ca..736ba8a6a77b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -121,6 +121,50 @@ enum { ATA_QUIRK_NO_FUA = BIT_ULL(__ATA_QUIRK_NO_FUA), }; +/* + * struct ata_device flags + */ +enum { + ATA_DFLAG_LBA = (1UL << 0), /* device supports LBA */ + ATA_DFLAG_LBA48 = (1UL << 1), /* device supports LBA48 */ + ATA_DFLAG_CDB_INTR = (1UL << 2), /* device asserts INTRQ when ready for CDB */ + ATA_DFLAG_NCQ = (1UL << 3), /* device supports NCQ */ + ATA_DFLAG_FLUSH_EXT = (1UL << 4), /* do FLUSH_EXT instead of FLUSH */ + ATA_DFLAG_ACPI_PENDING = (1UL << 5), /* ACPI resume action pending */ + ATA_DFLAG_ACPI_FAILED = (1UL << 6), /* ACPI on devcfg has failed */ + ATA_DFLAG_AN = (1UL << 7), /* AN configured */ + ATA_DFLAG_TRUSTED = (1UL << 8), /* device supports trusted send/recv */ + ATA_DFLAG_FUA = (1UL << 9), /* device supports FUA */ + ATA_DFLAG_DMADIR = (1UL << 10), /* device requires DMADIR */ + ATA_DFLAG_NCQ_SEND_RECV = (1UL << 11), /* device supports NCQ SEND and RECV */ + ATA_DFLAG_NCQ_PRIO = (1UL << 12), /* device supports NCQ priority */ + ATA_DFLAG_CDL = (1UL << 13), /* supports cmd duration limits */ + ATA_DFLAG_CFG_MASK = (1UL << 14) - 1, + + ATA_DFLAG_PIO = (1UL << 14), /* device limited to PIO mode */ + ATA_DFLAG_NCQ_OFF = (1UL << 15), /* device limited to non-NCQ mode */ + ATA_DFLAG_SLEEPING = (1UL << 16), /* device is sleeping */ + ATA_DFLAG_DUBIOUS_XFER = (1UL << 17), /* data transfer not verified */ + ATA_DFLAG_NO_UNLOAD = (1UL << 18), /* device doesn't support unload */ + ATA_DFLAG_UNLOCK_HPA = (1UL << 19), /* unlock HPA */ + ATA_DFLAG_INIT_MASK = (1UL << 20) - 1, + + ATA_DFLAG_NCQ_PRIO_ENABLED = (1UL << 20), /* Priority cmds sent to dev */ + ATA_DFLAG_CDL_ENABLED = (1UL << 21), /* cmd duration limits is enabled */ + ATA_DFLAG_RESUMING = (1UL << 22), /* Device is resuming */ + ATA_DFLAG_DETACH = (1UL << 24), + ATA_DFLAG_DETACHED = (1UL << 25), + ATA_DFLAG_DA = (1UL << 26), /* device supports Device Attention */ + ATA_DFLAG_DEVSLP = (1UL << 27), /* device supports Device Sleep */ + ATA_DFLAG_ACPI_DISABLED = (1UL << 28), /* ACPI for the device is disabled */ + ATA_DFLAG_D_SENSE = (1UL << 29), /* Descriptor sense requested */ + + ATA_DFLAG_FEATURES_MASK = (ATA_DFLAG_TRUSTED | ATA_DFLAG_DA | \ + ATA_DFLAG_DEVSLP | ATA_DFLAG_NCQ_SEND_RECV | \ + ATA_DFLAG_NCQ_PRIO | ATA_DFLAG_FUA | \ + ATA_DFLAG_CDL) +}; + enum { /* various global constants */ LIBATA_MAX_PRD = ATA_MAX_PRD / 2, @@ -146,46 +190,7 @@ enum { ATA_TFLAG_FUA = (1 << 5), /* enable FUA */ ATA_TFLAG_POLLING = (1 << 6), /* set nIEN to 1 and use polling */ - /* struct ata_device stuff */ - ATA_DFLAG_LBA = (1 << 0), /* device supports LBA */ - ATA_DFLAG_LBA48 = (1 << 1), /* device supports LBA48 */ - ATA_DFLAG_CDB_INTR = (1 << 2), /* device asserts INTRQ when ready for CDB */ - ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */ - ATA_DFLAG_FLUSH_EXT = (1 << 4), /* do FLUSH_EXT instead of FLUSH */ - ATA_DFLAG_ACPI_PENDING = (1 << 5), /* ACPI resume action pending */ - ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */ - ATA_DFLAG_AN = (1 << 7), /* AN configured */ - ATA_DFLAG_TRUSTED = (1 << 8), /* device supports trusted send/recv */ - ATA_DFLAG_FUA = (1 << 9), /* device supports FUA */ - ATA_DFLAG_DMADIR = (1 << 10), /* device requires DMADIR */ - ATA_DFLAG_NCQ_SEND_RECV = (1 << 11), /* device supports NCQ SEND and RECV */ - ATA_DFLAG_NCQ_PRIO = (1 << 12), /* device supports NCQ priority */ - ATA_DFLAG_CDL = (1 << 13), /* supports cmd duration limits */ - ATA_DFLAG_CFG_MASK = (1 << 14) - 1, - - ATA_DFLAG_PIO = (1 << 14), /* device limited to PIO mode */ - ATA_DFLAG_NCQ_OFF = (1 << 15), /* device limited to non-NCQ mode */ - ATA_DFLAG_SLEEPING = (1 << 16), /* device is sleeping */ - ATA_DFLAG_DUBIOUS_XFER = (1 << 17), /* data transfer not verified */ - ATA_DFLAG_NO_UNLOAD = (1 << 18), /* device doesn't support unload */ - ATA_DFLAG_UNLOCK_HPA = (1 << 19), /* unlock HPA */ - ATA_DFLAG_INIT_MASK = (1 << 20) - 1, - - ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 20), /* Priority cmds sent to dev */ - ATA_DFLAG_CDL_ENABLED = (1 << 21), /* cmd duration limits is enabled */ - ATA_DFLAG_RESUMING = (1 << 22), /* Device is resuming */ - ATA_DFLAG_DETACH = (1 << 24), - ATA_DFLAG_DETACHED = (1 << 25), - ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */ - ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */ - ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */ - ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */ - - ATA_DFLAG_FEATURES_MASK = (ATA_DFLAG_TRUSTED | ATA_DFLAG_DA | \ - ATA_DFLAG_DEVSLP | ATA_DFLAG_NCQ_SEND_RECV | \ - ATA_DFLAG_NCQ_PRIO | ATA_DFLAG_FUA | \ - ATA_DFLAG_CDL), - + /* sturct ata_device class. */ ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ ATA_DEV_ATA_UNSUP = 2, /* ATA device (unsupported) */ From 19598e92c8b76c77b1293eb58829cd27b1dd6d96 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 19 Jun 2026 18:29:32 +0900 Subject: [PATCH 11/27] ata: libata-scsi: improve ata_get_xlat_func ata_get_xlat_func() is given only the opcode of a SCSI command to determine the ATA command to translate to. This makes it impossible to translate SCSI commands such as SERVICE ACTION IN which need a service action field to fully specify the command. In preparation for supporting the translation of the SERVICE ACTION IN command with service actions different from the SAI_READ_CAPACITY_16 (READ CAPACITY 16), change ata_get_xlat_func() to take a pointer to a SCSI command CDB so that all fields of the SCSI command to translate can be easily inspected. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5cddb63a6bc6..f5c838ca0ce9 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4606,7 +4606,7 @@ static unsigned int ata_scsi_var_len_cdb_xlat(struct ata_queued_cmd *qc) /** * ata_get_xlat_func - check if SCSI to ATA translation is possible * @dev: ATA device - * @cmd: SCSI command opcode to consider + * @cdb: CDB of the SCSI command to consider * * Look up the SCSI command given, and determine whether the * SCSI command is to be translated or simulated. @@ -4615,9 +4615,10 @@ static unsigned int ata_scsi_var_len_cdb_xlat(struct ata_queued_cmd *qc) * Pointer to translation function if possible, %NULL if not. */ -static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) +static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, + u8 *cdb) { - switch (cmd) { + switch (cdb[0]) { case READ_6: case READ_10: case READ_16: @@ -4748,7 +4749,8 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_port *ap) __must_hold(ap->lock) { - u8 scsi_op = scmd->cmnd[0]; + u8 *cdb = scmd->cmnd; + u8 scsi_op = cdb[0]; ata_xlat_func_t xlat_func; /* @@ -4768,7 +4770,7 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, if (unlikely(scmd->cmd_len > dev->cdb_len)) goto bad_cdb_len; - xlat_func = ata_get_xlat_func(dev, scsi_op); + xlat_func = ata_get_xlat_func(dev, cdb); } else if (likely((scsi_op != ATA_16) || !atapi_passthru16)) { /* relay SCSI command to ATAPI device */ int len = COMMAND_SIZE(scsi_op); @@ -4784,7 +4786,7 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, if (unlikely(scmd->cmd_len > 16)) goto bad_cdb_len; - xlat_func = ata_get_xlat_func(dev, scsi_op); + xlat_func = ata_get_xlat_func(dev, cdb); } if (xlat_func) From 4ba5eb54c2c8671f741a161b4f0d5162a4cb9b1b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:04:41 +0900 Subject: [PATCH 12/27] ata: libata-core: detect support for depopulation capabilities Introduce the device flags ATA_DFLAG_DEPOP to indicate support by a device for the basic commands of the storage element depopulation feature set, that is, the GET PHYSICAL ELEMENT STATUS and REMOVE ELEMENT AND TRUNCATE commands. The device flag ATA_DFLAG_DEPOP_RESTORE flag is introduced to indicate support for the RESTORE ELEMENTS AND REBUILD command. Both flags are obtained from the command support bits of the qword at bytes 152 to 159 of the supported capabilities log page. For ZAC devices, the device flag ATA_DFLAG_DEPOP_MODIFY is introduced to indicate support for the REMOVE ELEMENT AND MODIFY ZONES command. This support is indicated by the REMOVE ELEMENT AND MODIFY ZONES SUPPORTED bit in the qword at byte 8 to 15 of the zoned device information log page. The function ata_dev_config_depop() is introduced to set these flags based on the content of the supported capabilities log and zoned device information log. As per the ACS specifications, NCQ autosense support is also mandatory if these flags are set. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-core.c | 73 +++++++++++++++++++++++++++++++++++++-- include/linux/libata.h | 41 ++++++++++++---------- 2 files changed, 94 insertions(+), 20 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5121faf9738e..d893c916df0b 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2705,6 +2705,71 @@ not_supported: ata_dev_cleanup_cdl_resources(dev); } +static void ata_dev_config_depop(struct ata_device *dev) +{ + unsigned int err_mask; + u64 val; + + /* Ignore old drives. */ + if (ata_id_major_version(dev->id) < 11) + goto not_supported; + + /* NCQ Autosense is required. */ + if (!ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES) || + !ata_id_has_ncq_autosense(dev->id)) + goto not_supported; + + err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE, + ATA_LOG_SUPPORTED_CAPABILITIES, + dev->sector_buf, 1); + if (err_mask) + goto not_supported; + + /* Check depopulation capabilities bits. */ + val = get_unaligned_le64(&dev->sector_buf[152]); + if (!(val & BIT_ULL(63))) + goto not_supported; + + /* + * Support for at least the GET PHYSICAL ELEMENT STATUS and + * REMOVE ELEMENT AND TRUNCATE commands is mandated. + */ + if (!(val & BIT_ULL(0)) || !(val & BIT_ULL(1))) + goto not_supported; + + dev->flags |= ATA_DFLAG_DEPOP; + + /* Check if RESTORE ELEMENTS AND REBUILD is supported. */ + if (val & BIT_ULL(2)) + dev->flags |= ATA_DFLAG_DEPOP_RESTORE; + + /* + * For ZAC devices, check if REMOVE ELEMENT AND MODIFY ZONES is + * supported. + */ + if (dev->class != ATA_DEV_ZAC) + return; + + err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE, + ATA_LOG_ZONED_INFORMATION, + dev->sector_buf, 1); + if (err_mask) + return; + + val = get_unaligned_le64(&dev->sector_buf[8]); + if (!(val & BIT_ULL(63))) + return; + + if (val & BIT_ULL(1)) + dev->flags |= ATA_DFLAG_DEPOP_MODIFY; + + return; + +not_supported: + dev->flags &= ~(ATA_DFLAG_DEPOP | ATA_DFLAG_DEPOP_RESTORE | + ATA_DFLAG_DEPOP_MODIFY); +} + static int ata_dev_config_lba(struct ata_device *dev) { const u16 *id = dev->id; @@ -2942,7 +3007,7 @@ static void ata_dev_print_features(struct ata_device *dev) return; ata_dev_info(dev, - "Features:%s%s%s%s%s%s%s%s%s%s\n", + "Features:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", dev->flags & ATA_DFLAG_FUA ? " FUA" : "", dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "", dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "", @@ -2952,7 +3017,10 @@ static void ata_dev_print_features(struct ata_device *dev) dev->flags & ATA_DFLAG_NCQ_SEND_RECV ? " NCQ-sndrcv" : "", dev->flags & ATA_DFLAG_NCQ_PRIO ? " NCQ-prio" : "", dev->flags & ATA_DFLAG_CDL ? " CDL" : "", - dev->cpr_log ? " CPR" : ""); + dev->cpr_log ? " CPR" : "", + dev->flags & ATA_DFLAG_DEPOP ? " Depop" : "", + dev->flags & ATA_DFLAG_DEPOP_RESTORE ? " Depop-Restore" : "", + dev->flags & ATA_DFLAG_DEPOP_MODIFY ? " Depop-Modify" : ""); } /** @@ -3115,6 +3183,7 @@ int ata_dev_configure(struct ata_device *dev) ata_dev_config_trusted(dev); ata_dev_config_cpr(dev); ata_dev_config_cdl(dev); + ata_dev_config_depop(dev); dev->cdb_len = 32; if (print_info) diff --git a/include/linux/libata.h b/include/linux/libata.h index 736ba8a6a77b..3703ef433bd4 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -139,30 +139,35 @@ enum { ATA_DFLAG_NCQ_SEND_RECV = (1UL << 11), /* device supports NCQ SEND and RECV */ ATA_DFLAG_NCQ_PRIO = (1UL << 12), /* device supports NCQ priority */ ATA_DFLAG_CDL = (1UL << 13), /* supports cmd duration limits */ - ATA_DFLAG_CFG_MASK = (1UL << 14) - 1, + ATA_DFLAG_DEPOP = (1UL << 14), /* supports depopulation capability */ + ATA_DFLAG_DEPOP_RESTORE = (1UL << 15), /* supports depopulation restoration */ + ATA_DFLAG_DEPOP_MODIFY = (1UL << 16), /* supports zoned depopulation */ + ATA_DFLAG_CFG_MASK = (1UL << 17) - 1, - ATA_DFLAG_PIO = (1UL << 14), /* device limited to PIO mode */ - ATA_DFLAG_NCQ_OFF = (1UL << 15), /* device limited to non-NCQ mode */ - ATA_DFLAG_SLEEPING = (1UL << 16), /* device is sleeping */ - ATA_DFLAG_DUBIOUS_XFER = (1UL << 17), /* data transfer not verified */ - ATA_DFLAG_NO_UNLOAD = (1UL << 18), /* device doesn't support unload */ - ATA_DFLAG_UNLOCK_HPA = (1UL << 19), /* unlock HPA */ - ATA_DFLAG_INIT_MASK = (1UL << 20) - 1, + ATA_DFLAG_PIO = (1UL << 17), /* device limited to PIO mode */ + ATA_DFLAG_NCQ_OFF = (1UL << 18), /* device limited to non-NCQ mode */ + ATA_DFLAG_SLEEPING = (1UL << 19), /* device is sleeping */ + ATA_DFLAG_DUBIOUS_XFER = (1UL << 20), /* data transfer not verified */ + ATA_DFLAG_NO_UNLOAD = (1UL << 21), /* device doesn't support unload */ + ATA_DFLAG_UNLOCK_HPA = (1UL << 22), /* unlock HPA */ + ATA_DFLAG_INIT_MASK = (1UL << 23) - 1, - ATA_DFLAG_NCQ_PRIO_ENABLED = (1UL << 20), /* Priority cmds sent to dev */ - ATA_DFLAG_CDL_ENABLED = (1UL << 21), /* cmd duration limits is enabled */ - ATA_DFLAG_RESUMING = (1UL << 22), /* Device is resuming */ - ATA_DFLAG_DETACH = (1UL << 24), - ATA_DFLAG_DETACHED = (1UL << 25), - ATA_DFLAG_DA = (1UL << 26), /* device supports Device Attention */ - ATA_DFLAG_DEVSLP = (1UL << 27), /* device supports Device Sleep */ - ATA_DFLAG_ACPI_DISABLED = (1UL << 28), /* ACPI for the device is disabled */ - ATA_DFLAG_D_SENSE = (1UL << 29), /* Descriptor sense requested */ + ATA_DFLAG_NCQ_PRIO_ENABLED = (1UL << 23), /* Priority cmds sent to dev */ + ATA_DFLAG_CDL_ENABLED = (1UL << 24), /* cmd duration limits is enabled */ + ATA_DFLAG_RESUMING = (1UL << 25), /* Device is resuming */ + ATA_DFLAG_DETACH = (1UL << 26), + ATA_DFLAG_DETACHED = (1UL << 27), + ATA_DFLAG_DA = (1UL << 28), /* device supports Device Attention */ + ATA_DFLAG_DEVSLP = (1UL << 29), /* device supports Device Sleep */ + ATA_DFLAG_ACPI_DISABLED = (1UL << 30), /* ACPI for the device is disabled */ + ATA_DFLAG_D_SENSE = (1UL << 31), /* Descriptor sense requested */ ATA_DFLAG_FEATURES_MASK = (ATA_DFLAG_TRUSTED | ATA_DFLAG_DA | \ ATA_DFLAG_DEVSLP | ATA_DFLAG_NCQ_SEND_RECV | \ ATA_DFLAG_NCQ_PRIO | ATA_DFLAG_FUA | \ - ATA_DFLAG_CDL) + ATA_DFLAG_CDL | ATA_DFLAG_DEPOP | \ + ATA_DFLAG_DEPOP_RESTORE | + ATA_DFLAG_DEPOP_MODIFY) }; enum { From 0f0bfa1bcaa5dd6a19c3614b16dc94d625030313 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 19 Jun 2026 17:25:37 +0900 Subject: [PATCH 13/27] ata: libata-scsi: add support for the GET PHYSICAL ELEMENT STATUS command Define the translation for the GET PHYSICAL ELEMENT STATUS command (SERVICE ACTION IN command with service action SAI_GET_PHYSICAL_ELEMENT_STATUS) into the ATA command ATA_CMD_GET_PHYS_ELEMENT_STATUS with the new function ata_scsi_get_phys_element_status_xlat(). The reply of this function also needs translation from little endian to big endian. This is done with the completion callback ata_scsi_get_phys_element_status_complete(). The array of supported commands ata_supported_cmds is modified to add a new entry for this command. ata_scsi_cmd_is_supported() is also modified to correctly handle this new entry depending on the target device flag ATA_DFLAG_DEPOP being set. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 141 ++++++++++++++++++++++++++++++++++++++ include/linux/ata.h | 1 + 2 files changed, 142 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index f5c838ca0ce9..8723faa96c48 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3724,6 +3724,11 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = { .sa_valid = true, .sa = SAI_READ_CAPACITY_16 }, + { + .op = SERVICE_ACTION_IN_16, .cdb_len = 16, + .sa_valid = true, + .sa = SAI_GET_PHYSICAL_ELEMENT_STATUS + }, { .op = REPORT_LUNS, .cdb_len = 12 }, { .op = ATA_12, .cdb_len = 12 }, { .op = SECURITY_PROTOCOL_IN, .cdb_len = 12 }, @@ -3804,6 +3809,14 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, case ZBC_IN: case ZBC_OUT: return ata_dev_is_zoned(dev); + case SERVICE_ACTION_IN_16: + switch (sa) { + case SAI_GET_PHYSICAL_ELEMENT_STATUS: + return dev->flags & ATA_DFLAG_DEPOP; + default: + return true; + } + break; case SECURITY_PROTOCOL_IN: case SECURITY_PROTOCOL_OUT: return dev->flags & ATA_DFLAG_TRUSTED; @@ -4575,6 +4588,126 @@ static unsigned int ata_scsi_security_inout_xlat(struct ata_queued_cmd *qc) return 0; } +/* + * Convert T-13 little-endian field representation of GET PHYSICAL ELEMENT + * STATUS DMA command reply into T-10 big-endian field representation. + */ +static void ata_scsi_get_phys_element_status_complete(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + struct sg_mapping_iter miter; + unsigned int bytes = 0; + + lockdep_assert_held(qc->ap->lock); + + sg_miter_start(&miter, scsi_sglist(scmd), scsi_sg_count(scmd), + SG_MITER_TO_SG | SG_MITER_ATOMIC); + + while (sg_miter_next(&miter)) { + unsigned int offset = 0; + + if (bytes == 0) { + u32 num_desc, num_desc_returned, id; + u16 max_depop, cur_depop; + char *hdr; + + /* Swizzle the header */ + hdr = miter.addr; + num_desc = get_unaligned_le32(&hdr[0]); + num_desc_returned = get_unaligned_le32(&hdr[4]); + id = get_unaligned_le32(&hdr[8]); + max_depop = get_unaligned_le16(&hdr[12]); + cur_depop = get_unaligned_le16(&hdr[14]); + + put_unaligned_be32(num_desc, &hdr[0]); + put_unaligned_be32(num_desc_returned, &hdr[4]); + put_unaligned_be32(id, &hdr[8]); + put_unaligned_be16(max_depop, &hdr[12]); + put_unaligned_be16(cur_depop, &hdr[14]); + + offset += 32; + bytes += 32; + } + + /* Swizzle the descriptors. */ + while (offset < miter.length) { + char *desc; + u32 id; + u8 type; + + desc = miter.addr + offset; + id = get_unaligned_le32(&desc[4]); + put_unaligned_be32(id, &desc[4]); + + type = desc[14]; + if (type == SCSI_PHYS_ELEM_TYPE_ALL_ACCESS_STORAGE) { + u64 capacity = get_unaligned_le64(&desc[16]); + + put_unaligned_be64(capacity, &desc[16]); + } else { + u64 num_zones; + + id = get_unaligned_le32(&desc[16]); + num_zones = get_unaligned_le64(&desc[24]); + + put_unaligned_be32(id, &desc[16]); + put_unaligned_be64(num_zones, &desc[24]); + } + + offset += 32; + bytes += 32; + } + } + sg_miter_stop(&miter); + + ata_scsi_qc_complete(qc); +} + +static unsigned int +ata_scsi_get_phys_element_status_xlat(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + const u8 *cdb = scmd->cmnd; + struct ata_device *dev = qc->dev; + struct ata_taskfile *tf = &qc->tf; + u32 starting_element, len; + + /* ATA_CMD_GET_PHYS_ELEMENT_STATUS is a DMA command. */ + if (!(dev->flags & ATA_DFLAG_DEPOP) || !ata_dma_enabled(dev)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; + } + + len = get_unaligned_be32(&cdb[10]) / ATA_SECT_SIZE; + if (!len || len > U16_MAX) { + ata_scsi_set_invalid_field(dev, scmd, 10, 0); + return 1; + } + + tf->protocol = ATA_PROT_DMA; + tf->command = ATA_CMD_GET_PHYS_ELEMENT_STATUS; + tf->hob_feature = cdb[14]; + tf->hob_nsect = (len >> 8) & 0xff; + tf->nsect = len & 0xff; + + starting_element = get_unaligned_be32(&cdb[6]); + if (starting_element) { + tf->hob_lbal = (starting_element >> 24) & 0xff; + tf->lbah = (starting_element >> 16) & 0xff; + tf->lbam = (starting_element >> 8) & 0xff; + tf->lbal = starting_element & 0xff; + } + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + ata_qc_set_pc_nbytes(qc); + + qc->flags |= ATA_QCFLAG_RESULT_TF; + qc->complete_fn = ata_scsi_get_phys_element_status_complete; + + return 0; +} + /** * ata_scsi_var_len_cdb_xlat - SATL variable length CDB to Handler * @qc: Command to be translated @@ -4618,6 +4751,8 @@ static unsigned int ata_scsi_var_len_cdb_xlat(struct ata_queued_cmd *qc) static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 *cdb) { + u8 sa; + switch (cdb[0]) { case READ_6: case READ_10: @@ -4652,6 +4787,12 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, case MODE_SELECT_10: return ata_scsi_mode_select_xlat; + case SERVICE_ACTION_IN_16: + sa = cdb[1] & 0x1f; + if (sa == SAI_GET_PHYSICAL_ELEMENT_STATUS) + return ata_scsi_get_phys_element_status_xlat; + break; + case ZBC_IN: return ata_scsi_zbc_in_xlat; diff --git a/include/linux/ata.h b/include/linux/ata.h index ac5616a9668b..8b726d9bdda3 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -289,6 +289,7 @@ enum { ATA_CMD_SANITIZE_DEVICE = 0xB4, ATA_CMD_ZAC_MGMT_IN = 0x4A, ATA_CMD_ZAC_MGMT_OUT = 0x9F, + ATA_CMD_GET_PHYS_ELEMENT_STATUS = 0x12, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, From db496721cb0d369f34d7e0bf66692b050e2b6ff8 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:35:38 +0900 Subject: [PATCH 14/27] ata: libata-scsi: add support for the REMOVE ELEMENT AND TRUNCATE command Define the translation for the REMOVE ELEMENT AND TRUNCATE command (SERVICE ACTION IN command with service action SAI_REMOVE_ELEMENT_AND_TRUNCATE) into the ATA command ATA_CMD_REMOVE_ELEMENT_AND_TRUNCATE with the new function ata_scsi_remove_element_and_truncate_xlat() The array of supported commands ata_supported_cmds is modified to add a new entry for this command. ata_scsi_cmd_is_supported() is also modify to correctly handle this new entry depending on the target device flag ATA_DFLAG_DEPOP being set. The ATA command completion is handled using the function ata_scsi_depop_ua_cap_changed_complete() so that on a successful completion, a UNIT ATTENTION with the additional sense code set to CAPACITY DATA HAS CHANGED is raised. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 73 +++++++++++++++++++++++++++++++++++++++ include/linux/ata.h | 1 + 2 files changed, 74 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 8723faa96c48..49beefa46cf3 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3729,6 +3729,11 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = { .sa_valid = true, .sa = SAI_GET_PHYSICAL_ELEMENT_STATUS }, + { + .op = SERVICE_ACTION_IN_16, .cdb_len = 16, + .sa_valid = true, + .sa = SAI_REMOVE_ELEMENT_AND_TRUNCATE + }, { .op = REPORT_LUNS, .cdb_len = 12 }, { .op = ATA_12, .cdb_len = 12 }, { .op = SECURITY_PROTOCOL_IN, .cdb_len = 12 }, @@ -3812,6 +3817,7 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, case SERVICE_ACTION_IN_16: switch (sa) { case SAI_GET_PHYSICAL_ELEMENT_STATUS: + case SAI_REMOVE_ELEMENT_AND_TRUNCATE: return dev->flags & ATA_DFLAG_DEPOP; default: return true; @@ -4708,6 +4714,71 @@ ata_scsi_get_phys_element_status_xlat(struct ata_queued_cmd *qc) return 0; } +static void ata_scsi_depop_ua_cap_changed_complete(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + u8 *cdb = scmd->cmnd; + bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12; + bool is_success = qc->err_mask == 0; + + /* + * For successful non-passthrough commands, raise a UNIT ATTENTION with + * the additional sense code set to CAPACITY DATA HAS CHANGED to be + * raised. Note that this should be done only if the capacity has + * actually changed, which may not be the case if the element that was + * specified for depopulation was already depopulated. But a capacity + * change unit attention is harmless, so always raise the unit attention. + */ + if (is_success && !is_ata_passthru) + ata_scsi_set_sense(qc->dev, scmd, UNIT_ATTENTION, + UA_CHANGED_ASC, CAPACITY_CHANGED_ASCQ); + ata_scsi_qc_complete(qc); +} + +static unsigned int +ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + const u8 *cdb = scmd->cmnd; + struct ata_device *dev = qc->dev; + struct ata_taskfile *tf = &qc->tf; + u64 req_capacity; + u32 id; + + if (!(dev->flags & ATA_DFLAG_DEPOP)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; + } + + req_capacity = get_unaligned_be64(&cdb[2]); + if (req_capacity == 1) { + ata_scsi_set_invalid_field(dev, scmd, 2, 0); + return 1; + } + + id = get_unaligned_be32(&cdb[10]); + + tf->protocol = ATA_PROT_NODATA; + tf->command = ATA_CMD_REMOVE_ELEMENT_AND_TRUNCATE; + tf->hob_feature = (id >> 24) & 0xff; + tf->feature = (id >> 16) & 0xff; + tf->hob_nsect = (id >> 8) & 0xff; + tf->nsect = id & 0xff; + tf->hob_lbah = (req_capacity >> 40) & 0xff; + tf->hob_lbam = (req_capacity >> 32) & 0xff; + tf->hob_lbal = (req_capacity >> 24) & 0xff; + tf->lbah = (req_capacity >> 16) & 0xff; + tf->lbam = (req_capacity >> 8) & 0xff; + tf->lbal = req_capacity & 0xff; + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + qc->flags |= ATA_QCFLAG_RESULT_TF; + qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete; + + return 0; +} + /** * ata_scsi_var_len_cdb_xlat - SATL variable length CDB to Handler * @qc: Command to be translated @@ -4791,6 +4862,8 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, sa = cdb[1] & 0x1f; if (sa == SAI_GET_PHYSICAL_ELEMENT_STATUS) return ata_scsi_get_phys_element_status_xlat; + if (sa == SAI_REMOVE_ELEMENT_AND_TRUNCATE) + return ata_scsi_remove_element_and_truncate_xlat; break; case ZBC_IN: diff --git a/include/linux/ata.h b/include/linux/ata.h index 8b726d9bdda3..89ac27743f50 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -290,6 +290,7 @@ enum { ATA_CMD_ZAC_MGMT_IN = 0x4A, ATA_CMD_ZAC_MGMT_OUT = 0x9F, ATA_CMD_GET_PHYS_ELEMENT_STATUS = 0x12, + ATA_CMD_REMOVE_ELEMENT_AND_TRUNCATE = 0x7c, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, From 1e307ca61a9cdd8c3b721577e4889c47816654d8 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:44:43 +0900 Subject: [PATCH 15/27] ata: libata-scsi: add support for the RESTORE ELEMENTS AND REBUILD command Define the translation for the RESTORE ELEMENTS AND REBUILD command (SERVICE ACTION IN command with service action SAI_RESTORE_ELEMENTS_AND_REBUILD) into the ATA command ATA_CMD_RESTORE_ELEMENTS_AND_REBUILD with the new function ata_scsi_restore_elements_and_rebuild_xlat() The array of supported commands ata_supported_cmds is modified to add a new entry for this command. ata_scsi_cmd_is_supported() is also modify to correctly handle this new entry depending on the target device flag ATA_DFLAG_DEPOP_RESTORE being set. The ATA command completion is handled using the function ata_scsi_depop_ua_cap_changed_complete() so that on a successful completion, a UNIT ATTENTION with the additional sense code set to CAPACITY DATA HAS CHANGED is raised. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 37 +++++++++++++++++++++++++++++++++++-- include/linux/ata.h | 1 + 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 49beefa46cf3..5bdf54a9e9b1 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3734,6 +3734,11 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = { .sa_valid = true, .sa = SAI_REMOVE_ELEMENT_AND_TRUNCATE }, + { + .op = SERVICE_ACTION_IN_16, .cdb_len = 16, + .sa_valid = true, + .sa = SAI_RESTORE_ELEMENTS_AND_REBUILD + }, { .op = REPORT_LUNS, .cdb_len = 12 }, { .op = ATA_12, .cdb_len = 12 }, { .op = SECURITY_PROTOCOL_IN, .cdb_len = 12 }, @@ -3819,6 +3824,8 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, case SAI_GET_PHYSICAL_ELEMENT_STATUS: case SAI_REMOVE_ELEMENT_AND_TRUNCATE: return dev->flags & ATA_DFLAG_DEPOP; + case SAI_RESTORE_ELEMENTS_AND_REBUILD: + return dev->flags & ATA_DFLAG_DEPOP_RESTORE; default: return true; } @@ -4726,8 +4733,9 @@ static void ata_scsi_depop_ua_cap_changed_complete(struct ata_queued_cmd *qc) * the additional sense code set to CAPACITY DATA HAS CHANGED to be * raised. Note that this should be done only if the capacity has * actually changed, which may not be the case if the element that was - * specified for depopulation was already depopulated. But a capacity - * change unit attention is harmless, so always raise the unit attention. + * specified for depopulation was already depopulated, or we did not + * restore any removed element. But a capacity change unit attention is + * harmless, so always raise the unit attention. */ if (is_success && !is_ata_passthru) ata_scsi_set_sense(qc->dev, scmd, UNIT_ATTENTION, @@ -4779,6 +4787,29 @@ ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc) return 0; } +static unsigned int +ata_scsi_restore_elements_and_rebuild_xlat(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + struct ata_device *dev = qc->dev; + struct ata_taskfile *tf = &qc->tf; + + if (!(dev->flags & ATA_DFLAG_DEPOP_RESTORE)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; + } + + tf->protocol = ATA_PROT_NODATA; + tf->command = ATA_CMD_RESTORE_ELEMENTS_AND_REBUILD; + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + qc->flags |= ATA_QCFLAG_RESULT_TF; + qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete; + + return 0; +} + /** * ata_scsi_var_len_cdb_xlat - SATL variable length CDB to Handler * @qc: Command to be translated @@ -4864,6 +4895,8 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, return ata_scsi_get_phys_element_status_xlat; if (sa == SAI_REMOVE_ELEMENT_AND_TRUNCATE) return ata_scsi_remove_element_and_truncate_xlat; + if (sa == SAI_RESTORE_ELEMENTS_AND_REBUILD) + return ata_scsi_restore_elements_and_rebuild_xlat; break; case ZBC_IN: diff --git a/include/linux/ata.h b/include/linux/ata.h index 89ac27743f50..a1cd68cfb44f 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -291,6 +291,7 @@ enum { ATA_CMD_ZAC_MGMT_OUT = 0x9F, ATA_CMD_GET_PHYS_ELEMENT_STATUS = 0x12, ATA_CMD_REMOVE_ELEMENT_AND_TRUNCATE = 0x7c, + ATA_CMD_RESTORE_ELEMENTS_AND_REBUILD = 0x7d, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, From b448bb42bd1024014125c822211731fc2ae9a2ee Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:59:54 +0900 Subject: [PATCH 16/27] ata: libata-scsi: add support for the REMOVE ELEMENT AND MODIFY ZONES command Define the translation for the REMOVE ELEMENT AND MODIFY ZONES command (SERVICE ACTION IN command with service action SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES) into the ATA command ATA_CMD_REMOVE_ELEMENT_AND_MODIFY_ZONES with the new function ata_scsi_remove_element_and_modify_zones_xlat() The array of supported commands ata_supported_cmds is modified to add a new entry for this command. ata_scsi_cmd_is_supported() is also modify to correctly handle this new entry depending on the target device flag ATA_DFLAG_DEPOP being set, and the target device being a ZAC zoned device. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/ata.h | 1 + 2 files changed, 40 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5bdf54a9e9b1..1d225ee9eb86 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3739,6 +3739,11 @@ static const struct ata_scsi_cmd ata_supported_cmds[] = { .sa_valid = true, .sa = SAI_RESTORE_ELEMENTS_AND_REBUILD }, + { + .op = SERVICE_ACTION_IN_16, .cdb_len = 16, + .sa_valid = true, + .sa = SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES + }, { .op = REPORT_LUNS, .cdb_len = 12 }, { .op = ATA_12, .cdb_len = 12 }, { .op = SECURITY_PROTOCOL_IN, .cdb_len = 12 }, @@ -3826,6 +3831,8 @@ static bool ata_scsi_cmd_is_supported(struct ata_device *dev, u8 op, u16 sa, return dev->flags & ATA_DFLAG_DEPOP; case SAI_RESTORE_ELEMENTS_AND_REBUILD: return dev->flags & ATA_DFLAG_DEPOP_RESTORE; + case SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES: + return dev->flags & ATA_DFLAG_DEPOP_MODIFY; default: return true; } @@ -4787,6 +4794,36 @@ ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc) return 0; } +static unsigned int +ata_scsi_remove_element_and_modify_zones_xlat(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + const u8 *cdb = scmd->cmnd; + struct ata_device *dev = qc->dev; + struct ata_taskfile *tf = &qc->tf; + u32 id; + + if (!(dev->flags & ATA_DFLAG_DEPOP_MODIFY)) { + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; + } + + id = get_unaligned_be32(&cdb[10]); + + tf->protocol = ATA_PROT_NODATA; + tf->command = ATA_CMD_REMOVE_ELEMENT_AND_MODIFY_ZONES; + tf->hob_feature = (id >> 24) & 0xff; + tf->feature = (id >> 16) & 0xff; + tf->hob_nsect = (id >> 8) & 0xff; + tf->nsect = id & 0xff; + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + qc->flags |= ATA_QCFLAG_RESULT_TF; + + return 0; +} + static unsigned int ata_scsi_restore_elements_and_rebuild_xlat(struct ata_queued_cmd *qc) { @@ -4895,6 +4932,8 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, return ata_scsi_get_phys_element_status_xlat; if (sa == SAI_REMOVE_ELEMENT_AND_TRUNCATE) return ata_scsi_remove_element_and_truncate_xlat; + if (sa == SAI_REMOVE_ELEMENT_AND_MODIFY_ZONES) + return ata_scsi_remove_element_and_modify_zones_xlat; if (sa == SAI_RESTORE_ELEMENTS_AND_REBUILD) return ata_scsi_restore_elements_and_rebuild_xlat; break; diff --git a/include/linux/ata.h b/include/linux/ata.h index a1cd68cfb44f..fc21a2417b25 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -292,6 +292,7 @@ enum { ATA_CMD_GET_PHYS_ELEMENT_STATUS = 0x12, ATA_CMD_REMOVE_ELEMENT_AND_TRUNCATE = 0x7c, ATA_CMD_RESTORE_ELEMENTS_AND_REBUILD = 0x7d, + ATA_CMD_REMOVE_ELEMENT_AND_MODIFY_ZONES = 0x7e, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, From 9bf9aefdc0f5ef24674e7787e169b7d4d389009c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 13 Jul 2026 13:16:53 +0900 Subject: [PATCH 17/27] ata: libata-eh: make ata_eh_qc_complete() and ata_eh_qc_retry() static The functions ata_eh_qc_complete() and ata_eh_qc_retry() are used only in libata-eh.c. So remove the declaration of these functions from include/linux/libata.h and define them as static. While at it, add a missing blank line between variable declaration and code in these two functions. No functional changes intended. Signed-off-by: Damien Le Moal Reviewed-by: Niklas Cassel --- drivers/ata/libata-eh.c | 6 ++++-- include/linux/libata.h | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 05df7ea6954a..29ec0f7fef4a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1230,9 +1230,10 @@ static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) * Indicate to the mid and upper layers that an ATA command has * completed. To be used from EH. */ -void ata_eh_qc_complete(struct ata_queued_cmd *qc) +static void ata_eh_qc_complete(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; + scmd->retries = scmd->allowed; __ata_eh_qc_complete(qc); } @@ -1248,9 +1249,10 @@ void ata_eh_qc_complete(struct ata_queued_cmd *qc) * scmd->allowed is incremented for commands which get retried * due to unrelated failures (qc->err_mask is zero). */ -void ata_eh_qc_retry(struct ata_queued_cmd *qc) +static void ata_eh_qc_retry(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; + if (!qc->err_mask) scmd->allowed++; __ata_eh_qc_complete(qc); diff --git a/include/linux/libata.h b/include/linux/libata.h index 3703ef433bd4..18edb36c29fc 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1428,9 +1428,6 @@ extern int ata_port_freeze(struct ata_port *ap); extern void ata_eh_freeze_port(struct ata_port *ap); extern void ata_eh_thaw_port(struct ata_port *ap); -extern void ata_eh_qc_complete(struct ata_queued_cmd *qc); -extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); - extern void ata_std_error_handler(struct ata_port *ap) __must_hold(&ap->host->eh_mutex); extern void ata_std_sched_eh(struct ata_port *ap); From 5aef4cb3210d74f92dc6bb86a431a34be93528a6 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 10 Jul 2026 19:02:46 +0800 Subject: [PATCH 18/27] ata: pata_mpc52xx: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() call. Signed-off-by: Pan Chuang Reviewed-by: Hannes Reinecke Signed-off-by: Damien Le Moal --- drivers/ata/pata_mpc52xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 210a63283f62..1739743427cf 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -769,10 +769,8 @@ static int mpc52xx_ata_probe(struct platform_device *op) task_irq = bcom_get_task_irq(dmatsk); rv = devm_request_irq(&op->dev, task_irq, &mpc52xx_ata_task_irq, 0, "ATA task", priv); - if (rv) { - dev_err(&op->dev, "error requesting DMA IRQ\n"); + if (rv) goto err2; - } priv->dmatsk = dmatsk; /* Init the hw */ From 692a9643058cb2be676756238f778b97588ee51c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 15:20:03 -0700 Subject: [PATCH 19/27] ata: sata_mv: Use platform_get_irq() to get interrupt Extract platform_get_irq() out of the conditional block to run it unconditionally, replacing the direct use of irq_of_parse_and_map() for DT-based systems. This simplifies the probe logic and avoids the need for irq_dispose_mapping. Remove dead branch for platform_get_irq(). It intercepts 0 and returns -EINVAL, which goes to the first one. Assisted-by: Antigravity:Gemini-3.5-Flash Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_mv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 41647a56a9f4..a2bf9551a600 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4054,17 +4054,13 @@ static int mv_platform_probe(struct platform_device *pdev) n_ports); return -EINVAL; } - - irq = irq_of_parse_and_map(pdev->dev.of_node, 0); } else { mv_platform_data = dev_get_platdata(&pdev->dev); n_ports = mv_platform_data->n_ports; - irq = platform_get_irq(pdev, 0); } + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - if (!irq) - return -EINVAL; host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); From 427ac6f5ff7607da32862f2918ca6e4bd32252f0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 15:37:33 -0700 Subject: [PATCH 20/27] ata: sata_mv: use devm clock helpers Replace clk_get/clk_prepare_enable/clk_put with devm_clk_get_optional_enabled for both the main clock and per-port clocks. This eliminates the need for manual clock cleanup in probe error and remove paths. The err label is retained for phy_power_off cleanup, since devm_phy_optional_get does not manage phy power state. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_mv.c | 46 ++++++++++++------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index a2bf9551a600..0de4f9b8e811 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4088,21 +4088,19 @@ static int mv_platform_probe(struct platform_device *pdev) hpriv->base -= SATAHC0_REG_BASE; - hpriv->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(hpriv->clk)) { - dev_notice(&pdev->dev, "cannot get optional clkdev\n"); - } else { - rc = clk_prepare_enable(hpriv->clk); - if (rc) - goto err; - } + hpriv->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); + if (IS_ERR(hpriv->clk)) + return PTR_ERR(hpriv->clk); for (port = 0; port < n_ports; port++) { char port_number[16]; sprintf(port_number, "%d", port); - hpriv->port_clks[port] = clk_get(&pdev->dev, port_number); - if (!IS_ERR(hpriv->port_clks[port])) - clk_prepare_enable(hpriv->port_clks[port]); + hpriv->port_clks[port] = devm_clk_get_optional_enabled(&pdev->dev, port_number); + if (IS_ERR(hpriv->port_clks[port])) { + rc = PTR_ERR(hpriv->port_clks[port]); + hpriv->n_ports = port; + goto err; + } sprintf(port_number, "port%d", port); hpriv->port_phys[port] = devm_phy_optional_get(&pdev->dev, @@ -4116,8 +4114,8 @@ static int mv_platform_probe(struct platform_device *pdev) /* Cleanup only the initialized ports */ hpriv->n_ports = port; goto err; - } else - phy_power_on(hpriv->port_phys[port]); + } + phy_power_on(hpriv->port_phys[port]); } /* All the ports have been initialized */ @@ -4156,17 +4154,8 @@ static int mv_platform_probe(struct platform_device *pdev) return 0; err: - if (!IS_ERR(hpriv->clk)) { - clk_disable_unprepare(hpriv->clk); - clk_put(hpriv->clk); - } - for (port = 0; port < hpriv->n_ports; port++) { - if (!IS_ERR(hpriv->port_clks[port])) { - clk_disable_unprepare(hpriv->port_clks[port]); - clk_put(hpriv->port_clks[port]); - } + for (port = 0; port < hpriv->n_ports; port++) phy_power_off(hpriv->port_phys[port]); - } return rc; } @@ -4186,17 +4175,8 @@ static void mv_platform_remove(struct platform_device *pdev) int port; ata_host_detach(host); - if (!IS_ERR(hpriv->clk)) { - clk_disable_unprepare(hpriv->clk); - clk_put(hpriv->clk); - } - for (port = 0; port < host->n_ports; port++) { - if (!IS_ERR(hpriv->port_clks[port])) { - clk_disable_unprepare(hpriv->port_clks[port]); - clk_put(hpriv->port_clks[port]); - } + for (port = 0; port < host->n_ports; port++) phy_power_off(hpriv->port_phys[port]); - } } #ifdef CONFIG_PM_SLEEP From da75bb11166eb48f988d762a471e426955b27d03 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 13 Jul 2026 16:21:43 -0700 Subject: [PATCH 21/27] ata: pata_rb532_cf: use devm_platform_ioremap_resource() Replace the open-coded platform_get_resource() plus devm_ioremap() sequence with a single devm_platform_ioremap_resource() call, which folds the resource lookup and mapping into one step and returns an ERR_PTR on failure, checked with IS_ERR() and propagated via PTR_ERR(). Similar to platform_get_irq(), it can return -EPROBE_DEFER so move it early. The pata-rb532-cf platform device (arch/mips/rb532/devices.c) provides a single IORESOURCE_MEM window at the DEV1BASE chip-select, distinct from the other RB532 chip-selects, so the region reservation now performed by devm_platform_ioremap_resource() introduces no conflict. The mapped size is unchanged. Drop the redundant error message, as devm_platform_ioremap_resource() already logs on failure. Built for MIPS (rb532_defconfig) with LLVM=1; drivers/ata/pata_rb532_cf.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/pata_rb532_cf.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index fd81e75c9402..0144e597707e 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -103,16 +103,14 @@ static int rb532_pata_driver_probe(struct platform_device *pdev) { int irq; struct gpio_desc *gpiod; - struct resource *res; struct ata_host *ah; struct rb532_cf_info *info; + void __iomem *iobase; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no IOMEM resource found\n"); - return -EINVAL; - } + iobase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(iobase)) + return PTR_ERR(iobase); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -139,11 +137,7 @@ static int rb532_pata_driver_probe(struct platform_device *pdev) ah->private_data = info; info->gpio_line = gpiod; info->irq = irq; - - info->iobase = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!info->iobase) - return -ENOMEM; + info->iobase = iobase; rb532_pata_setup_ports(ah); From 6f9fb3cf9333684e57983bb50d165715b49b8ae9 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 14 Jul 2026 18:13:43 -0700 Subject: [PATCH 22/27] ata: pata_pxa: use devm_platform_ioremap_resource Replace the open-coded platform_get_resource() plus devm_ioremap() sequences for the CMD and CTL ports with devm_platform_ioremap_resource(), which fetches the resource, requests the region and maps it in one call. Switch the error checks to IS_ERR()/PTR_ERR() accordingly and drop the now-unused cmd_res and ctl_res variables. The DMA port keeps using devm_ioremap() as its resource is IORESOURCE_DMA. The CMD (res 0) and CTL (res 1) MEM resources are disjoint in the ATA task-file layout, so the added region reservation in devm_ioremap_resource() does not introduce overlap failures. Built for ARM (drivers/ata/pata_pxa.o) with LLVM=1. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/pata_pxa.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index 9f63bdfb8576..c816997dc0a9 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -161,8 +161,6 @@ static int pxa_ata_probe(struct platform_device *pdev) struct ata_host *host; struct ata_port *ap; struct pata_pxa_data *data; - struct resource *cmd_res; - struct resource *ctl_res; struct resource *dma_res; struct pata_pxa_pdata *pdata = dev_get_platdata(&pdev->dev); struct dma_slave_config config; @@ -181,20 +179,6 @@ static int pxa_ata_probe(struct platform_device *pdev) return -EINVAL; } - /* - * CMD port base address - */ - cmd_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(cmd_res == NULL)) - return -EINVAL; - - /* - * CTL port base address - */ - ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (unlikely(ctl_res == NULL)) - return -EINVAL; - /* * DMA port base address */ @@ -221,14 +205,12 @@ static int pxa_ata_probe(struct platform_device *pdev) ap->pio_mask = ATA_PIO4; ap->mwdma_mask = ATA_MWDMA2; - ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start, - resource_size(cmd_res)); - if (!ap->ioaddr.cmd_addr) - return -ENOMEM; - ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, - resource_size(ctl_res)); - if (!ap->ioaddr.ctl_addr) - return -ENOMEM; + ap->ioaddr.cmd_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ap->ioaddr.cmd_addr)) + return PTR_ERR(ap->ioaddr.cmd_addr); + ap->ioaddr.ctl_addr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(ap->ioaddr.ctl_addr)) + return PTR_ERR(ap->ioaddr.ctl_addr); ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start, resource_size(dma_res)); if (!ap->ioaddr.bmdma_addr) From a807f19c0e9c667c5c9a209bd7ff0c851222cf35 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 14 Jul 2026 18:14:38 -0700 Subject: [PATCH 23/27] ata: sata_highbank: use devm_platform_ioremap_resource Replace the open-coded platform_get_resource() plus devm_ioremap() sequence with devm_platform_ioremap_resource(), which fetches the resource, requests the region and maps it in one call. Switch the error check to IS_ERR()/PTR_ERR() and drop the now-unused struct resource pointer. There is a single IORESOURCE_MEM resource, and the SATA PHY registers are mapped separately via of_iomap() from distinct DT nodes, so the region reservation added by devm_ioremap_resource() is exclusive and does not introduce overlap failures. The mem-based ata_port_desc() line is dropped along with the resource pointer; the remaining per-port description is retained. Built for ARM (drivers/ata/sata_highbank.o) with LLVM=1. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_highbank.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index 3421039f4bae..a0b6e197cfb5 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -455,7 +455,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; struct ecx_plat_data *pdata; struct ata_host *host; - struct resource *mem; + void __iomem *mmio; int irq; int i; int rc; @@ -463,11 +463,9 @@ static int ahci_highbank_probe(struct platform_device *pdev) struct ata_port_info pi = ahci_highbank_port_info; const struct ata_port_info *ppi[] = { &pi, NULL }; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(dev, "no mmio space\n"); - return -EINVAL; - } + mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -488,12 +486,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) hpriv->irq = irq; hpriv->flags |= (unsigned long)pi.private_data; - - hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); - if (!hpriv->mmio) { - dev_err(dev, "can't map %pR\n", mem); - return -ENOMEM; - } + hpriv->mmio = mmio; rc = highbank_initialize_phys(dev, hpriv->mmio); if (rc) @@ -537,7 +530,6 @@ static int ahci_highbank_probe(struct platform_device *pdev) for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - ata_port_desc(ap, "mmio %pR", mem); ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); /* set enclosure management message type */ From 5e8e8c42ce25782ff738a68724f8f5b93348121f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 14 Jul 2026 18:16:58 -0700 Subject: [PATCH 24/27] ata: ahci_da850: use devm_platform_ioremap_resource() Replace the open-coded platform_get_resource(IORESOURCE_MEM, 1) plus devm_ioremap() sequence with devm_platform_ioremap_resource(pdev, 1), which looks up resource index 1 (the power-down control register), reserves it and maps it in one call, returning an ERR_PTR checked with IS_ERR()/PTR_ERR(). Resource index 0 (the AHCI register block) is still mapped separately as hpriv->mmio via ahci_platform_get_resources(). Built for ARM (davinci_all_defconfig + CONFIG_AHCI_DA850) with LLVM=1; drivers/ata/ahci_da850.o compiles cleanly. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/ahci_da850.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index f97566c420f8..4e53ff96d712 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -162,7 +162,6 @@ static int ahci_da850_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; void __iomem *pwrdn_reg; - struct resource *res; u32 mpy; int rc; @@ -198,22 +197,14 @@ static int ahci_da850_probe(struct platform_device *pdev) return -EINVAL; } + pwrdn_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(pwrdn_reg)) + return PTR_ERR(pwrdn_reg); + rc = ahci_platform_enable_resources(hpriv); if (rc) return rc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - rc = -ENODEV; - goto disable_resources; - } - - pwrdn_reg = devm_ioremap(dev, res->start, resource_size(res)); - if (!pwrdn_reg) { - rc = -ENOMEM; - goto disable_resources; - } - da850_sata_init(dev, pwrdn_reg, hpriv->mmio, mpy); rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, From 4a2e5409472e8a0e3e2817e149c55c1d5f803742 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 17 Jul 2026 11:04:04 +0200 Subject: [PATCH 25/27] ata: libata-sff: don't busy-wait for PIO data-in command completion Unlike PIO data-out, the PIO data-in protocol raises no completion interrupt, the last interrupt announces the final data block, and once the host has drained it from the data register the ending status must be obtained synchronously. ata_sff_hsm_move() does this by spinning in ata_wait_idle() for up to 10ms. Usually this is not a big deal unless the device is slow. In my case it's a CF card which keeps BSY asserted for multiple milliseconds(!) after the final data block. Since the waiting happens in the interrupt handler, under the port lock with interrupts disabled, the CPU is hogged for milliseconds on every read command. To improve the situation, bound the inline wait to ~100us. If the device is still busy after that, mark the command ATA_TFLAG_POLLING, so the interrupt handler won't race for it, and obtain the ending status via ata_sff_pio_task(), which sleeps between status checks instead of spinning with the lock held. Since ata_sff_pio_task() may now finish a data-in command, it must wait for both BSY and DRQ to clear at HSM_ST_LAST, matching what ata_wait_idle() enforced. Signed-off-by: Richard Weinberger Signed-off-by: Damien Le Moal --- drivers/ata/libata-sff.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 734e7c88439a..976e4e160494 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1114,8 +1114,14 @@ fsm_start: if (ap->hsm_task_state == HSM_ST_LAST && (!(qc->tf.flags & ATA_TFLAG_WRITE))) { - /* all data read */ - status = ata_wait_idle(ap); + status = ata_sff_busy_wait(ap, + ATA_BUSY | ATA_DRQ, 10); + if (status != 0xff && + (status & (ATA_BUSY | ATA_DRQ))) { + qc->tf.flags |= ATA_TFLAG_POLLING; + ata_sff_queue_pio_task(link, 0); + return 0; + } goto fsm_start; } } @@ -1213,7 +1219,7 @@ static void ata_sff_pio_task(struct work_struct *work) container_of(work, struct ata_port, sff_pio_task.work); struct ata_link *link = ap->sff_pio_task_link; struct ata_queued_cmd *qc; - u8 status; + u8 status, wait_mask; int poll_next; spin_lock_irq(ap->lock); @@ -1229,6 +1235,10 @@ static void ata_sff_pio_task(struct work_struct *work) fsm_start: WARN_ON_ONCE(ap->hsm_task_state == HSM_ST_IDLE); + wait_mask = ATA_BUSY; + if (ap->hsm_task_state == HSM_ST_LAST) + wait_mask |= ATA_DRQ; + /* * This is purely heuristic. This is a fast path. * Sometimes when we enter, BSY will be cleared in @@ -1236,14 +1246,14 @@ fsm_start: * or something. Snooze for a couple msecs, then * chk-status again. If still busy, queue delayed work. */ - status = ata_sff_busy_wait(ap, ATA_BUSY, 5); - if (status & ATA_BUSY) { + status = ata_sff_busy_wait(ap, wait_mask, 5); + if (status & wait_mask) { spin_unlock_irq(ap->lock); ata_msleep(ap, 2); spin_lock_irq(ap->lock); - status = ata_sff_busy_wait(ap, ATA_BUSY, 10); - if (status & ATA_BUSY) { + status = ata_sff_busy_wait(ap, wait_mask, 10); + if (status & wait_mask) { ata_sff_queue_pio_task(link, ATA_SHORT_PAUSE); goto out_unlock; } From 43ea91dadb35579169884f6707d43f7b2607153d Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 20 Jul 2026 18:58:56 -0700 Subject: [PATCH 26/27] ata: pata_pxa: use devres for DMA channel management Convert the DMA channel request to devm_dma_request_chan() so the channel is released automatically on device teardown. This removes the explicit dma_release_channel() calls in the probe error paths and in pxa_ata_remove(), simplifying the driver. Use ata_platform_remove_one() which is now equivalent to what remains of the remove function after dma_release_channel() removal. Built as a module for arm/pxa_defconfig (CONFIG_PATA_PXA=m) with LLVM=1 W=1; no new warnings. Assisted-by: opencode:hy3-free Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/pata_pxa.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index c816997dc0a9..a694f6a178e3 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -262,40 +262,26 @@ static int pxa_ata_probe(struct platform_device *pdev) /* * Request the DMA channel */ - data->dma_chan = dma_request_chan(&pdev->dev, "data"); + data->dma_chan = devm_dma_request_chan(&pdev->dev, "data"); if (IS_ERR(data->dma_chan)) return PTR_ERR(data->dma_chan); + ret = dmaengine_slave_config(data->dma_chan, &config); if (ret < 0) { dev_err(&pdev->dev, "dma configuration failed: %d\n", ret); - dma_release_channel(data->dma_chan); return ret; } /* * Activate the ATA host */ - ret = ata_host_activate(host, irq, ata_sff_interrupt, + return ata_host_activate(host, irq, ata_sff_interrupt, pdata->irq_flags, &pxa_ata_sht); - if (ret) - dma_release_channel(data->dma_chan); - - return ret; -} - -static void pxa_ata_remove(struct platform_device *pdev) -{ - struct ata_host *host = platform_get_drvdata(pdev); - struct pata_pxa_data *data = host->ports[0]->private_data; - - dma_release_channel(data->dma_chan); - - ata_host_detach(host); } static struct platform_driver pxa_ata_driver = { .probe = pxa_ata_probe, - .remove = pxa_ata_remove, + .remove = ata_platform_remove_one, .driver = { .name = DRV_NAME, }, From c303c3619a1d5cf7d4b457106062d16724724a80 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 19 Jul 2026 19:02:29 -0700 Subject: [PATCH 27/27] ata: pata_ep93xx: fix PIO fallback when DMA init fails ep93xx_pata_dma_init() returns an error when dma_request_chan() fails, which causes ep93xx_pata_probe() to abort entirely. The probe function already has a PIO fallback path (it checks both channel pointers before enabling UDMA), so the DMA init should not fail the probe on non-fatal errors. Propagate -EPROBE_DEFER, such that we allow the DMA controller driver to load, in case we got probed before the DMA controller driver. For all other failures (e.g. -ENODEV when the DMA controller is missing in the device tree), fall back to PIO. Assisted-by: Opencode:Big-Pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/pata_ep93xx.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c index 42a24dc51d26..339ee5e43e9f 100644 --- a/drivers/ata/pata_ep93xx.c +++ b/drivers/ata/pata_ep93xx.c @@ -656,14 +656,22 @@ static int ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data) * start of new transfer. */ drv_data->dma_rx_channel = dma_request_chan(dev, "rx"); - if (IS_ERR(drv_data->dma_rx_channel)) - return dev_err_probe(dev, PTR_ERR(drv_data->dma_rx_channel), - "rx DMA setup failed\n"); + if (IS_ERR(drv_data->dma_rx_channel)) { + ret = PTR_ERR(drv_data->dma_rx_channel); + drv_data->dma_rx_channel = NULL; + if (ret == -EPROBE_DEFER) + return ret; + dev_warn(dev, "rx DMA unavailable, using PIO\n"); + return 0; + } drv_data->dma_tx_channel = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR(drv_data->dma_tx_channel)) { - ret = dev_err_probe(dev, PTR_ERR(drv_data->dma_tx_channel), - "tx DMA setup failed\n"); + ret = PTR_ERR(drv_data->dma_tx_channel); + drv_data->dma_tx_channel = NULL; + if (ret == -EPROBE_DEFER) + goto fail_release_rx; + dev_warn(dev, "tx DMA unavailable, using PIO\n"); goto fail_release_rx; } @@ -674,7 +682,7 @@ static int ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data) conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ret = dmaengine_slave_config(drv_data->dma_rx_channel, &conf); if (ret) { - dev_err_probe(dev, ret, "failed to configure rx dma channel"); + dev_warn(dev, "failed to configure rx dma channel, using PIO\n"); goto fail_release_dma; } @@ -685,7 +693,7 @@ static int ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data) conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ret = dmaengine_slave_config(drv_data->dma_tx_channel, &conf); if (ret) { - dev_err_probe(dev, ret, "failed to configure tx dma channel"); + dev_warn(dev, "failed to configure tx dma channel, using PIO\n"); goto fail_release_dma; } @@ -693,10 +701,14 @@ static int ep93xx_pata_dma_init(struct ep93xx_pata_data *drv_data) fail_release_rx: dma_release_channel(drv_data->dma_rx_channel); + drv_data->dma_rx_channel = NULL; + if (ret == -EPROBE_DEFER) + return ret; + return 0; + fail_release_dma: ep93xx_pata_release_dma(drv_data); - - return ret; + return 0; } static void ep93xx_pata_dma_start(struct ata_queued_cmd *qc)