diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-06 14:53:28 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-06 14:53:28 +0100 |
| commit | 35e8e9e78770d77b462d3612bb3c9a6abf86dc90 (patch) | |
| tree | b6f34e400bf81f1ae55f05a133de02a74b433e28 | |
| parent | d997703d9c51b7e86a7cce99b3df2f1060a13d56 (diff) | |
| parent | e64e6b5dc86758c14ed28a6e85bf5d1b78146ed5 (diff) | |
| download | linux-next-35e8e9e78770d77b462d3612bb3c9a6abf86dc90.tar.gz linux-next-35e8e9e78770d77b462d3612bb3c9a6abf86dc90.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/libata/linux
| -rw-r--r-- | drivers/ata/libata-core.c | 6 | ||||
| -rw-r--r-- | drivers/ata/libata-scsi.c | 416 | ||||
| -rw-r--r-- | drivers/ata/libata.h | 2 | ||||
| -rw-r--r-- | include/linux/ata.h | 13 |
4 files changed, 334 insertions, 103 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..5cddb63a6bc6 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++; @@ -2202,6 +2202,39 @@ static unsigned int ata_scsiop_inq_89(struct ata_device *dev, } /** + * 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. * @cmd: SCSI command of interest. @@ -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); @@ -2320,7 +2354,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 +2885,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; @@ -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,39 +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) { - struct scsi_device *sdp = cmd->device; - size_t len = sdp->sector_size; - size_t r; - __le64 *buf; - u32 i = 0; - unsigned long flags; + struct sg_mapping_iter miter; + size_t offset = 0; - WARN_ON(len > 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; - if (len > ATA_SCSI_RBUF_SIZE) - len = ATA_SCSI_RBUF_SIZE; + for (i = 0; i < n; i++) { + u64 entry = 0; - 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; + 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; } /** @@ -3495,14 +3535,13 @@ 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; + unsigned int max_pages = ata_dsm_trim_pages(dev); + unsigned int n_pages; + size_t size; u64 block; u32 n_block; - const u32 trmax = len >> 3; - u32 size; u16 fp; u8 bp = 0xff; u8 unmap = cdb[1] & 0x8; @@ -3532,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; } @@ -3545,31 +3584,39 @@ 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 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. */ + 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; - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != len) + 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; } @@ -3577,6 +3624,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 (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; return 0; @@ -3593,88 +3646,253 @@ invalid_opcode: return 1; } -static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, - struct scsi_cmnd *cmd, - u8 *rbuf) +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, u16 sa) { - u8 *cdb = cmd->cmnd; - u8 supported = 0, cdlp = 0, rwcdlp = 0; + const struct ata_scsi_cmd *cmd; + int i; - if (cdb[2] != 1 && cdb[2] != 3) { - ata_dev_warn(dev, "invalid command format %d\n", cdb[2]); - ata_scsi_set_invalid_field(dev, cmd, 2, 0xff); - return 0; + for (i = 0; i < ARRAY_SIZE(ata_supported_cmds); i++) { + cmd = &ata_supported_cmds[i]; + if (cmd->op == op && cmd->sa == sa) + return cmd; } - 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; + return NULL; +} + +static bool ata_scsi_supported_cmd_use_sa(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->sa_valid; + } + + return false; +} + +struct ata_scsi_cmd_support { + u8 cdlp; + u8 rwcdlp; +}; + +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, sa); + if (!cmd) + return false; + + /* Now refine the support report depending on the device features. */ + memset(sup, 0, sizeof(*sup)); + switch (op) { 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; + sup->rwcdlp = 0x01; + sup->cdlp = 0x01; } 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; + sup->rwcdlp = 0x01; + sup->cdlp = 0x02; } break; case ZBC_IN: case ZBC_OUT: - if (ata_id_zoned_cap(dev->id) || - dev->class == ATA_DEV_ZAC) - supported = 3; - break; + return ata_dev_is_zoned(dev); case SECURITY_PROTOCOL_IN: case SECURITY_PROTOCOL_OUT: - if (dev->flags & ATA_DFLAG_TRUSTED) - supported = 3; - break; + return dev->flags & ATA_DFLAG_TRUSTED; default: break; } + 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) +{ + struct ata_scsi_cmd_support sup; + u8 *cdb = cmd->cmnd; + 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])) { + ata_scsi_set_invalid_field(dev, cmd, 3, 0xff); + 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]); + 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 */ - rbuf[0] = rwcdlp; - rbuf[1] = cdlp | supported; + if (ata_scsi_cmd_is_supported(dev, cdb[3], sa, &sup)) { + rbuf[0] = sup.rwcdlp; + rbuf[1] = (sup.cdlp << 3) | 0x03; + } else { + rbuf[1] = 0x01; + } return 4; } 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 || 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 */ |
