From e64e6b5dc86758c14ed28a6e85bf5d1b78146ed5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 2 Jul 2026 12:59:59 +0200 Subject: 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 --- include/linux/ata.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') 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 */ -- cgit v1.2.3 From 7c8b3f6578bb874ba9d334aef0e51fe931ce6c04 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sun, 14 Jun 2026 14:39:39 +0900 Subject: 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(-) (limited to 'include') 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 -- cgit v1.2.3 From 2a0adbb80a2f7c5d33baea981dacb8ee956b8677 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 22 Jun 2026 16:40:07 +0900 Subject: 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(+) (limited to 'include') 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, -- cgit v1.2.3 From ee1badfde360e3b95289ed359b4416f0fbb66eb1 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 6 Jul 2026 12:27:39 +0900 Subject: 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(-) (limited to 'include') 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) */ -- cgit v1.2.3 From 4ba5eb54c2c8671f741a161b4f0d5162a4cb9b1b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:04:41 +0900 Subject: 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 | 45 ++++++++++++++++------------- 2 files changed, 96 insertions(+), 22 deletions(-) (limited to 'include') 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_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_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 << 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 << 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 { -- cgit v1.2.3 From 0f0bfa1bcaa5dd6a19c3614b16dc94d625030313 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 19 Jun 2026 17:25:37 +0900 Subject: 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(+) (limited to 'include') 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, -- cgit v1.2.3 From db496721cb0d369f34d7e0bf66692b050e2b6ff8 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:35:38 +0900 Subject: 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(+) (limited to 'include') 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, -- cgit v1.2.3 From 1e307ca61a9cdd8c3b721577e4889c47816654d8 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:44:43 +0900 Subject: 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(-) (limited to 'include') 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, -- cgit v1.2.3 From b448bb42bd1024014125c822211731fc2ae9a2ee Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 20 Jun 2026 20:59:54 +0900 Subject: 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(+) (limited to 'include') 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, -- cgit v1.2.3 From 9bf9aefdc0f5ef24674e7787e169b7d4d389009c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 13 Jul 2026 13:16:53 +0900 Subject: 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(-) (limited to 'include') 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); -- cgit v1.2.3