diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-23 17:53:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-23 17:53:39 -0700 |
commit | e4f7bdc2ec0d0dcc27f7d70db27a620dfdc1f697 (patch) | |
tree | b034d89f25c2403ca17eaa356cb37c2d1cb3b803 /drivers/ata | |
parent | 3159ee58d2133fbf9a710c21201ce717a8cf61a0 (diff) | |
parent | 6d1003ae8db228b74ef61536364cd2a1bd973dd8 (diff) | |
download | linux-e4f7bdc2ec0d0dcc27f7d70db27a620dfdc1f697.tar.gz linux-e4f7bdc2ec0d0dcc27f7d70db27a620dfdc1f697.tar.xz |
Merge branch 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
Pull libata ZAC support from Tejun Heo:
"This contains Zone ATA Command support for Shingled Magnetic Recording
devices.
In addition to sending the new commands down to the device, as ZAC
commands depend on getting a lot of responses from the device, piping
up responses is beefed up too. However, it doesn't involve changes to
libata core mechanism or its interaction with upper layers, so I'm not
expecting too many fallouts.
Kudos to Hannes for driving SMR support"
* 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: (28 commits)
libata: support host-aware and host-managed ZAC devices
libata: support device-managed ZAC devices
libata: NCQ encapsulation for ZAC MANAGEMENT OUT
libata: Implement ZBC OUT translation
libata: implement ZBC IN translation
libata: fixup ZAC device disabling
libata-scsi: Generate sense code for disabled devices
libata-trace: decode subcommands
libata: Check log page directory before accessing pages
libata: Add command definitions for NCQ Encapsulation for READ LOG DMA EXT
libata: Separate out ata_dev_config_ncq_send_recv()
libata/libsas: Define ATA_CMD_NCQ_NON_DATA
libsas: enable FPDMA SEND/RECEIVE
libata: do not attempt to retrieve sense code twice
libata-scsi: Set information sense field for invalid parameter
libata-scsi: set bit pointer for sense code information
libata-scsi: Set field pointer in sense code
scsi: add scsi_set_sense_field_pointer()
libata: Implement control mode page to select sense format
libata-scsi: generate correct ATA pass-through sense
...
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/libata-core.c | 215 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 113 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 752 | ||||
-rw-r--r-- | drivers/ata/libata-trace.c | 72 | ||||
-rw-r--r-- | drivers/ata/libata.h | 8 |
5 files changed, 989 insertions, 171 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 6f33ace33daf..6be7770f68e9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -66,6 +66,7 @@ #include <scsi/scsi_host.h> #include <linux/libata.h> #include <asm/byteorder.h> +#include <asm/unaligned.h> #include <linux/cdrom.h> #include <linux/ratelimit.h> #include <linux/pm_runtime.h> @@ -695,7 +696,7 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) * RETURNS: * Block address read from @tf. */ -u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) +u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) { u64 block = 0; @@ -720,7 +721,7 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) if (!sect) { ata_dev_warn(dev, "device reported invalid CHS sector 0\n"); - sect = 1; /* oh well */ + return U64_MAX; } block = (cyl * dev->heads + head) * dev->sectors + sect - 1; @@ -2079,6 +2080,81 @@ static inline u8 ata_dev_knobble(struct ata_device *dev) return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id))); } +static void ata_dev_config_ncq_send_recv(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + int log_index = ATA_LOG_NCQ_SEND_RECV * 2; + u16 log_pages; + + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (!log_pages) { + ata_dev_warn(dev, + "NCQ Send/Recv Log not supported\n"); + return; + } + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Send/Recv Log Emask 0x%x\n", + err_mask); + } else { + u8 *cmds = dev->ncq_send_recv_cmds; + + dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; + memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE); + + if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) { + ata_dev_dbg(dev, "disabling queued TRIM support\n"); + cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &= + ~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM; + } + } +} + +static void ata_dev_config_ncq_non_data(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + int log_index = ATA_LOG_NCQ_NON_DATA * 2; + u16 log_pages; + + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (!log_pages) { + ata_dev_warn(dev, + "NCQ Send/Recv Log not supported\n"); + return; + } + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_NON_DATA, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Non-Data Log Emask 0x%x\n", + err_mask); + } else { + u8 *cmds = dev->ncq_non_data_cmds; + + memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_NON_DATA_SIZE); + } +} + static int ata_dev_config_ncq(struct ata_device *dev, char *desc, size_t desc_sz) { @@ -2123,29 +2199,125 @@ static int ata_dev_config_ncq(struct ata_device *dev, snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth, ddepth, aa_desc); - if ((ap->flags & ATA_FLAG_FPDMA_AUX) && - ata_id_has_ncq_send_and_recv(dev->id)) { - err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, - 0, ap->sector_buf, 1); - if (err_mask) { - ata_dev_dbg(dev, - "failed to get NCQ Send/Recv Log Emask 0x%x\n", - err_mask); - } else { - u8 *cmds = dev->ncq_send_recv_cmds; + if ((ap->flags & ATA_FLAG_FPDMA_AUX)) { + if (ata_id_has_ncq_send_and_recv(dev->id)) + ata_dev_config_ncq_send_recv(dev); + if (ata_id_has_ncq_non_data(dev->id)) + ata_dev_config_ncq_non_data(dev); + } - dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; - memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE); + return 0; +} - if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) { - ata_dev_dbg(dev, "disabling queued TRIM support\n"); - cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &= - ~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM; - } +static void ata_dev_config_sense_reporting(struct ata_device *dev) +{ + unsigned int err_mask; + + if (!ata_id_has_sense_reporting(dev->id)) + return; + + if (ata_id_sense_reporting_enabled(dev->id)) + return; + + err_mask = ata_dev_set_feature(dev, SETFEATURE_SENSE_DATA, 0x1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to enable Sense Data Reporting, Emask 0x%x\n", + err_mask); + } +} + +static void ata_dev_config_zac(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + unsigned int err_mask; + u8 *identify_buf = ap->sector_buf; + int log_index = ATA_LOG_SATA_ID_DEV_DATA * 2, i, found = 0; + u16 log_pages; + + dev->zac_zones_optimal_open = U32_MAX; + dev->zac_zones_optimal_nonseq = U32_MAX; + dev->zac_zones_max_open = U32_MAX; + + /* + * Always set the 'ZAC' flag for Host-managed devices. + */ + if (dev->class == ATA_DEV_ZAC) + dev->flags |= ATA_DFLAG_ZAC; + else if (ata_id_zoned_cap(dev->id) == 0x01) + /* + * Check for host-aware devices. + */ + dev->flags |= ATA_DFLAG_ZAC; + + if (!(dev->flags & ATA_DFLAG_ZAC)) + return; + + /* + * Read Log Directory to figure out if IDENTIFY DEVICE log + * is supported. + */ + err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_info(dev, + "failed to get Log Directory Emask 0x%x\n", + err_mask); + return; + } + log_pages = get_unaligned_le16(&ap->sector_buf[log_index]); + if (log_pages == 0) { + ata_dev_warn(dev, + "ATA Identify Device Log not supported\n"); + return; + } + /* + * Read IDENTIFY DEVICE data log, page 0, to figure out + * if page 9 is supported. + */ + err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, 0, + identify_buf, 1); + if (err_mask) { + ata_dev_info(dev, + "failed to get Device Identify Log Emask 0x%x\n", + err_mask); + return; + } + log_pages = identify_buf[8]; + for (i = 0; i < log_pages; i++) { + if (identify_buf[9 + i] == ATA_LOG_ZONED_INFORMATION) { + found++; + break; } } + if (!found) { + ata_dev_warn(dev, + "ATA Zoned Information Log not supported\n"); + return; + } - return 0; + /* + * Read IDENTIFY DEVICE data log, page 9 (Zoned-device information) + */ + err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, + ATA_LOG_ZONED_INFORMATION, + identify_buf, 1); + if (!err_mask) { + u64 zoned_cap, opt_open, opt_nonseq, max_open; + + zoned_cap = get_unaligned_le64(&identify_buf[8]); + if ((zoned_cap >> 63)) + dev->zac_zoned_cap = (zoned_cap & 1); + opt_open = get_unaligned_le64(&identify_buf[24]); + if ((opt_open >> 63)) + dev->zac_zones_optimal_open = (u32)opt_open; + opt_nonseq = get_unaligned_le64(&identify_buf[32]); + if ((opt_nonseq >> 63)) + dev->zac_zones_optimal_nonseq = (u32)opt_nonseq; + max_open = get_unaligned_le64(&identify_buf[40]); + if ((max_open >> 63)) + dev->zac_zones_max_open = (u32)max_open; + } } /** @@ -2370,7 +2542,8 @@ int ata_dev_configure(struct ata_device *dev) dev->devslp_timing[i] = sata_setting[j]; } } - + ata_dev_config_sense_reporting(dev); + ata_dev_config_zac(dev); dev->cdb_len = 16; } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 961acc788f44..61dc7a99e89a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1600,6 +1600,8 @@ static int ata_eh_read_log_10h(struct ata_device *dev, tf->hob_lbah = buf[10]; tf->nsect = buf[12]; tf->hob_nsect = buf[13]; + if (ata_id_has_ncq_autosense(dev->id)) + tf->auxiliary = buf[14] << 16 | buf[15] << 8 | buf[16]; return 0; } @@ -1636,6 +1638,56 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) } /** + * ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT + * @dev: device to perform REQUEST_SENSE_SENSE_DATA_EXT to + * @cmd: scsi command for which the sense code should be set + * + * Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK + * SENSE. This function is an EH helper. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +static void ata_eh_request_sense(struct ata_queued_cmd *qc, + struct scsi_cmnd *cmd) +{ + struct ata_device *dev = qc->dev; + struct ata_taskfile tf; + unsigned int err_mask; + + if (qc->ap->pflags & ATA_PFLAG_FROZEN) { + ata_dev_warn(dev, "sense data available but port frozen\n"); + return; + } + + if (!cmd || qc->flags & ATA_QCFLAG_SENSE_VALID) + return; + + if (!ata_id_sense_reporting_enabled(dev->id)) { + ata_dev_warn(qc->dev, "sense data reporting disabled\n"); + return; + } + + DPRINTK("ATA request sense\n"); + + ata_tf_init(dev, &tf); + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; + tf.command = ATA_CMD_REQ_SENSE_DATA; + tf.protocol = ATA_PROT_NODATA; + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + /* Ignore err_mask; ATA_ERR might be set */ + if (tf.command & ATA_SENSE) { + ata_scsi_set_sense(dev, cmd, tf.lbah, tf.lbam, tf.lbal); + qc->flags |= ATA_QCFLAG_SENSE_VALID; + } else { + ata_dev_warn(dev, "request sense failed stat %02x emask %x\n", + tf.command, err_mask); + } +} + +/** * atapi_eh_request_sense - perform ATAPI REQUEST_SENSE * @dev: device to perform REQUEST_SENSE to * @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long) @@ -1797,6 +1849,18 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) memcpy(&qc->result_tf, &tf, sizeof(tf)); qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48; qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ; + if ((qc->result_tf.command & ATA_SENSE) || qc->result_tf.auxiliary) { + char sense_key, asc, ascq; + + sense_key = (qc->result_tf.auxiliary >> 16) & 0xff; + asc = (qc->result_tf.auxiliary >> 8) & 0xff; + ascq = qc->result_tf.auxiliary & 0xff; + ata_scsi_set_sense(dev, qc->scsicmd, sense_key, asc, ascq); + ata_scsi_set_sense_information(dev, qc->scsicmd, + &qc->result_tf); + qc->flags |= ATA_QCFLAG_SENSE_VALID; + } + ehc->i.err_mask &= ~AC_ERR_DEV; } @@ -1826,14 +1890,23 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, return ATA_EH_RESET; } - if (stat & (ATA_ERR | ATA_DF)) + if (stat & (ATA_ERR | ATA_DF)) { qc->err_mask |= AC_ERR_DEV; - else + /* + * Sense data reporting does not work if the + * device fault bit is set. + */ + if (stat & ATA_DF) + stat &= ~ATA_SENSE; + } else { return 0; + } switch (qc->dev->class) { case ATA_DEV_ATA: case ATA_DEV_ZAC: + if (stat & ATA_SENSE) + ata_eh_request_sense(qc, qc->scsicmd); if (err & ATA_ICRC) qc->err_mask |= AC_ERR_ATA_BUS; if (err & (ATA_UNC | ATA_AMNF)) @@ -1847,20 +1920,31 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, tmp = atapi_eh_request_sense(qc->dev, qc->scsicmd->sense_buffer, qc->result_tf.feature >> 4); - if (!tmp) { - /* ATA_QCFLAG_SENSE_VALID is used to - * tell atapi_qc_complete() that sense - * data is already valid. - * - * TODO: interpret sense data and set - * appropriate err_mask. - */ + if (!tmp) qc->flags |= ATA_QCFLAG_SENSE_VALID; - } else + else qc->err_mask |= tmp; } } + if (qc->flags & ATA_QCFLAG_SENSE_VALID) { + int ret = scsi_check_sense(qc->scsicmd); + /* + * SUCCESS here means that the sense code could + * evaluated and should be passed to the upper layers + * for correct evaluation. + * FAILED means the sense code could not interpreted + * and the device would need to be reset. + * NEEDS_RETRY and ADD_TO_MLQUEUE means that the + * command would need to be retried. + */ + if (ret == NEEDS_RETRY || ret == ADD_TO_MLQUEUE) { + qc->flags |= ATA_QCFLAG_RETRY; + qc->err_mask |= AC_ERR_OTHER; + } else if (ret != SUCCESS) { + qc->err_mask |= AC_ERR_HSM; + } + } if (qc->err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT | AC_ERR_ATA_BUS)) action |= ATA_EH_RESET; @@ -2398,6 +2482,8 @@ const char *ata_get_cmd_descript(u8 command) { ATA_CMD_CFA_WRITE_MULT_NE, "CFA WRITE MULTIPLE WITHOUT ERASE" }, { ATA_CMD_REQ_SENSE_DATA, "REQUEST SENSE DATA EXT" }, { ATA_CMD_SANITIZE_DEVICE, "SANITIZE DEVICE" }, + { ATA_CMD_ZAC_MGMT_IN, "ZAC MANAGEMENT IN" }, + { ATA_CMD_ZAC_MGMT_OUT, "ZAC MANAGEMENT OUT" }, { ATA_CMD_READ_LONG, "READ LONG (with retries)" }, { ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" }, { ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" }, @@ -2569,14 +2655,15 @@ static void ata_eh_link_report(struct ata_link *link) #ifdef CONFIG_ATA_VERBOSE_ERROR if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | - ATA_ERR)) { + ATA_SENSE | ATA_ERR)) { if (res->command & ATA_BUSY) ata_dev_err(qc->dev, "status: { Busy }\n"); else - ata_dev_err(qc->dev, "status: { %s%s%s%s}\n", + ata_dev_err(qc->dev, "status: { %s%s%s%s%s}\n", res->command & ATA_DRDY ? "DRDY " : "", res->command & ATA_DF ? "DF " : "", res->command & ATA_DRQ ? "DRQ " : "", + res->command & ATA_SENSE ? "SENSE " : "", res->command & ATA_ERR ? "ERR " : ""); } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7bcc870afeaa..bfec66fb26e2 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,11 +270,52 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); -static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +void ata_scsi_set_sense(struct ata_device *dev, struct scsi_cmnd *cmd, + u8 sk, u8 asc, u8 ascq) { + bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE); + + if (!cmd) + return; + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); + scsi_build_sense_buffer(d_sense, cmd->sense_buffer, sk, asc, ascq); +} + +void ata_scsi_set_sense_information(struct ata_device *dev, + struct scsi_cmnd *cmd, + const struct ata_taskfile *tf) +{ + u64 information; + + if (!cmd) + return; + + information = ata_tf_read_block(tf, dev); + if (information == U64_MAX) + return; + + scsi_set_sense_information(cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, information); +} + +static void ata_scsi_set_invalid_field(struct ata_device *dev, + struct scsi_cmnd *cmd, u16 field, u8 bit) +{ + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + scsi_set_sense_field_pointer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE, + field, bit, 1); +} + +static void ata_scsi_set_invalid_parameter(struct ata_device *dev, + struct scsi_cmnd *cmd, u16 field) +{ + /* "Invalid field in parameter list" */ + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x26, 0x0); + scsi_set_sense_field_pointer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE, + field, 0xff, 0); } static ssize_t @@ -364,10 +405,10 @@ struct device_attribute *ata_common_sdev_attrs[] = { }; EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); -static void ata_scsi_invalid_field(struct scsi_cmnd *cmd) +static void ata_scsi_invalid_field(struct ata_device *dev, + struct scsi_cmnd *cmd, u16 field) { - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(dev, cmd, field, 0xff); cmd->scsi_done(cmd); } @@ -980,6 +1021,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) unsigned char *sb = cmd->sense_buffer; unsigned char *desc = sb + 8; int verbose = qc->ap->ops->error_handler == NULL; + u8 sense_key, asc, ascq; memset(sb, 0, SCSI_SENSE_BUFFERSIZE); @@ -992,47 +1034,71 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) if (qc->err_mask || tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, - &sb[1], &sb[2], &sb[3], verbose); - sb[1] &= 0x0f; + &sense_key, &asc, &ascq, verbose); + ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq); } else { - sb[1] = RECOVERED_ERROR; - sb[2] = 0; - sb[3] = 0x1D; + /* + * ATA PASS-THROUGH INFORMATION AVAILABLE + * Always in descriptor format sense. + */ + scsi_build_sense_buffer(1, cmd->sense_buffer, + RECOVERED_ERROR, 0, 0x1D); } - /* - * Sense data is current and format is descriptor. - */ - sb[0] = 0x72; - - desc[0] = 0x09; - - /* set length of additional sense data */ - sb[7] = 14; - desc[1] = 12; - - /* - * Copy registers into sense buffer. - */ - desc[2] = 0x00; - desc[3] = tf->feature; /* == error reg */ - desc[5] = tf->nsect; - desc[7] = tf->lbal; - desc[9] = tf->lbam; - desc[11] = tf->lbah; - desc[12] = tf->device; - desc[13] = tf->command; /* == status reg */ + if ((cmd->sense_buffer[0] & 0x7f) >= 0x72) { + u8 len; + + /* descriptor format */ + len = sb[7]; + desc = (char *)scsi_sense_desc_find(sb, len + 8, 9); + if (!desc) { + if (SCSI_SENSE_BUFFERSIZE < len + 14) + return; + sb[7] = len + 14; + desc = sb + 8 + len; + } + desc[0] = 9; + desc[1] = 12; + /* + * Copy registers into sense buffer. + */ + desc[2] = 0x00; + desc[3] = tf->feature; /* == error reg */ + desc[5] = tf->nsect; + desc[7] = tf->lbal; + desc[9] = tf->lbam; + desc[11] = tf->lbah; + desc[12] = tf->device; + desc[13] = tf->command; /* == status reg */ - /* - * Fill in Extend bit, and the high order bytes - * if applicable. - */ - if (tf->flags & ATA_TFLAG_LBA48) { - desc[2] |= 0x01; - desc[4] = tf->hob_nsect; - desc[6] = tf->hob_lbal; - desc[8] = tf->hob_lbam; - desc[10] = tf->hob_lbah; + /* + * Fill in Extend bit, and the high order bytes + * if applicable. + */ + if (tf->flags & ATA_TFLAG_LBA48) { + desc[2] |= 0x01; + desc[4] = tf->hob_nsect; + desc[6] = tf->hob_lbal; + desc[8] = tf->hob_lbam; + desc[10] = tf->hob_lbah; + } + } else { + /* Fixed sense format */ + desc[0] = tf->feature; + desc[1] = tf->command; /* status */ + desc[2] = tf->device; + desc[3] = tf->nsect; + desc[0] = 0; + if (tf->flags & ATA_TFLAG_LBA48) { + desc[8] |= 0x80; + if (tf->hob_nsect) + desc[8] |= 0x40; + if (tf->hob_lbal || tf->hob_lbam || tf->hob_lbah) + desc[8] |= 0x20; + } + desc[9] = tf->lbal; + desc[10] = tf->lbam; + desc[11] = tf->lbah; } } @@ -1052,41 +1118,41 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) struct scsi_cmnd *cmd = qc->scsicmd; struct ata_taskfile *tf = &qc->result_tf; unsigned char *sb = cmd->sense_buffer; - unsigned char *desc = sb + 8; int verbose = qc->ap->ops->error_handler == NULL; u64 block; + u8 sense_key, asc, ascq; memset(sb, 0, SCSI_SENSE_BUFFERSIZE); cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - /* sense data is current and format is descriptor */ - sb[0] = 0x72; - + if (ata_dev_disabled(dev)) { + /* Device disabled after error recovery */ + /* LOGICAL UNIT NOT READY, HARD RESET REQUIRED */ + ata_scsi_set_sense(dev, cmd, NOT_READY, 0x04, 0x21); + return; + } /* Use ata_to_sense_error() to map status register bits * onto sense key, asc & ascq. */ if (qc->err_mask || tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, - &sb[1], &sb[2], &sb[3], verbose); - sb[1] &= 0x0f; + &sense_key, &asc, &ascq, verbose); + ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq); + } else { + /* Could not decode error */ + ata_dev_warn(dev, "could not decode error status 0x%x err_mask 0x%x\n", + tf->command, qc->err_mask); + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); + return; } block = ata_tf_read_block(&qc->result_tf, dev); + if (block == U64_MAX) + return; - /* information sense data descriptor */ - sb[7] = 12; - desc[0] = 0x00; - desc[1] = 10; - - desc[2] |= 0x80; /* valid */ - desc[6] = block >> 40; - desc[7] = block >> 32; - desc[8] = block >> 24; - desc[9] = block >> 16; - desc[10] = block >> 8; - desc[11] = block; + scsi_set_sense_information(sb, SCSI_SENSE_BUFFERSIZE, block); } static void ata_scsi_sdev_config(struct scsi_device *sdev) @@ -1343,19 +1409,29 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) struct scsi_cmnd *scmd = qc->scsicmd; struct ata_taskfile *tf = &qc->tf; const u8 *cdb = scmd->cmnd; + u16 fp; + u8 bp = 0xff; - if (scmd->cmd_len < 5) + if (scmd->cmd_len < 5) { + fp = 4; goto invalid_fld; + } tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; tf->protocol = ATA_PROT_NODATA; if (cdb[1] & 0x1) { ; /* ignore IMMED bit, violates sat-r05 */ } - if (cdb[4] & 0x2) + if (cdb[4] & 0x2) { + fp = 4; + bp = 1; goto invalid_fld; /* LOEJ bit set not supported */ - if (((cdb[4] >> 4) & 0xf) != 0) + } + if (((cdb[4] >> 4) & 0xf) != 0) { + fp = 4; + bp = 3; goto invalid_fld; /* power conditions not supported */ + } if (cdb[4] & 0x1) { tf->nsect = 1; /* 1 sector, lba=0 */ @@ -1401,8 +1477,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); return 1; skip: scmd->result = SAM_STAT_GOOD; @@ -1553,20 +1628,27 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc) const u8 *cdb = scmd->cmnd; u64 block; u32 n_block; + u16 fp; tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->protocol = ATA_PROT_NODATA; if (cdb[0] == VERIFY) { - if (scmd->cmd_len < 10) + if (scmd->cmd_len < 10) { + fp = 9; goto invalid_fld; + } scsi_10_lba_len(cdb, &block, &n_block); } else if (cdb[0] == VERIFY_16) { - if (scmd->cmd_len < 16) + if (scmd->cmd_len < 16) { + fp = 15; goto invalid_fld; + } scsi_16_lba_len(cdb, &block, &n_block); - } else + } else { + fp = 0; goto invalid_fld; + } if (!n_block) goto nothing_to_do; @@ -1640,12 +1722,11 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); return 1; out_of_range: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0); /* "Logical Block Address out of range" */ return 1; @@ -1680,6 +1761,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) u64 block; u32 n_block; int rc; + u16 fp = 0; if (cdb[0] == WRITE_10 || cdb[0] == WRITE_6 || cdb[0] == WRITE_16) tf_flags |= ATA_TFLAG_WRITE; @@ -1688,16 +1770,20 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) switch (cdb[0]) { case READ_10: case WRITE_10: - if (unlikely(scmd->cmd_len < 10)) + if (unlikely(scmd->cmd_len < 10)) { + fp = 9; goto invalid_fld; + } scsi_10_lba_len(cdb, &block, &n_block); if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; break; case READ_6: case WRITE_6: - if (unlikely(scmd->cmd_len < 6)) + if (unlikely(scmd->cmd_len < 6)) { + fp = 5; goto invalid_fld; + } scsi_6_lba_len(cdb, &block, &n_block); /* for 6-byte r/w commands, transfer length 0 @@ -1708,14 +1794,17 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) break; case READ_16: case WRITE_16: - if (unlikely(scmd->cmd_len < 16)) + if (unlikely(scmd->cmd_len < 16)) { + fp = 15; goto invalid_fld; + } scsi_16_lba_len(cdb, &block, &n_block); if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; break; default: DPRINTK("no-byte command\n"); + fp = 0; goto invalid_fld; } @@ -1742,12 +1831,11 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) goto out_of_range; /* treat all other errors as -EINVAL, fall through */ invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); return 1; out_of_range: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x21, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x0); /* "Logical Block Address out of range" */ return 1; @@ -1784,6 +1872,8 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) ata_gen_passthru_sense(qc); + else if (qc->flags & ATA_QCFLAG_SENSE_VALID) + cmd->result = SAM_STAT_CHECK_CONDITION; else if (need_sense) ata_gen_ata_sense(qc); else @@ -1992,14 +2082,14 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) 0x00, 0xA0, /* SAM-5 (no version claimed) */ - 0x04, - 0xC0, /* SBC-3 (no version claimed) */ + 0x06, + 0x00, /* SBC-4 (no version claimed) */ - 0x04, - 0x60, /* SPC-4 (no version claimed) */ + 0x05, + 0xC0, /* SPC-5 (no version claimed) */ 0x60, - 0x20, /* ZBC (no version claimed) */ + 0x24, /* ZBC r05 */ }; u8 hdr[] = { @@ -2019,10 +2109,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) (args->dev->link->ap->pflags & ATA_PFLAG_EXTERNAL)) hdr[1] |= (1 << 7); - if (args->dev->class == ATA_DEV_ZAC) { + if (args->dev->class == ATA_DEV_ZAC) hdr[0] = TYPE_ZBC; - hdr[2] = 0x6; /* ZBC is defined in SPC-4 */ - } memcpy(rbuf, hdr, sizeof(hdr)); memcpy(&rbuf[8], "ATA ", 8); @@ -2036,7 +2124,7 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) if (rbuf[32] == 0 || rbuf[32] == ' ') memcpy(&rbuf[32], "n/a ", 4); - if (args->dev->class == ATA_DEV_ZAC) + if (ata_id_zoned_cap(args->id) || args->dev->class == ATA_DEV_ZAC) memcpy(rbuf + 58, versions_zbc, sizeof(versions_zbc)); else memcpy(rbuf + 58, versions, sizeof(versions)); @@ -2056,6 +2144,7 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) */ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf) { + int num_pages; const u8 pages[] = { 0x00, /* page 0x00, this page */ 0x80, /* page 0x80, unit serial no page */ @@ -2064,10 +2153,14 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf) 0xb0, /* page 0xb0, block limits page */ 0xb1, /* page 0xb1, block device characteristics page */ 0xb2, /* page 0xb2, thin provisioning page */ + 0xb6, /* page 0xb6, zoned block device characteristics */ }; - rbuf[3] = sizeof(pages); /* number of supported VPD pages */ - memcpy(rbuf + 4, pages, sizeof(pages)); + num_pages = sizeof(pages); + if (!(args->dev->flags & ATA_DFLAG_ZAC)) + num_pages--; + rbuf[3] = num_pages; /* number of supported VPD pages */ + memcpy(rbuf + 4, pages, num_pages); return 0; } @@ -2232,12 +2325,15 @@ static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf) { int form_factor = ata_id_form_factor(args->id); int media_rotation_rate = ata_id_rotation_rate(args->id); + u8 zoned = ata_id_zoned_cap(args->id); rbuf[1] = 0xb1; rbuf[3] = 0x3c; rbuf[4] = media_rotation_rate >> 8; rbuf[5] = media_rotation_rate; rbuf[7] = form_factor; + if (zoned) + rbuf[8] = (zoned << 4); return 0; } @@ -2252,6 +2348,26 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf) return 0; } +static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf) +{ + /* + * zbc-r05 SCSI Zoned Block device characteristics VPD page + */ + rbuf[1] = 0xb6; + rbuf[3] = 0x3C; + + /* + * URSWRZ bit is only meaningful for host-managed ZAC drives + */ + if (args->dev->zac_zoned_cap & 1) + rbuf[4] |= 1; + put_unaligned_be32(args->dev->zac_zones_optimal_open, &rbuf[8]); + put_unaligned_be32(args->dev->zac_zones_optimal_nonseq, &rbuf[12]); + put_unaligned_be32(args->dev->zac_zones_max_open, &rbuf[16]); + + return 0; +} + /** * ata_scsiop_noop - Command handler that simply returns success. * @args: device IDENTIFY data / SCSI command of interest. @@ -2317,6 +2433,7 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) /** * ata_msense_ctl_mode - Simulate MODE SENSE control mode page + * @dev: ATA device of interest * @buf: output buffer * @changeable: whether changeable parameters are requested * @@ -2325,9 +2442,12 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable) * LOCKING: * None. */ -static unsigned int ata_msense_ctl_mode(u8 *buf, bool changeable) +static unsigned int ata_msense_ctl_mode(struct ata_device *dev, u8 *buf, + bool changeable) { modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable); + if (changeable && (dev->flags & ATA_DFLAG_D_SENSE)) + buf[2] |= (1 << 2); /* Descriptor sense requested */ return sizeof(def_control_mpage); } @@ -2395,7 +2515,8 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) }; u8 pg, spg; unsigned int ebd, page_control, six_byte; - u8 dpofua; + u8 dpofua, bp = 0xff; + u16 fp; VPRINTK("ENTER\n"); @@ -2414,6 +2535,8 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) case 3: /* saved */ goto saving_not_supp; default: + fp = 2; + bp = 6; goto invalid_fld; } @@ -2428,8 +2551,10 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) * No mode subpages supported (yet) but asking for _all_ * subpages may be valid */ - if (spg && (spg != ALL_SUB_MPAGES)) + if (spg && (spg != ALL_SUB_MPAGES)) { + fp = 3; goto invalid_fld; + } switch(pg) { case RW_RECOVERY_MPAGE: @@ -2441,16 +2566,17 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) break; case CONTROL_MPAGE: - p += ata_msense_ctl_mode(p, page_control == 1); + p += ata_msense_ctl_mode(args->dev, p, page_control == 1); break; case ALL_MPAGES: p += ata_msense_rw_recovery(p, page_control == 1); p += ata_msense_caching(args->id, p, page_control == 1); - p += ata_msense_ctl_mode(p, page_control == 1); + p += ata_msense_ctl_mode(args->dev, p, page_control == 1); break; default: /* invalid page code */ + fp = 2; goto invalid_fld; } @@ -2480,12 +2606,11 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) return 0; invalid_fld: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(dev, args->cmd, fp, bp); return 1; saving_not_supp: - ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); + ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); /* "Saving parameters not supported" */ return 1; } @@ -2561,6 +2686,9 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf) rbuf[14] |= 0x40; /* LBPRZ */ } } + if (ata_id_zoned_cap(args->id) || + args->dev->class == ATA_DEV_ZAC) + rbuf[12] = (1 << 4); /* RC_BASIS */ } return 0; } @@ -2942,9 +3070,12 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) struct scsi_cmnd *scmd = qc->scsicmd; struct ata_device *dev = qc->dev; const u8 *cdb = scmd->cmnd; + u16 fp; - if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN) + if ((tf->protocol = ata_scsi_map_proto(cdb[1])) == ATA_PROT_UNKNOWN) { + fp = 1; goto invalid_fld; + } /* enable LBA */ tf->flags |= ATA_TFLAG_LBA; @@ -3008,8 +3139,10 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) case ATA_CMD_READ_LONG_ONCE: case ATA_CMD_WRITE_LONG: case ATA_CMD_WRITE_LONG_ONCE: - if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1) + if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1) { + fp = 1; goto invalid_fld; + } qc->sect_size = scsi_bufflen(scmd); break; @@ -3072,12 +3205,16 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) ata_qc_set_pc_nbytes(qc); /* We may not issue DMA commands if no DMA mode is set */ - if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0) + if (tf->protocol == ATA_PROT_DMA && dev->dma_mode == 0) { + fp = 1; goto invalid_fld; + } /* sanity check for pio multi commands */ - if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf)) + if ((cdb[1] & 0xe0) && !is_multi_taskfile(tf)) { + fp = 1; goto invalid_fld; + } if (is_multi_taskfile(tf)) { unsigned int multi_count = 1 << (cdb[1] >> 5); @@ -3098,8 +3235,10 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) * ->set_dmamode(), and ->post_set_mode() hooks). */ if (tf->command == ATA_CMD_SET_FEATURES && - tf->feature == SETFEATURES_XFER) + tf->feature == SETFEATURES_XFER) { + fp = (cdb[0] == ATA_16) ? 4 : 3; goto invalid_fld; + } /* * Filter TPM commands by default. These provide an @@ -3116,14 +3255,15 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) * so that we comply with the TC consortium stated goal that the user * can turn off TC features of their system. */ - if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm) + if (tf->command >= 0x5C && tf->command <= 0x5F && !libata_allow_tpm) { + fp = (cdb[0] == ATA_16) ? 14 : 9; goto invalid_fld; + } return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); - /* "Invalid field in cdb" */ + ata_scsi_set_invalid_field(dev, scmd, fp, 0xff); return 1; } @@ -3137,25 +3277,32 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) u32 n_block; u32 size; void *buf; + u16 fp; + u8 bp = 0xff; /* we may not issue DMA commands if no DMA mode is set */ if (unlikely(!dev->dma_mode)) - goto invalid_fld; + goto invalid_opcode; - if (unlikely(scmd->cmd_len < 16)) + if (unlikely(scmd->cmd_len < 16)) { + fp = 15; goto invalid_fld; + } scsi_16_lba_len(cdb, &block, &n_block); /* for now we only support WRITE SAME with the unmap bit set */ - if (unlikely(!(cdb[1] & 0x8))) + if (unlikely(!(cdb[1] & 0x8))) { + fp = 1; + bp = 3; goto invalid_fld; + } /* * WRITE SAME always has a sector sized buffer as payload, this * should never be a multiple entry S/G list. */ if (!scsi_sg_count(scmd)) - goto invalid_fld; + goto invalid_param_len; buf = page_address(sg_page(scsi_sglist(scmd))); size = ata_set_lba_range_entries(buf, 512, block, n_block); @@ -3186,9 +3333,242 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) return 0; +invalid_fld: + ata_scsi_set_invalid_field(dev, scmd, fp, bp); + return 1; +invalid_param_len: + /* "Parameter list length error" */ + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + return 1; +invalid_opcode: + /* "Invalid command operation code" */ + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x20, 0x0); + return 1; +} + +/** + * ata_scsi_report_zones_complete - convert ATA output + * @qc: command structure returning the data + * + * Convert T-13 little-endian field representation into + * T-10 big-endian field representation. + * What a mess. + */ +static void ata_scsi_report_zones_complete(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + struct sg_mapping_iter miter; + unsigned long flags; + unsigned int bytes = 0; + + sg_miter_start(&miter, scsi_sglist(scmd), scsi_sg_count(scmd), + SG_MITER_TO_SG | SG_MITER_ATOMIC); + + local_irq_save(flags); + while (sg_miter_next(&miter)) { + unsigned int offset = 0; + + if (bytes == 0) { + char *hdr; + u32 list_length; + u64 max_lba, opt_lba; + u16 same; + + /* Swizzle header */ + hdr = miter.addr; + list_length = get_unaligned_le32(&hdr[0]); + same = get_unaligned_le16(&hdr[4]); + max_lba = get_unaligned_le64(&hdr[8]); + opt_lba = get_unaligned_le64(&hdr[16]); + put_unaligned_be32(list_length, &hdr[0]); + hdr[4] = same & 0xf; + put_unaligned_be64(max_lba, &hdr[8]); + put_unaligned_be64(opt_lba, &hdr[16]); + offset += 64; + bytes += 64; + } + while (offset < miter.length) { + char *rec; + u8 cond, type, non_seq, reset; + u64 size, start, wp; + + /* Swizzle zone descriptor */ + rec = miter.addr + offset; + type = rec[0] & 0xf; + cond = (rec[1] >> 4) & 0xf; + non_seq = (rec[1] & 2); + reset = (rec[1] & 1); + size = get_unaligned_le64(&rec[8]); + start = get_unaligned_le64(&rec[16]); + wp = get_unaligned_le64(&rec[24]); + rec[0] = type; + rec[1] = (cond << 4) | non_seq | reset; + put_unaligned_be64(size, &rec[8]); + put_unaligned_be64(start, &rec[16]); + put_unaligned_be64(wp, &rec[24]); + WARN_ON(offset + 64 > miter.length); + offset += 64; + bytes += 64; + } + } + sg_miter_stop(&miter); + local_irq_restore(flags); + + ata_scsi_qc_complete(qc); +} + +static unsigned int ata_scsi_zbc_in_xlat(struct ata_queued_cmd *qc) +{ + struct ata_taskfile *tf = &qc->tf; + struct scsi_cmnd *scmd = qc->scsicmd; + const u8 *cdb = scmd->cmnd; + u16 sect, fp = (u16)-1; + u8 sa, options, bp = 0xff; + u64 block; + u32 n_block; + + if (unlikely(scmd->cmd_len < 16)) { + ata_dev_warn(qc->dev, "invalid cdb length %d\n", + scmd->cmd_len); + fp = 15; + goto invalid_fld; + } + scsi_16_lba_len(cdb, &block, &n_block); + if (n_block != scsi_bufflen(scmd)) { + ata_dev_warn(qc->dev, "non-matching transfer count (%d/%d)\n", + n_block, scsi_bufflen(scmd)); + goto invalid_param_len; + } + sa = cdb[1] & 0x1f; + if (sa != ZI_REPORT_ZONES) { + ata_dev_warn(qc->dev, "invalid service action %d\n", sa); + fp = 1; + goto invalid_fld; + } + /* + * ZAC allows only for transfers in 512 byte blocks, + * and uses a 16 bit value for the transfer count. + */ + if ((n_block / 512) > 0xffff || n_block < 512 || (n_block % 512)) { + ata_dev_warn(qc->dev, "invalid transfer count %d\n", n_block); + goto invalid_param_len; + } + sect = n_block / 512; + options = cdb[14]; + + if (ata_ncq_enabled(qc->dev) && + ata_fpdma_zac_mgmt_in_supported(qc->dev)) { + tf->protocol = ATA_PROT_NCQ; + tf->command = ATA_CMD_FPDMA_RECV; + tf->hob_nsect = ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN & 0x1f; + tf->nsect = qc->tag << 3; + tf->feature = sect & 0xff; + tf->hob_feature = (sect >> 8) & 0xff; + tf->auxiliary = ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES; + } else { + tf->command = ATA_CMD_ZAC_MGMT_IN; + tf->feature = ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES; + tf->protocol = ATA_PROT_DMA; + tf->hob_feature = options; + tf->hob_nsect = (sect >> 8) & 0xff; + tf->nsect = sect & 0xff; + } + tf->device = ATA_LBA; + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + qc->flags |= ATA_QCFLAG_RESULT_TF; + + ata_qc_set_pc_nbytes(qc); + + qc->complete_fn = ata_scsi_report_zones_complete; + + return 0; + +invalid_fld: + ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); + return 1; + +invalid_param_len: + /* "Parameter list length error" */ + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + return 1; +} + +static unsigned int ata_scsi_zbc_out_xlat(struct ata_queued_cmd *qc) +{ + struct ata_taskfile *tf = &qc->tf; + struct scsi_cmnd *scmd = qc->scsicmd; + struct ata_device *dev = qc->dev; + const u8 *cdb = scmd->cmnd; + u8 reset_all, sa; + u64 block; + u32 n_block; + u16 fp = (u16)-1; + + if (unlikely(scmd->cmd_len < 16)) { + fp = 15; + goto invalid_fld; + } + + sa = cdb[1] & 0x1f; + if ((sa != ZO_CLOSE_ZONE) && (sa != ZO_FINISH_ZONE) && + (sa != ZO_OPEN_ZONE) && (sa != ZO_RESET_WRITE_POINTER)) { + fp = 1; + goto invalid_fld; + } + + scsi_16_lba_len(cdb, &block, &n_block); + if (n_block) { + /* + * ZAC MANAGEMENT OUT doesn't define any length + */ + goto invalid_param_len; + } + if (block > dev->n_sectors) + goto out_of_range; + + reset_all = cdb[14] & 0x1; + + if (ata_ncq_enabled(qc->dev) && + ata_fpdma_zac_mgmt_out_supported(qc->dev)) { + tf->protocol = ATA_PROT_NCQ; + tf->command = ATA_CMD_NCQ_NON_DATA; + tf->hob_nsect = ATA_SUBCMD_NCQ_NON_DATA_ZAC_MGMT_OUT; + tf->nsect = qc->tag << 3; + tf->auxiliary = sa | (reset_all & 0x1) << 8; + } else { + tf->protocol = ATA_PROT_NODATA; + tf->command = ATA_CMD_ZAC_MGMT_OUT; + tf->feature = sa; + tf->hob_feature = reset_all & 0x1; + } + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + tf->device = ATA_LBA; + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; + + return 0; + invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); - /* "Invalid field in cdb" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); + return 1; + out_of_range: + /* "Logical Block Address out of range" */ + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x21, 0x00); + return 1; +invalid_param_len: + /* "Parameter list length error" */ + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); return 1; } @@ -3197,6 +3577,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) * @qc: Storage for translated ATA taskfile * @buf: input buffer * @len: number of valid bytes in the input buffer + * @fp: out parameter for the failed field on error * * Prepare a taskfile to modify caching information for the device. * @@ -3204,20 +3585,26 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) * None. */ static int ata_mselect_caching(struct ata_queued_cmd *qc, - const u8 *buf, int len) + const u8 *buf, int len, u16 *fp) { struct ata_taskfile *tf = &qc->tf; struct ata_device *dev = qc->dev; char mpage[CACHE_MPAGE_LEN]; u8 wce; + int i; /* * The first two bytes of def_cache_mpage are a header, so offsets * in mpage are off by 2 compared to buf. Same for len. */ - if (len != CACHE_MPAGE_LEN - 2) + if (len != CACHE_MPAGE_LEN - 2) { + if (len < CACHE_MPAGE_LEN - 2) + *fp = len; + else + *fp = CACHE_MPAGE_LEN - 2; return -EINVAL; + } wce = buf[0] & (1 << 2); @@ -3225,10 +3612,14 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc, * Check that read-only bits are not modified. */ ata_msense_caching(dev->id, mpage, false); - mpage[2] &= ~(1 << 2); - mpage[2] |= wce; - if (memcmp(mpage + 2, buf, CACHE_MPAGE_LEN - 2) != 0) - return -EINVAL; + for (i = 0; i < CACHE_MPAGE_LEN - 2; i++) { + if (i == 0) + continue; + if (mpage[i + 2] != buf[i]) { + *fp = i; + return -EINVAL; + } + } tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; tf->protocol = ATA_PROT_NODATA; @@ -3239,6 +3630,62 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc, } /** + * ata_mselect_control - Simulate MODE SELECT for control page + * @qc: Storage for translated ATA taskfile + * @buf: input buffer + * @len: number of valid bytes in the input buffer + * @fp: out parameter for the failed field on error + * + * Prepare a taskfile to modify caching information for the device. + * + * LOCKING: + * None. + */ +static int ata_mselect_control(struct ata_queued_cmd *qc, + const u8 *buf, int len, u16 *fp) +{ + struct ata_device *dev = qc->dev; + char mpage[CONTROL_MPAGE_LEN]; + u8 d_sense; + int i; + + /* + * The first two bytes of def_control_mpage are a header, so offsets + * in mpage are off by 2 compared to buf. Same for len. + */ + + if (len != CONTROL_MPAGE_LEN - 2) { + if (len < CONTROL_MPAGE_LEN - 2) + *fp = len; + else + *fp = CONTROL_MPAGE_LEN - 2; + return -EINVAL; + } + + d_sense = buf[0] & (1 << 2); + + /* + * Check that read-only bits are not modified. + */ + ata_msense_ctl_mode(dev, mpage, false); + for (i = 0; i < CONTROL_MPAGE_LEN - 2; i++) { + if (i == 0) + continue; + if (mpage[2 + i] != buf[i]) { + *fp = i; + return -EINVAL; + } + } + if (d_sense & (1 << 2)) + dev->flags |= ATA_DFLAG_D_SENSE; + else + dev->flags &= ~ATA_DFLAG_D_SENSE; + qc->scsicmd->result = SAM_STAT_GOOD; + qc->scsicmd->scsi_done(qc->scsicmd); + return 0; +} + +/** * ata_scsiop_mode_select - Simulate MODE SELECT 6, 10 commands * @qc: Storage for translated ATA taskfile * @@ -3257,27 +3704,36 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) u8 pg, spg; unsigned six_byte, pg_len, hdr_len, bd_len; int len; + u16 fp = (u16)-1; + u8 bp = 0xff; VPRINTK("ENTER\n"); six_byte = (cdb[0] == MODE_SELECT); if (six_byte) { - if (scmd->cmd_len < 5) + if (scmd->cmd_len < 5) { + fp = 4; goto invalid_fld; + } len = cdb[4]; hdr_len = 4; } else { - if (scmd->cmd_len < 9) + if (scmd->cmd_len < 9) { + fp = 8; goto invalid_fld; + } len = (cdb[7] << 8) + cdb[8]; hdr_len = 8; } /* We only support PF=1, SP=0. */ - if ((cdb[1] & 0x11) != 0x10) + if ((cdb[1] & 0x11) != 0x10) { + fp = 1; + bp = (cdb[1] & 0x01) ? 1 : 5; goto invalid_fld; + } /* Test early for possible overrun. */ if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len) @@ -3298,8 +3754,11 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) p += hdr_len; if (len < bd_len) goto invalid_param_len; - if (bd_len != 0 && bd_len != 8) + if (bd_len != 0 && bd_len != 8) { + fp = (six_byte) ? 3 : 6; + fp += bd_len + hdr_len; goto invalid_param; + } len -= bd_len; p += bd_len; @@ -3330,18 +3789,29 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) * No mode subpages supported (yet) but asking for _all_ * subpages may be valid */ - if (spg && (spg != ALL_SUB_MPAGES)) + if (spg && (spg != ALL_SUB_MPAGES)) { + fp = (p[0] & 0x40) ? 1 : 0; + fp += hdr_len + bd_len; goto invalid_param; + } if (pg_len > len) goto invalid_param_len; switch (pg) { case CACHE_MPAGE: - if (ata_mselect_caching(qc, p, pg_len) < 0) + if (ata_mselect_caching(qc, p, pg_len, &fp) < 0) { + fp += hdr_len + bd_len; goto invalid_param; + } + break; + case CONTROL_MPAGE: + if (ata_mselect_control(qc, p, pg_len, &fp) < 0) { + fp += hdr_len + bd_len; + goto invalid_param; + } break; - default: /* invalid page code */ + fp = bd_len + hdr_len; goto invalid_param; } @@ -3355,18 +3825,16 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - /* "Invalid field in CDB" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); return 1; invalid_param: - /* "Invalid field in parameter list" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0); + ata_scsi_set_invalid_parameter(qc->dev, scmd, fp); return 1; invalid_param_len: /* "Parameter list length error" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x1a, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x1a, 0x0); return 1; skip: @@ -3419,6 +3887,12 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) return ata_scsi_mode_select_xlat; break; + case ZBC_IN: + return ata_scsi_zbc_in_xlat; + + case ZBC_OUT: + return ata_scsi_zbc_out_xlat; + case START_STOP: return ata_scsi_start_stop_xlat; } @@ -3567,12 +4041,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) switch(scsicmd[0]) { /* TODO: worth improving? */ case FORMAT_UNIT: - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd, 0); break; case INQUIRY: - if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_scsi_invalid_field(cmd); + if (scsicmd[1] & 2) /* is CmdDt set? */ + ata_scsi_invalid_field(dev, cmd, 1); else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); else switch (scsicmd[2]) { @@ -3597,8 +4071,14 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) case 0xb2: ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2); break; + case 0xb6: + if (dev->flags & ATA_DFLAG_ZAC) { + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b6); + break; + } + /* Fallthrough */ default: - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd, 2); break; } break; @@ -3616,7 +4096,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); else - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd, 1); break; case REPORT_LUNS: @@ -3624,7 +4104,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) break; case REQUEST_SENSE: - ata_scsi_set_sense(cmd, 0, 0, 0); + ata_scsi_set_sense(dev, cmd, 0, 0, 0); cmd->result = (DRIVER_SENSE << 24); cmd->scsi_done(cmd); break; @@ -3648,12 +4128,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) if ((tmp8 == 0x4) && (!scsicmd[3]) && (!scsicmd[4])) ata_scsi_rbuf_fill(&args, ata_scsiop_noop); else - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd, 1); break; /* all other commands */ default: - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0); + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0); /* "Invalid command operation code" */ cmd->scsi_done(cmd); break; diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index fd30b8c10cf5..f8c550df0615 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -149,3 +149,75 @@ libata_trace_parse_qc_flags(struct trace_seq *p, unsigned int qc_flags) return ret; } + +const char * +libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, + unsigned char feature, unsigned char hob_nsect) +{ + const char *ret = trace_seq_buffer_ptr(p); + + switch (cmd) { + case ATA_CMD_FPDMA_RECV: + switch (hob_nsect & 0x5f) { + case ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT: + trace_seq_printf(p, " READ_LOG_DMA_EXT"); + break; + case ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN: + trace_seq_printf(p, " ZAC_MGMT_IN"); + break; + } + break; + case ATA_CMD_FPDMA_SEND: + switch (hob_nsect & 0x5f) { + case ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT: + trace_seq_printf(p, " WRITE_LOG_DMA_EXT"); + break; + case ATA_SUBCMD_FPDMA_SEND_DSM: + trace_seq_printf(p, " DATASET_MANAGEMENT"); + break; + } + break; + case ATA_CMD_NCQ_NON_DATA: + switch (feature) { + case ATA_SUBCMD_NCQ_NON_DATA_ABORT_QUEUE: + trace_seq_printf(p, " ABORT_QUEUE"); + break; + case ATA_SUBCMD_NCQ_NON_DATA_SET_FEATURES: + trace_seq_printf(p, " SET_FEATURES"); + break; + case ATA_SUBCMD_NCQ_NON_DATA_ZERO_EXT: + trace_seq_printf(p, " ZERO_EXT"); + break; + case ATA_SUBCMD_NCQ_NON_DATA_ZAC_MGMT_OUT: + trace_seq_printf(p, " ZAC_MGMT_OUT"); + break; + } + break; + case ATA_CMD_ZAC_MGMT_IN: + switch (feature) { + case ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES: + trace_seq_printf(p, " REPORT_ZONES"); + break; + } + break; + case ATA_CMD_ZAC_MGMT_OUT: + switch (feature) { + case ATA_SUBCMD_ZAC_MGMT_OUT_CLOSE_ZONE: + trace_seq_printf(p, " CLOSE_ZONE"); + break; + case ATA_SUBCMD_ZAC_MGMT_OUT_FINISH_ZONE: + trace_seq_printf(p, " FINISH_ZONE"); + break; + case ATA_SUBCMD_ZAC_MGMT_OUT_OPEN_ZONE: + trace_seq_printf(p, " OPEN_ZONE"); + break; + case ATA_SUBCMD_ZAC_MGMT_OUT_RESET_WRITE_POINTER: + trace_seq_printf(p, " RESET_WRITE_POINTER"); + break; + } + break; + } + trace_seq_putc(p, 0); + + return ret; +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index f840ca18a7c0..3b301a48007c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -67,7 +67,8 @@ extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, unsigned int tag); -extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev); +extern u64 ata_tf_read_block(const struct ata_taskfile *tf, + struct ata_device *dev); extern unsigned ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, int dma_dir, void *buf, unsigned int buflen, @@ -137,6 +138,11 @@ extern int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap, int sync); extern int ata_scsi_offline_dev(struct ata_device *dev); +extern void ata_scsi_set_sense(struct ata_device *dev, + struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); +extern void ata_scsi_set_sense_information(struct ata_device *dev, + struct scsi_cmnd *cmd, + const struct ata_taskfile *tf); extern void ata_scsi_media_change_notify(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); |