From 5b01e4b9efa0b78672cbbea830c9fbcc7f239e29 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:54 +0200 Subject: [PATCH 01/28] libata: Implement NCQ autosense Some newer devices support NCQ autosense (cf ACS-4), so we should be using it to retrieve the sense code and speed up recovery. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 12 ++++++++++++ drivers/ata/libata-scsi.c | 7 ++++++- drivers/ata/libata.h | 1 + include/linux/ata.h | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 961acc788f44..8c8355f0792e 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; } @@ -1797,6 +1799,16 @@ 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.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(qc->scsicmd, sense_key, asc, ascq); + qc->flags |= ATA_QCFLAG_SENSE_VALID; + } + ehc->i.err_mask &= ~AC_ERR_DEV; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 567859ce0512..6dc2fadfd7c5 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,8 +270,11 @@ 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 scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) { + if (!cmd) + return; + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); @@ -1784,6 +1787,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 diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index f840ca18a7c0..8cfdd9616d16 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -137,6 +137,7 @@ 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 scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); 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); diff --git a/include/linux/ata.h b/include/linux/ata.h index c1a2f345cbe6..e797e1b53006 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -528,6 +528,8 @@ struct ata_bmdma_prd { #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) #define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4)) #define ata_id_has_devslp(id) ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8)) +#define ata_id_has_ncq_autosense(id) \ + ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7)) static inline bool ata_id_has_hipm(const u16 *id) { From e87fd28cf9a2d9018ac4b6dd92f0b417714bc18d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:55 +0200 Subject: [PATCH 02/28] libata: Implement support for sense data reporting ACS-4 defines a sense data reporting feature set. This patch implements support for it. tj: Cosmetic formatting updates. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 20 +++++++++++- drivers/ata/libata-eh.c | 68 ++++++++++++++++++++++++++++++++++++--- include/linux/ata.h | 16 +++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 55e257c268dd..f991f786227e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2148,6 +2148,24 @@ static int ata_dev_config_ncq(struct ata_device *dev, return 0; } +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); + } +} + /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -2370,7 +2388,7 @@ int ata_dev_configure(struct ata_device *dev) dev->devslp_timing[i] = sata_setting[j]; } } - + ata_dev_config_sense_reporting(dev); dev->cdb_len = 16; } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 8c8355f0792e..170e891e79af 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1637,6 +1637,56 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) return err_mask; } +/** + * 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) + 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(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 @@ -1838,14 +1888,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)) @@ -2581,14 +2640,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/include/linux/ata.h b/include/linux/ata.h index e797e1b53006..00aebc4c83ad 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -385,6 +385,8 @@ enum { SATA_SSP = 0x06, /* Software Settings Preservation */ SATA_DEVSLP = 0x09, /* Device Sleep */ + SETFEATURE_SENSE_DATA = 0xC3, /* Sense Data Reporting feature */ + /* feature values for SET_MAX */ ATA_SET_MAX_ADDR = 0x00, ATA_SET_MAX_PASSWD = 0x01, @@ -718,6 +720,20 @@ static inline bool ata_id_has_read_log_dma_ext(const u16 *id) return false; } +static inline bool ata_id_has_sense_reporting(const u16 *id) +{ + if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + return false; + return id[ATA_ID_COMMAND_SET_3] & (1 << 6); +} + +static inline bool ata_id_sense_reporting_enabled(const u16 *id) +{ + if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) + return false; + return id[ATA_ID_COMMAND_SET_4] & (1 << 6); +} + /** * ata_id_major_version - get ATA level of drive * @id: Identify data From 5e6acd1c8c4dc9a7c16208aeac3de09151ee6233 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:56 +0200 Subject: [PATCH 03/28] libata-scsi: sanitize ata_gen_ata_sense() ata_to_sense_error() is called conditionally, so we should be generating a default sense if the condition is not met. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 6dc2fadfd7c5..e331077ee446 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1074,6 +1074,12 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sb[1], &sb[2], &sb[3], verbose); sb[1] &= 0x0f; + } 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(cmd, ABORTED_COMMAND, 0, 0); + return; } block = ata_tf_read_block(&qc->result_tf, dev); From cffd1ee991c566bca937392cfacdafbe3b7b58c6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:57 +0200 Subject: [PATCH 04/28] libata: sanitize ata_tf_read_block() Return U64_MAX if ata_tf_read_block() could not decode the LBA address, and do not set the information sense descriptor in ata_gen_ata_sense() in these cases. tj: s/(u64)-1/U64_MAX/ Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 4 ++-- drivers/ata/libata-scsi.c | 2 ++ drivers/ata/libata.h | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index f991f786227e..7bdb2c4e357f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -695,7 +695,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 +720,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; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e331077ee446..fc23d3f4282d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1083,6 +1083,8 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) } block = ata_tf_read_block(&qc->result_tf, dev); + if (block == U64_MAX) + return; /* information sense data descriptor */ sb[7] = 12; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 8cfdd9616d16..507c22f7a63b 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, From cf8b49b0af39b8e8fa358623acda57f01251b6d4 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:58 +0200 Subject: [PATCH 05/28] libata-scsi: use scsi_set_sense_information() Use scsi_set_sense_information() instead of hand-crafted function. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index fc23d3f4282d..47b103d9baac 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1055,7 +1055,6 @@ 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; @@ -1086,18 +1085,7 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) 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) From 492bf62107347aca764070dbc9d412da6bda73d1 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:43:59 +0200 Subject: [PATCH 06/28] libata-eh: Set 'information' field for autosense If NCQ autosense or the sense data reporting feature is enabled the LBA of the offending command should be stored in the sense data 'information' field. tj: s/(u64)-1/U64_MAX/ Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 2 ++ drivers/ata/libata-scsi.c | 17 +++++++++++++++++ drivers/ata/libata.h | 3 +++ 3 files changed, 22 insertions(+) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 170e891e79af..e4e0e2e80c20 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1856,6 +1856,8 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) asc = (qc->result_tf.auxiliary >> 8) & 0xff; ascq = qc->result_tf.auxiliary & 0xff; ata_scsi_set_sense(qc->scsicmd, sense_key, asc, ascq); + ata_scsi_set_sense_information(dev, qc->scsicmd, + &qc->result_tf); qc->flags |= ATA_QCFLAG_SENSE_VALID; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 47b103d9baac..97e8f6b0494c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -280,6 +280,23 @@ void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) scsi_build_sense_buffer(0, 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 ssize_t ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 507c22f7a63b..dbc67604b3c5 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -139,6 +139,9 @@ extern int ata_scsi_add_hosts(struct ata_host *host, 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 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); From b525e7731b90ebc7a70a095fc5d5363408b94274 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:00 +0200 Subject: [PATCH 07/28] libata-scsi: use ata_scsi_set_sense() Use ata_scsi_set_sense() throughout to ensure the sense code format is consistent. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 97e8f6b0494c..3c33f32c04c4 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1000,6 +1000,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); @@ -1012,12 +1013,11 @@ 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(cmd, sense_key, asc, ascq); } else { - sb[1] = RECOVERED_ERROR; - sb[2] = 0; - sb[3] = 0x1D; + /* ATA PASS-THROUGH INFORMATION AVAILABLE */ + ata_scsi_set_sense(cmd, RECOVERED_ERROR, 0, 0x1D); } /* @@ -1074,22 +1074,20 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) unsigned char *sb = cmd->sense_buffer; 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; - /* 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(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", From 3852e37382664a06cd006bb389a8223e32cedf45 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:01 +0200 Subject: [PATCH 08/28] libata: evaluate SCSI sense code Whenever a sense code is set it would need to be evaluated to update the error mask. tj: Cosmetic formatting updates. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 29 ++++++++++++++++++++--------- drivers/scsi/scsi_error.c | 3 ++- include/scsi/scsi_eh.h | 1 + 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e4e0e2e80c20..e37258b78e01 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1920,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; diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 984ddcb4786d..a8b610eaa0ca 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -452,7 +452,7 @@ static void scsi_report_sense(struct scsi_device *sdev, * When a deferred error is detected the current command has * not been executed and needs retrying. */ -static int scsi_check_sense(struct scsi_cmnd *scmd) +int scsi_check_sense(struct scsi_cmnd *scmd) { struct scsi_device *sdev = scmd->device; struct scsi_sense_hdr sshdr; @@ -602,6 +602,7 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return SUCCESS; } } +EXPORT_SYMBOL_GPL(scsi_check_sense); static void scsi_handle_queue_ramp_up(struct scsi_device *sdev) { diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h index dbb8c640e26f..98d366b55770 100644 --- a/include/scsi/scsi_eh.h +++ b/include/scsi/scsi_eh.h @@ -16,6 +16,7 @@ extern void scsi_report_device_reset(struct Scsi_Host *, int, int); extern int scsi_block_when_processing_errors(struct scsi_device *); extern bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd, struct scsi_sense_hdr *sshdr); +extern int scsi_check_sense(struct scsi_cmnd *); static inline bool scsi_sense_is_deferred(const struct scsi_sense_hdr *sshdr) { From 11093cb1ef56147fe33f5750b1eab347bdef30db Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:02 +0200 Subject: [PATCH 09/28] libata-scsi: generate correct ATA pass-through sense Generate ATA pass-through sense for both fixed and descriptor format sense. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 91 +++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3c33f32c04c4..0da03c019f27 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1016,43 +1016,68 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) &sense_key, &asc, &ascq, verbose); ata_scsi_set_sense(cmd, sense_key, asc, ascq); } else { - /* ATA PASS-THROUGH INFORMATION AVAILABLE */ - ata_scsi_set_sense(cmd, RECOVERED_ERROR, 0, 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; + if ((cmd->sense_buffer[0] & 0x7f) >= 0x72) { + u8 len; - desc[0] = 0x09; + /* 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 */ - /* 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 */ - - /* - * 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; } } From 06dbde5f3a44248fc02e24d662ac4849202abb48 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:03 +0200 Subject: [PATCH 10/28] libata: Implement control mode page to select sense format Implement MODE SELECT for the control mode page to allow the OS to switch to descriptor sense. tj: Dropped s/sb/cmd->sense_buffer/ in ata_gen_ata_sense(). Added @dev description to ata_msense_ctl_mode(). Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 4 +- drivers/ata/libata-scsi.c | 116 ++++++++++++++++++++++++++++---------- drivers/ata/libata.h | 3 +- include/linux/libata.h | 1 + 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e37258b78e01..5b340ce4eeac 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1679,7 +1679,7 @@ static void ata_eh_request_sense(struct ata_queued_cmd *qc, 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(cmd, tf.lbah, tf.lbam, tf.lbal); + 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", @@ -1855,7 +1855,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) 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(qc->scsicmd, sense_key, asc, ascq); + 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; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0da03c019f27..2389247bdf6f 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,14 +270,17 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); -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, @@ -384,9 +387,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) { - ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ cmd->scsi_done(cmd); } @@ -1014,7 +1018,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sense_key, &asc, &ascq, verbose); - ata_scsi_set_sense(cmd, sense_key, asc, ascq); + ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq); } else { /* * ATA PASS-THROUGH INFORMATION AVAILABLE @@ -1112,12 +1116,12 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sense_key, &asc, &ascq, verbose); - ata_scsi_set_sense(cmd, sense_key, asc, ascq); + 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(cmd, ABORTED_COMMAND, 0, 0); + ata_scsi_set_sense(dev, cmd, ABORTED_COMMAND, 0, 0); return; } @@ -1440,7 +1444,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); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; skip: @@ -1679,12 +1683,12 @@ 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); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ 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; @@ -1781,12 +1785,12 @@ 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); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ 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; @@ -2358,6 +2362,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 * @@ -2366,9 +2371,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); } @@ -2482,13 +2490,13 @@ 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 */ @@ -2521,12 +2529,12 @@ 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); + ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ 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; } @@ -3163,7 +3171,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); /* "Invalid field in cdb" */ return 1; } @@ -3228,7 +3236,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00); + ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); /* "Invalid field in cdb" */ return 1; } @@ -3279,6 +3287,51 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc, return 0; } +/** + * 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 + * + * 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) +{ + struct ata_device *dev = qc->dev; + char mpage[CONTROL_MPAGE_LEN]; + u8 d_sense; + + /* + * 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) + return -EINVAL; + + d_sense = buf[0] & (1 << 2); + + /* + * Check that read-only bits are not modified. + */ + ata_msense_ctl_mode(dev, mpage, false); + mpage[2] &= ~(1 << 2); + mpage[2] |= d_sense; + if (memcmp(mpage + 2, buf, CONTROL_MPAGE_LEN - 2) != 0) + 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 @@ -3381,7 +3434,10 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) if (ata_mselect_caching(qc, p, pg_len) < 0) goto invalid_param; break; - + case CONTROL_MPAGE: + if (ata_mselect_control(qc, p, pg_len) < 0) + goto invalid_param; + break; default: /* invalid page code */ goto invalid_param; } @@ -3397,17 +3453,17 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) invalid_fld: /* "Invalid field in CDB" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); return 1; invalid_param: /* "Invalid field in parameter list" */ - ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x26, 0x0); + ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x26, 0x0); 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: @@ -3611,12 +3667,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); break; case INQUIRY: if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); else switch (scsicmd[2]) { @@ -3642,7 +3698,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2); break; default: - ata_scsi_invalid_field(cmd); + ata_scsi_invalid_field(dev, cmd); break; } break; @@ -3660,7 +3716,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); break; case REPORT_LUNS: @@ -3668,7 +3724,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; @@ -3692,12 +3748,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); 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.h b/drivers/ata/libata.h index dbc67604b3c5..3b301a48007c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -138,7 +138,8 @@ 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 scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); +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); diff --git a/include/linux/libata.h b/include/linux/libata.h index 2c4ebef79d0c..a418bca0df0d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -180,6 +180,7 @@ enum { 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_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ From 78db6e30281aa056a6773cb4def00afc158c097f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:04 +0200 Subject: [PATCH 11/28] scsi: add scsi_set_sense_field_pointer() Add a function to set the field pointer for SCSI sense codes. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/scsi/scsi_common.c | 53 ++++++++++++++++++++++++++++++++++++++ include/scsi/scsi_common.h | 1 + 2 files changed, 54 insertions(+) diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c index ce79de822e46..b1383a71400e 100644 --- a/drivers/scsi/scsi_common.c +++ b/drivers/scsi/scsi_common.c @@ -293,3 +293,56 @@ int scsi_set_sense_information(u8 *buf, int buf_len, u64 info) return 0; } EXPORT_SYMBOL(scsi_set_sense_information); + +/** + * scsi_set_sense_field_pointer - set the field pointer sense key + * specific information in a formatted sense data buffer + * @buf: Where to build sense data + * @buf_len: buffer length + * @fp: field pointer to be set + * @bp: bit pointer to be set + * @cd: command/data bit + * + * Return value: + * 0 on success or EINVAL for invalid sense buffer length + */ +int scsi_set_sense_field_pointer(u8 *buf, int buf_len, u16 fp, u8 bp, bool cd) +{ + u8 *ucp, len; + + if ((buf[0] & 0x7f) == 0x72) { + len = buf[7]; + ucp = (char *)scsi_sense_desc_find(buf, len + 8, 2); + if (!ucp) { + buf[7] = len + 8; + ucp = buf + 8 + len; + } + + if (buf_len < len + 8) + /* Not enough room for info */ + return -EINVAL; + + ucp[0] = 2; + ucp[1] = 6; + ucp[4] = 0x80; /* Valid bit */ + if (cd) + ucp[4] |= 0x40; + if (bp < 0x8) + ucp[4] |= 0x8 | bp; + put_unaligned_be16(fp, &ucp[5]); + } else if ((buf[0] & 0x7f) == 0x70) { + len = buf[7]; + if (len < 18) + buf[7] = 18; + + buf[15] = 0x80; + if (cd) + buf[15] |= 0x40; + if (bp < 0x8) + buf[15] |= 0x8 | bp; + put_unaligned_be16(fp, &buf[16]); + } + + return 0; +} +EXPORT_SYMBOL(scsi_set_sense_field_pointer); diff --git a/include/scsi/scsi_common.h b/include/scsi/scsi_common.h index 11571b2a831e..20bf7eaef05a 100644 --- a/include/scsi/scsi_common.h +++ b/include/scsi/scsi_common.h @@ -63,6 +63,7 @@ extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); int scsi_set_sense_information(u8 *buf, int buf_len, u64 info); +int scsi_set_sense_field_pointer(u8 *buf, int buf_len, u16 fp, u8 bp, bool cd); extern const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, int desc_type); From bcfc867d467c98aba23ce0331455282936c04b73 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:05 +0200 Subject: [PATCH 12/28] libata-scsi: Set field pointer in sense code If the sense code is 'Invalid field in CDB' we should be setting the field pointer to the offending byte. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 155 ++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 47 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 2389247bdf6f..062cb2eee8de 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -300,6 +300,15 @@ void ata_scsi_set_sense_information(struct ata_device *dev, SCSI_SENSE_BUFFERSIZE, information); } +static void ata_scsi_set_invalid_field(struct ata_device *dev, + struct scsi_cmnd *cmd, u16 field) +{ + 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, 0xff, 1); +} + static ssize_t ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -388,10 +397,9 @@ struct device_attribute *ata_common_sdev_attrs[] = { EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); static void ata_scsi_invalid_field(struct ata_device *dev, - struct scsi_cmnd *cmd) + struct scsi_cmnd *cmd, u16 field) { - ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(dev, cmd, field); cmd->scsi_done(cmd); } @@ -1386,19 +1394,26 @@ 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; - 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; goto invalid_fld; /* LOEJ bit set not supported */ - if (((cdb[4] >> 4) & 0xf) != 0) + } + if (((cdb[4] >> 4) & 0xf) != 0) { + fp = 4; goto invalid_fld; /* power conditions not supported */ + } if (cdb[4] & 0x1) { tf->nsect = 1; /* 1 sector, lba=0 */ @@ -1444,8 +1459,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp); return 1; skip: scmd->result = SAM_STAT_GOOD; @@ -1596,20 +1610,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; @@ -1683,8 +1704,7 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp); return 1; out_of_range: @@ -1723,6 +1743,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; @@ -1731,16 +1752,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 @@ -1751,14 +1776,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; } @@ -1785,8 +1813,7 @@ 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(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(qc->dev, scmd, fp); return 1; out_of_range: @@ -2445,6 +2472,7 @@ 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; + u16 fp; VPRINTK("ENTER\n"); @@ -2463,6 +2491,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) case 3: /* saved */ goto saving_not_supp; default: + fp = 2; goto invalid_fld; } @@ -2477,8 +2506,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: @@ -2500,6 +2531,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) break; default: /* invalid page code */ + fp = 2; goto invalid_fld; } @@ -2529,8 +2561,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) return 0; invalid_fld: - ata_scsi_set_sense(dev, args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); - /* "Invalid field in cbd" */ + ata_scsi_set_invalid_field(dev, args->cmd, fp); return 1; saving_not_supp: @@ -2991,9 +3022,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; @@ -3057,8 +3091,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; @@ -3121,12 +3157,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); @@ -3147,8 +3187,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 @@ -3165,14 +3207,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(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); - /* "Invalid field in cdb" */ + ata_scsi_set_invalid_field(dev, scmd, fp); return 1; } @@ -3186,25 +3229,30 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) u32 n_block; u32 size; void *buf; + u16 fp; /* 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; 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); @@ -3235,9 +3283,16 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) return 0; - invalid_fld: - ata_scsi_set_sense(dev, scmd, ILLEGAL_REQUEST, 0x24, 0x00); - /* "Invalid field in cdb" */ +invalid_fld: + ata_scsi_set_invalid_field(dev, scmd, fp); + 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; } @@ -3351,27 +3406,34 @@ 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; 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; goto invalid_fld; + } /* Test early for possible overrun. */ if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len) @@ -3452,8 +3514,7 @@ 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(qc->dev, scmd, ILLEGAL_REQUEST, 0x24, 0x0); + ata_scsi_set_invalid_field(qc->dev, scmd, fp); return 1; invalid_param: @@ -3667,12 +3728,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(dev, cmd); + ata_scsi_invalid_field(dev, cmd, 0); break; case INQUIRY: - if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_scsi_invalid_field(dev, 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]) { @@ -3698,7 +3759,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2); break; default: - ata_scsi_invalid_field(dev, cmd); + ata_scsi_invalid_field(dev, cmd, 2); break; } break; @@ -3716,7 +3777,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(dev, cmd); + ata_scsi_invalid_field(dev, cmd, 1); break; case REPORT_LUNS: @@ -3748,7 +3809,7 @@ 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(dev, cmd); + ata_scsi_invalid_field(dev, cmd, 1); break; /* all other commands */ From 0df10b84af88a482beea982f5f27a2e42157f600 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:06 +0200 Subject: [PATCH 13/28] libata-scsi: set bit pointer for sense code information When generating a sense code of 'Invalid field in CDB' we should be setting the bit pointer where appropriate. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 062cb2eee8de..339a373250f3 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -301,12 +301,12 @@ void ata_scsi_set_sense_information(struct ata_device *dev, } static void ata_scsi_set_invalid_field(struct ata_device *dev, - struct scsi_cmnd *cmd, u16 field) + 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, 0xff, 1); + field, bit, 1); } static ssize_t @@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(ata_common_sdev_attrs); static void ata_scsi_invalid_field(struct ata_device *dev, struct scsi_cmnd *cmd, u16 field) { - ata_scsi_set_invalid_field(dev, cmd, field); + ata_scsi_set_invalid_field(dev, cmd, field, 0xff); cmd->scsi_done(cmd); } @@ -1395,6 +1395,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) struct ata_taskfile *tf = &qc->tf; const u8 *cdb = scmd->cmnd; u16 fp; + u8 bp = 0xff; if (scmd->cmd_len < 5) { fp = 4; @@ -1408,10 +1409,12 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) } if (cdb[4] & 0x2) { fp = 4; + bp = 1; goto invalid_fld; /* LOEJ bit set not supported */ } if (((cdb[4] >> 4) & 0xf) != 0) { fp = 4; + bp = 3; goto invalid_fld; /* power conditions not supported */ } @@ -1459,7 +1462,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_invalid_field(qc->dev, scmd, fp); + ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); return 1; skip: scmd->result = SAM_STAT_GOOD; @@ -1704,7 +1707,7 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_invalid_field(qc->dev, scmd, fp); + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); return 1; out_of_range: @@ -1813,7 +1816,7 @@ 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_invalid_field(qc->dev, scmd, fp); + ata_scsi_set_invalid_field(qc->dev, scmd, fp, 0xff); return 1; out_of_range: @@ -2471,7 +2474,7 @@ 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"); @@ -2492,6 +2495,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) goto saving_not_supp; default: fp = 2; + bp = 6; goto invalid_fld; } @@ -2561,7 +2565,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf) return 0; invalid_fld: - ata_scsi_set_invalid_field(dev, args->cmd, fp); + ata_scsi_set_invalid_field(dev, args->cmd, fp, bp); return 1; saving_not_supp: @@ -3215,7 +3219,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_invalid_field(dev, scmd, fp); + ata_scsi_set_invalid_field(dev, scmd, fp, 0xff); return 1; } @@ -3230,6 +3234,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) 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)) @@ -3244,6 +3249,7 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) /* for now we only support WRITE SAME with the unmap bit set */ if (unlikely(!(cdb[1] & 0x8))) { fp = 1; + bp = 3; goto invalid_fld; } @@ -3284,7 +3290,7 @@ 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); + ata_scsi_set_invalid_field(dev, scmd, fp, bp); return 1; invalid_param_len: /* "Parameter list length error" */ @@ -3407,6 +3413,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) unsigned six_byte, pg_len, hdr_len, bd_len; int len; u16 fp; + u8 bp; VPRINTK("ENTER\n"); @@ -3432,6 +3439,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) /* We only support PF=1, SP=0. */ if ((cdb[1] & 0x11) != 0x10) { fp = 1; + bp = (cdb[1] & 0x01) ? 1 : 5; goto invalid_fld; } @@ -3514,7 +3522,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) return 0; invalid_fld: - ata_scsi_set_invalid_field(qc->dev, scmd, fp); + ata_scsi_set_invalid_field(qc->dev, scmd, fp, bp); return 1; invalid_param: From 7780081c1f04a4ea31331b5579ca010cc1f26c74 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 4 Apr 2016 11:44:07 +0200 Subject: [PATCH 14/28] libata-scsi: Set information sense field for invalid parameter Whenever the sense key is set to 'invalid parameter' we should be filling out the sense-key specific information field in the sense buffer. tj: Added description of @fp for ata_mselect_*(). Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 81 +++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 339a373250f3..8b61d63ab0be 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -309,6 +309,15 @@ static void ata_scsi_set_invalid_field(struct ata_device *dev, 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 ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -3307,6 +3316,7 @@ invalid_opcode: * @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. * @@ -3314,20 +3324,26 @@ invalid_opcode: * 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); @@ -3335,10 +3351,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; @@ -3353,6 +3373,7 @@ static int ata_mselect_caching(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. * @@ -3360,19 +3381,25 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc, * None. */ static int ata_mselect_control(struct ata_queued_cmd *qc, - const u8 *buf, int len) + 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) { + if (len < CONTROL_MPAGE_LEN - 2) + *fp = len; + else + *fp = CONTROL_MPAGE_LEN - 2; return -EINVAL; + } d_sense = buf[0] & (1 << 2); @@ -3380,10 +3407,14 @@ static int ata_mselect_control(struct ata_queued_cmd *qc, * Check that read-only bits are not modified. */ ata_msense_ctl_mode(dev, mpage, false); - mpage[2] &= ~(1 << 2); - mpage[2] |= d_sense; - if (memcmp(mpage + 2, buf, CONTROL_MPAGE_LEN - 2) != 0) - return -EINVAL; + 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 @@ -3412,8 +3443,8 @@ 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; - u8 bp; + u16 fp = (u16)-1; + u8 bp = 0xff; VPRINTK("ENTER\n"); @@ -3462,8 +3493,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; @@ -3494,21 +3528,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) < 0) + 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; } @@ -3526,8 +3568,7 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) return 1; invalid_param: - /* "Invalid field in parameter list" */ - ata_scsi_set_sense(qc->dev, scmd, ILLEGAL_REQUEST, 0x26, 0x0); + ata_scsi_set_invalid_parameter(qc->dev, scmd, fp); return 1; invalid_param_len: From d238ffd59d3c65c608f39a0ab2819b63c912f783 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:43 +0200 Subject: [PATCH 15/28] libata: do not attempt to retrieve sense code twice Do not call ata_request_sense() if the sense code is already present. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 5b340ce4eeac..e81661981a09 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1660,7 +1660,7 @@ static void ata_eh_request_sense(struct ata_queued_cmd *qc, return; } - if (!cmd) + if (!cmd || qc->flags & ATA_QCFLAG_SENSE_VALID) return; if (!ata_id_sense_reporting_enabled(dev->id)) { @@ -1849,7 +1849,7 @@ 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.auxiliary) { + if ((qc->result_tf.command & ATA_SENSE) || qc->result_tf.auxiliary) { char sense_key, asc, ascq; sense_key = (qc->result_tf.auxiliary >> 16) & 0xff; From ef026b18bb8260e21b4a61685eac46ecdc490d00 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:44 +0200 Subject: [PATCH 16/28] libsas: enable FPDMA SEND/RECEIVE Update libsas and dependent drivers to handle FPDMA SEND/RECEIVE correctly. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 2 ++ drivers/scsi/isci/request.c | 4 +++- drivers/scsi/libsas/sas_ata.c | 6 ++++-- drivers/scsi/mvsas/mv_sas.c | 4 +++- drivers/scsi/pm8001/pm8001_sas.c | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index bbe98ecea0bc..860c9f847371 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1630,6 +1630,8 @@ static u8 get_ata_protocol(u8 cmd, int direction) switch (cmd) { case ATA_CMD_FPDMA_WRITE: case ATA_CMD_FPDMA_READ: + case ATA_CMD_FPDMA_RECV: + case ATA_CMD_FPDMA_SEND: return SATA_PROTOCOL_FPDMA; case ATA_CMD_ID_ATA: diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index cfd0084f1cd2..29456e097a30 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3169,7 +3169,9 @@ static enum sci_status isci_request_stp_request_construct(struct isci_request *i status = sci_io_request_construct_basic_sata(ireq); if (qc && (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ)) { + qc->tf.command == ATA_CMD_FPDMA_READ || + qc->tf.command == ATA_CMD_FPDMA_RECV || + qc->tf.command == ATA_CMD_FPDMA_SEND)) { fis->sector_count = qc->tag << 3; ireq->tc->type.stp.ncq_tag = qc->tag; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 9c706d8c1441..fe1cd2691748 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -205,7 +205,9 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->task_done = sas_ata_task_done; if (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ) { + qc->tf.command == ATA_CMD_FPDMA_READ || + qc->tf.command == ATA_CMD_FPDMA_RECV || + qc->tf.command == ATA_CMD_FPDMA_SEND) { /* Need to zero out the tag libata assigned us */ qc->tf.nsect = 0; } @@ -548,7 +550,7 @@ static struct ata_port_operations sas_sata_ops = { static struct ata_port_info sata_port_info = { .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | - ATA_FLAG_SAS_HOST, + ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 83cd3ea2df41..db3714964c0a 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -429,7 +429,9 @@ static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag) if (qc) { if (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ) { + qc->tf.command == ATA_CMD_FPDMA_READ || + qc->tf.command == ATA_CMD_FPDMA_RECV || + qc->tf.command == ATA_CMD_FPDMA_SEND) { *tag = qc->tag; return 1; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 949198c01ced..62abd9896625 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -280,7 +280,9 @@ u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag) struct ata_queued_cmd *qc = task->uldd_task; if (qc) { if (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ) { + qc->tf.command == ATA_CMD_FPDMA_READ || + qc->tf.command == ATA_CMD_FPDMA_RECV || + qc->tf.command == ATA_CMD_FPDMA_SEND) { *tag = qc->tag; return 1; } From 661ce1f0c4a69f92ad781d8d2c205c90dd9c5833 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:45 +0200 Subject: [PATCH 17/28] libata/libsas: Define ATA_CMD_NCQ_NON_DATA Define the NCQ NON DATA command and update libsas to handle it correctly. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 1 + drivers/scsi/isci/request.c | 3 ++- drivers/scsi/libsas/sas_ata.c | 3 ++- drivers/scsi/mvsas/mv_sas.c | 3 ++- drivers/scsi/pm8001/pm8001_sas.c | 3 ++- include/linux/ata.h | 1 + include/trace/events/libata.h | 1 + 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 860c9f847371..bd20c5488768 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1632,6 +1632,7 @@ static u8 get_ata_protocol(u8 cmd, int direction) case ATA_CMD_FPDMA_READ: case ATA_CMD_FPDMA_RECV: case ATA_CMD_FPDMA_SEND: + case ATA_CMD_NCQ_NON_DATA: return SATA_PROTOCOL_FPDMA; case ATA_CMD_ID_ATA: diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 29456e097a30..b709d2b20880 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3171,7 +3171,8 @@ static enum sci_status isci_request_stp_request_construct(struct isci_request *i if (qc && (qc->tf.command == ATA_CMD_FPDMA_WRITE || qc->tf.command == ATA_CMD_FPDMA_READ || qc->tf.command == ATA_CMD_FPDMA_RECV || - qc->tf.command == ATA_CMD_FPDMA_SEND)) { + qc->tf.command == ATA_CMD_FPDMA_SEND || + qc->tf.command == ATA_CMD_NCQ_NON_DATA)) { fis->sector_count = qc->tag << 3; ireq->tc->type.stp.ncq_tag = qc->tag; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index fe1cd2691748..935c43095109 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -207,7 +207,8 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) if (qc->tf.command == ATA_CMD_FPDMA_WRITE || qc->tf.command == ATA_CMD_FPDMA_READ || qc->tf.command == ATA_CMD_FPDMA_RECV || - qc->tf.command == ATA_CMD_FPDMA_SEND) { + qc->tf.command == ATA_CMD_FPDMA_SEND || + qc->tf.command == ATA_CMD_NCQ_NON_DATA) { /* Need to zero out the tag libata assigned us */ qc->tf.nsect = 0; } diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index db3714964c0a..5b9fcff6cd94 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -431,7 +431,8 @@ static u32 mvs_get_ncq_tag(struct sas_task *task, u32 *tag) if (qc->tf.command == ATA_CMD_FPDMA_WRITE || qc->tf.command == ATA_CMD_FPDMA_READ || qc->tf.command == ATA_CMD_FPDMA_RECV || - qc->tf.command == ATA_CMD_FPDMA_SEND) { + qc->tf.command == ATA_CMD_FPDMA_SEND || + qc->tf.command == ATA_CMD_NCQ_NON_DATA) { *tag = qc->tag; return 1; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 62abd9896625..dc33dfa8f994 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -282,7 +282,8 @@ u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag) if (qc->tf.command == ATA_CMD_FPDMA_WRITE || qc->tf.command == ATA_CMD_FPDMA_READ || qc->tf.command == ATA_CMD_FPDMA_RECV || - qc->tf.command == ATA_CMD_FPDMA_SEND) { + qc->tf.command == ATA_CMD_FPDMA_SEND || + qc->tf.command == ATA_CMD_NCQ_NON_DATA) { *tag = qc->tag; return 1; } diff --git a/include/linux/ata.h b/include/linux/ata.h index 00aebc4c83ad..b84210a28a00 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -243,6 +243,7 @@ enum { ATA_CMD_WRITE_QUEUED_FUA_EXT = 0x3E, ATA_CMD_FPDMA_READ = 0x60, ATA_CMD_FPDMA_WRITE = 0x61, + ATA_CMD_NCQ_NON_DATA = 0x63, ATA_CMD_FPDMA_SEND = 0x64, ATA_CMD_FPDMA_RECV = 0x65, ATA_CMD_PIO_READ = 0x20, diff --git a/include/trace/events/libata.h b/include/trace/events/libata.h index 8b0fbd93082c..016860320f6f 100644 --- a/include/trace/events/libata.h +++ b/include/trace/events/libata.h @@ -39,6 +39,7 @@ ata_opcode_name(ATA_CMD_WRITE_QUEUED_FUA_EXT), \ ata_opcode_name(ATA_CMD_FPDMA_READ), \ ata_opcode_name(ATA_CMD_FPDMA_WRITE), \ + ata_opcode_name(ATA_CMD_NCQ_NON_DATA), \ ata_opcode_name(ATA_CMD_FPDMA_SEND), \ ata_opcode_name(ATA_CMD_FPDMA_RECV), \ ata_opcode_name(ATA_CMD_PIO_READ), \ From 5a2335512ad30ea38e53e112ca51e8a314dc6874 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:46 +0200 Subject: [PATCH 18/28] libata: Separate out ata_dev_config_ncq_send_recv() Move NCQ SEND/RECEIVE checks into a separate function. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 7bdb2c4e357f..fa74b57ee52e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2079,6 +2079,31 @@ 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; + + 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 int ata_dev_config_ncq(struct ata_device *dev, char *desc, size_t desc_sz) { @@ -2124,26 +2149,8 @@ static int ata_dev_config_ncq(struct ata_device *dev, 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; - - 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; - } - } - } + ata_id_has_ncq_send_and_recv(dev->id)) + ata_dev_config_ncq_send_recv(dev); return 0; } From 5c65d8bb3503beb12864895426a69269c19e6e87 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:47 +0200 Subject: [PATCH 19/28] libata: Add command definitions for NCQ Encapsulation for READ LOG DMA EXT ACS-4 defines an NCQ encapsulation for READ LOG DMA EXT. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- include/linux/ata.h | 5 +++++ include/linux/libata.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/include/linux/ata.h b/include/linux/ata.h index b84210a28a00..94ccde5ee83c 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -306,6 +306,9 @@ enum { /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, + /* Subcmds for ATA_CMD_FPDMA_RECV */ + ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT = 0x01, + /* Subcmds for ATA_CMD_FPDMA_SEND */ ATA_SUBCMD_FPDMA_SEND_DSM = 0x00, ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 0x02, @@ -329,7 +332,9 @@ enum { ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET = 0x04, ATA_LOG_NCQ_SEND_RECV_DSM_TRIM = (1 << 0), ATA_LOG_NCQ_SEND_RECV_RD_LOG_OFFSET = 0x08, + ATA_LOG_NCQ_SEND_RECV_RD_LOG_SUPPORTED = (1 << 0), ATA_LOG_NCQ_SEND_RECV_WR_LOG_OFFSET = 0x0C, + ATA_LOG_NCQ_SEND_RECV_WR_LOG_SUPPORTED = (1 << 0), ATA_LOG_NCQ_SEND_RECV_SIZE = 0x10, /* READ/WRITE LONG (obsolete) */ diff --git a/include/linux/libata.h b/include/linux/libata.h index a418bca0df0d..09ddb5a6f555 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1642,6 +1642,13 @@ static inline bool ata_fpdma_dsm_supported(struct ata_device *dev) ATA_LOG_NCQ_SEND_RECV_DSM_TRIM); } +static inline bool ata_fpdma_read_log_supported(struct ata_device *dev) +{ + return (dev->flags & ATA_DFLAG_NCQ_SEND_RECV) && + (dev->ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_RD_LOG_OFFSET] & + ATA_LOG_NCQ_SEND_RECV_RD_LOG_SUPPORTED); +} + static inline void ata_qc_set_polling(struct ata_queued_cmd *qc) { qc->tf.ctl |= ATA_NIEN; From fe5af0cc3029d52e31d282f5d53787d308e9695a Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:48 +0200 Subject: [PATCH 20/28] libata: Check log page directory before accessing pages When reading the NCQ Send/Recv log it might actually not supported, thereby causing irritating messages 'READ LOG DMA EXT failed'. Instead we should be reading the log directory first to figure out if the log is actually supported before trying to access it. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 17 +++++++++++++++++ include/linux/ata.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index fa74b57ee52e..b2bd7c499653 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -2083,7 +2084,23 @@ 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) { diff --git a/include/linux/ata.h b/include/linux/ata.h index 94ccde5ee83c..b5be5e85d2d3 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -314,6 +314,7 @@ enum { ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 0x02, /* READ_LOG_EXT pages */ + ATA_LOG_DIRECTORY = 0x0, ATA_LOG_SATA_NCQ = 0x10, ATA_LOG_NCQ_SEND_RECV = 0x13, ATA_LOG_SATA_ID_DEV_DATA = 0x30, From a57038496422d7d21b7e41ed70d63bf0c6ff6068 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:49 +0200 Subject: [PATCH 21/28] libata-trace: decode subcommands Some commands like FPDMA RECEIVE or NCQ NON DATA can encapsulate other commands to NCQ transport. So decode the subcmds, too. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-trace.c | 43 +++++++++++++++++++++++++++++++++++ include/linux/ata.h | 17 ++++++++++++++ include/trace/events/libata.h | 7 +++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index fd30b8c10cf5..99ec1e8cb95d 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -149,3 +149,46 @@ 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; + } + 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; + } + break; + } + trace_seq_putc(p, 0); + + return ret; +} diff --git a/include/linux/ata.h b/include/linux/ata.h index b5be5e85d2d3..032bb223cd8c 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -313,6 +313,11 @@ enum { ATA_SUBCMD_FPDMA_SEND_DSM = 0x00, ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 0x02, + /* Subcmds for ATA_CMD_NCQ_NON_DATA */ + ATA_SUBCMD_NCQ_NON_DATA_ABORT_QUEUE = 0x00, + ATA_SUBCMD_NCQ_NON_DATA_SET_FEATURES = 0x05, + ATA_SUBCMD_NCQ_NON_DATA_ZERO_EXT = 0x06, + /* READ_LOG_EXT pages */ ATA_LOG_DIRECTORY = 0x0, ATA_LOG_SATA_NCQ = 0x10, @@ -338,6 +343,18 @@ enum { ATA_LOG_NCQ_SEND_RECV_WR_LOG_SUPPORTED = (1 << 0), ATA_LOG_NCQ_SEND_RECV_SIZE = 0x10, + /* NCQ Non-Data log */ + ATA_LOG_NCQ_NON_DATA_SUBCMDS_OFFSET = 0x00, + ATA_LOG_NCQ_NON_DATA_ABORT_OFFSET = 0x00, + ATA_LOG_NCQ_NON_DATA_ABORT_NCQ = (1 << 0), + ATA_LOG_NCQ_NON_DATA_ABORT_ALL = (1 << 1), + ATA_LOG_NCQ_NON_DATA_ABORT_STREAMING = (1 << 2), + ATA_LOG_NCQ_NON_DATA_ABORT_NON_STREAMING = (1 << 3), + ATA_LOG_NCQ_NON_DATA_ABORT_SELECTED = (1 << 4), + ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OFFSET = 0x1C, + ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OUT = (1 << 0), + ATA_LOG_NCQ_NON_DATA_SIZE = 0x40, + /* READ/WRITE LONG (obsolete) */ ATA_CMD_READ_LONG = 0x22, ATA_CMD_READ_LONG_ONCE = 0x23, diff --git a/include/trace/events/libata.h b/include/trace/events/libata.h index 016860320f6f..8e77572350f0 100644 --- a/include/trace/events/libata.h +++ b/include/trace/events/libata.h @@ -140,6 +140,10 @@ const char *libata_trace_parse_eh_err_mask(struct trace_seq *, unsigned int); const char *libata_trace_parse_qc_flags(struct trace_seq *, unsigned int); #define __parse_qc_flags(f) libata_trace_parse_qc_flags(p, f) +const char *libata_trace_parse_subcmd(struct trace_seq *, unsigned char, + unsigned char, unsigned char); +#define __parse_subcmd(c,f,h) libata_trace_parse_subcmd(p, c, f, h) + TRACE_EVENT(ata_qc_issue, TP_PROTO(struct ata_queued_cmd *qc), @@ -186,11 +190,12 @@ TRACE_EVENT(ata_qc_issue, __entry->hob_nsect = qc->tf.hob_nsect; ), - TP_printk("ata_port=%u ata_dev=%u tag=%d proto=%s cmd=%s " \ + TP_printk("ata_port=%u ata_dev=%u tag=%d proto=%s cmd=%s%s " \ " tf=(%02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x)", __entry->ata_port, __entry->ata_dev, __entry->tag, show_protocol_name(__entry->proto), show_opcode_name(__entry->cmd), + __parse_subcmd(__entry->cmd, __entry->feature, __entry->hob_nsect), __entry->cmd, __entry->feature, __entry->nsect, __entry->lbal, __entry->lbam, __entry->lbah, __entry->hob_feature, __entry->hob_nsect, From 59f68474c2a1f5f0cedede60b60de97c603dc194 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:50 +0200 Subject: [PATCH 22/28] libata-scsi: Generate sense code for disabled devices If a device is disabled after error recovery it doesn't make any sense to generate an ATA sense, but we should rather return a generic sense code indicating the device is gone. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 8b61d63ab0be..6d78b4b422a4 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1126,6 +1126,12 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + 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. */ From 3a92945b24c7ff46757a3d5d5112bfc62d2e45b2 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:51 +0200 Subject: [PATCH 23/28] libata: fixup ZAC device disabling libata device disabling is ... curious. So add the correct definitions that we can disable ZAC devices properly. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- include/linux/libata.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/libata.h b/include/linux/libata.h index 09ddb5a6f555..92297cd111f6 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -192,7 +192,8 @@ enum { ATA_DEV_SEMB = 7, /* SEMB */ ATA_DEV_SEMB_UNSUP = 8, /* SEMB (unsupported) */ ATA_DEV_ZAC = 9, /* ZAC device */ - ATA_DEV_NONE = 10, /* no device */ + ATA_DEV_ZAC_UNSUP = 10, /* ZAC device (unsupported) */ + ATA_DEV_NONE = 11, /* no device */ /* struct ata_link flags */ ATA_LFLAG_NO_HRST = (1 << 1), /* avoid hardreset */ @@ -1524,7 +1525,8 @@ static inline unsigned int ata_class_enabled(unsigned int class) static inline unsigned int ata_class_disabled(unsigned int class) { return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP || - class == ATA_DEV_PMP_UNSUP || class == ATA_DEV_SEMB_UNSUP; + class == ATA_DEV_PMP_UNSUP || class == ATA_DEV_SEMB_UNSUP || + class == ATA_DEV_ZAC_UNSUP; } static inline unsigned int ata_class_absent(unsigned int class) From 28a3fc2295a744a0d2ddf86b2ccdf03fbab123f9 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:52 +0200 Subject: [PATCH 24/28] libata: implement ZBC IN translation ZAC drives implement a 'ZAC Management In' command template, which maps onto the ZBC IN command. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata-scsi.c | 157 ++++++++++++++++++++++++++++++++++ drivers/ata/libata-trace.c | 10 +++ include/linux/ata.h | 10 ++- include/linux/libata.h | 7 ++ include/trace/events/libata.h | 1 + 6 files changed, 185 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e81661981a09..ee6c572d2a4a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2482,6 +2482,7 @@ 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_READ_LONG, "READ LONG (with retries)" }, { ATA_CMD_READ_LONG_ONCE, "READ LONG (without retries)" }, { ATA_CMD_WRITE_LONG, "WRITE LONG (with retries)" }, diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 6d78b4b422a4..06d5a62f507d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3317,6 +3317,160 @@ invalid_opcode: 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; +} + /** * ata_mselect_caching - Simulate MODE SELECT for caching info page * @qc: Storage for translated ATA taskfile @@ -3632,6 +3786,9 @@ 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 START_STOP: return ata_scsi_start_stop_xlat; } diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index 99ec1e8cb95d..9caeabd69ccb 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -162,6 +162,9 @@ libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, 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: @@ -187,6 +190,13 @@ libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, 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; } trace_seq_putc(p, 0); diff --git a/include/linux/ata.h b/include/linux/ata.h index 032bb223cd8c..255aa0f1c9bc 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -302,12 +302,14 @@ enum { ATA_CMD_CFA_WRITE_MULT_NE = 0xCD, ATA_CMD_REQ_SENSE_DATA = 0x0B, ATA_CMD_SANITIZE_DEVICE = 0xB4, + ATA_CMD_ZAC_MGMT_IN = 0x4A, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, /* Subcmds for ATA_CMD_FPDMA_RECV */ ATA_SUBCMD_FPDMA_RECV_RD_LOG_DMA_EXT = 0x01, + ATA_SUBCMD_FPDMA_RECV_ZAC_MGMT_IN = 0x02, /* Subcmds for ATA_CMD_FPDMA_SEND */ ATA_SUBCMD_FPDMA_SEND_DSM = 0x00, @@ -318,6 +320,9 @@ enum { ATA_SUBCMD_NCQ_NON_DATA_SET_FEATURES = 0x05, ATA_SUBCMD_NCQ_NON_DATA_ZERO_EXT = 0x06, + /* Subcmds for ATA_CMD_ZAC_MGMT_IN */ + ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES = 0x00, + /* READ_LOG_EXT pages */ ATA_LOG_DIRECTORY = 0x0, ATA_LOG_SATA_NCQ = 0x10, @@ -341,7 +346,10 @@ enum { ATA_LOG_NCQ_SEND_RECV_RD_LOG_SUPPORTED = (1 << 0), ATA_LOG_NCQ_SEND_RECV_WR_LOG_OFFSET = 0x0C, ATA_LOG_NCQ_SEND_RECV_WR_LOG_SUPPORTED = (1 << 0), - ATA_LOG_NCQ_SEND_RECV_SIZE = 0x10, + ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_OFFSET = 0x10, + ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_OUT_SUPPORTED = (1 << 0), + ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_IN_SUPPORTED = (1 << 1), + ATA_LOG_NCQ_SEND_RECV_SIZE = 0x14, /* NCQ Non-Data log */ ATA_LOG_NCQ_NON_DATA_SUBCMDS_OFFSET = 0x00, diff --git a/include/linux/libata.h b/include/linux/libata.h index 92297cd111f6..c0806b60c4fa 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1651,6 +1651,13 @@ static inline bool ata_fpdma_read_log_supported(struct ata_device *dev) ATA_LOG_NCQ_SEND_RECV_RD_LOG_SUPPORTED); } +static inline bool ata_fpdma_zac_mgmt_in_supported(struct ata_device *dev) +{ + return (dev->flags & ATA_DFLAG_NCQ_SEND_RECV) && + (dev->ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_OFFSET] & + ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_IN_SUPPORTED); +} + static inline void ata_qc_set_polling(struct ata_queued_cmd *qc) { qc->tf.ctl |= ATA_NIEN; diff --git a/include/trace/events/libata.h b/include/trace/events/libata.h index 8e77572350f0..77370a650c15 100644 --- a/include/trace/events/libata.h +++ b/include/trace/events/libata.h @@ -98,6 +98,7 @@ ata_opcode_name(ATA_CMD_CFA_WRITE_MULT_NE), \ ata_opcode_name(ATA_CMD_REQ_SENSE_DATA), \ ata_opcode_name(ATA_CMD_SANITIZE_DEVICE), \ + ata_opcode_name(ATA_CMD_ZAC_MGMT_IN), \ ata_opcode_name(ATA_CMD_RESTORE), \ ata_opcode_name(ATA_CMD_READ_LONG), \ ata_opcode_name(ATA_CMD_READ_LONG_ONCE), \ From 27708a9579ee069c6e0ebb6e61ac1114ed1d546c Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:53 +0200 Subject: [PATCH 25/28] libata: Implement ZBC OUT translation ZAC drives implement a 'ZAC Management Out' command template, which maps onto the ZBC OUT command. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata-scsi.c | 67 +++++++++++++++++++++++++++++++++++ drivers/ata/libata-trace.c | 16 +++++++++ include/linux/ata.h | 7 ++++ include/trace/events/libata.h | 1 + 5 files changed, 92 insertions(+) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index ee6c572d2a4a..61dc7a99e89a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2483,6 +2483,7 @@ const char *ata_get_cmd_descript(u8 command) { 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)" }, diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 06d5a62f507d..6afd0840ebbe 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3471,6 +3471,70 @@ invalid_param_len: 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; + + 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_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; +} + /** * ata_mselect_caching - Simulate MODE SELECT for caching info page * @qc: Storage for translated ATA taskfile @@ -3789,6 +3853,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) 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; } diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index 9caeabd69ccb..1111ba7db5b3 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -197,6 +197,22 @@ libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, 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); diff --git a/include/linux/ata.h b/include/linux/ata.h index 255aa0f1c9bc..9d7c47075ebc 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -303,6 +303,7 @@ enum { ATA_CMD_REQ_SENSE_DATA = 0x0B, ATA_CMD_SANITIZE_DEVICE = 0xB4, ATA_CMD_ZAC_MGMT_IN = 0x4A, + ATA_CMD_ZAC_MGMT_OUT = 0x9F, /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, @@ -323,6 +324,12 @@ enum { /* Subcmds for ATA_CMD_ZAC_MGMT_IN */ ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES = 0x00, + /* Subcmds for ATA_CMD_ZAC_MGMT_OUT */ + ATA_SUBCMD_ZAC_MGMT_OUT_CLOSE_ZONE = 0x01, + ATA_SUBCMD_ZAC_MGMT_OUT_FINISH_ZONE = 0x02, + ATA_SUBCMD_ZAC_MGMT_OUT_OPEN_ZONE = 0x03, + ATA_SUBCMD_ZAC_MGMT_OUT_RESET_WRITE_POINTER = 0x04, + /* READ_LOG_EXT pages */ ATA_LOG_DIRECTORY = 0x0, ATA_LOG_SATA_NCQ = 0x10, diff --git a/include/trace/events/libata.h b/include/trace/events/libata.h index 77370a650c15..75fff8696bae 100644 --- a/include/trace/events/libata.h +++ b/include/trace/events/libata.h @@ -99,6 +99,7 @@ ata_opcode_name(ATA_CMD_REQ_SENSE_DATA), \ ata_opcode_name(ATA_CMD_SANITIZE_DEVICE), \ ata_opcode_name(ATA_CMD_ZAC_MGMT_IN), \ + ata_opcode_name(ATA_CMD_ZAC_MGMT_OUT), \ ata_opcode_name(ATA_CMD_RESTORE), \ ata_opcode_name(ATA_CMD_READ_LONG), \ ata_opcode_name(ATA_CMD_READ_LONG_ONCE), \ From 284b3b77ea883234dadb2cbf97b145c3c30fe4bd Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:54 +0200 Subject: [PATCH 26/28] libata: NCQ encapsulation for ZAC MANAGEMENT OUT Add NCQ encapsulation for ZAC MANAGEMENT OUT and evaluate NCQ Non-Data log pages to figure out if NCQ encapsulation is supported. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 43 +++++++++++++++++++++++++++++++++++--- drivers/ata/libata-scsi.c | 18 +++++++++++----- drivers/ata/libata-trace.c | 3 +++ include/linux/ata.h | 7 +++++++ include/linux/libata.h | 7 +++++++ 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b2bd7c499653..1528c7cc0089 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2121,6 +2121,40 @@ static void ata_dev_config_ncq_send_recv(struct ata_device *dev) } } +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) { @@ -2165,9 +2199,12 @@ 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)) - ata_dev_config_ncq_send_recv(dev); + 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); + } return 0; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 6afd0840ebbe..43403aa0801f 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3506,11 +3506,19 @@ static unsigned int ata_scsi_zbc_out_xlat(struct ata_queued_cmd *qc) reset_all = cdb[14] & 0x1; - tf->protocol = ATA_PROT_NODATA; - tf->command = ATA_CMD_ZAC_MGMT_OUT; - tf->feature = sa; - tf->hob_feature = reset_all & 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; diff --git a/drivers/ata/libata-trace.c b/drivers/ata/libata-trace.c index 1111ba7db5b3..f8c550df0615 100644 --- a/drivers/ata/libata-trace.c +++ b/drivers/ata/libata-trace.c @@ -188,6 +188,9 @@ libata_trace_parse_subcmd(struct trace_seq *p, unsigned char cmd, 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: diff --git a/include/linux/ata.h b/include/linux/ata.h index 9d7c47075ebc..e62703201e84 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -320,6 +320,7 @@ enum { ATA_SUBCMD_NCQ_NON_DATA_ABORT_QUEUE = 0x00, ATA_SUBCMD_NCQ_NON_DATA_SET_FEATURES = 0x05, ATA_SUBCMD_NCQ_NON_DATA_ZERO_EXT = 0x06, + ATA_SUBCMD_NCQ_NON_DATA_ZAC_MGMT_OUT = 0x07, /* Subcmds for ATA_CMD_ZAC_MGMT_IN */ ATA_SUBCMD_ZAC_MGMT_IN_REPORT_ZONES = 0x00, @@ -333,6 +334,7 @@ enum { /* READ_LOG_EXT pages */ ATA_LOG_DIRECTORY = 0x0, ATA_LOG_SATA_NCQ = 0x10, + ATA_LOG_NCQ_NON_DATA = 0x12, ATA_LOG_NCQ_SEND_RECV = 0x13, ATA_LOG_SATA_ID_DEV_DATA = 0x30, ATA_LOG_SATA_SETTINGS = 0x08, @@ -877,6 +879,11 @@ static inline bool ata_id_has_ncq_send_and_recv(const u16 *id) return id[ATA_ID_SATA_CAPABILITY_2] & BIT(6); } +static inline bool ata_id_has_ncq_non_data(const u16 *id) +{ + return id[ATA_ID_SATA_CAPABILITY_2] & BIT(5); +} + static inline bool ata_id_has_trim(const u16 *id) { if (ata_id_major_version(id) >= 7 && diff --git a/include/linux/libata.h b/include/linux/libata.h index c0806b60c4fa..0019d4b51b11 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -729,6 +729,7 @@ struct ata_device { /* NCQ send and receive log subcommand support */ u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE]; + u8 ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_SIZE]; /* error history */ int spdn_cnt; @@ -1658,6 +1659,12 @@ static inline bool ata_fpdma_zac_mgmt_in_supported(struct ata_device *dev) ATA_LOG_NCQ_SEND_RECV_ZAC_MGMT_IN_SUPPORTED); } +static inline bool ata_fpdma_zac_mgmt_out_supported(struct ata_device *dev) +{ + return (dev->ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OFFSET] & + ATA_LOG_NCQ_NON_DATA_ZAC_MGMT_OUT); +} + static inline void ata_qc_set_polling(struct ata_queued_cmd *qc) { qc->tf.ctl |= ATA_NIEN; From 856c4663930988118d9f355aad66811dd6df06de Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:55 +0200 Subject: [PATCH 27/28] libata: support device-managed ZAC devices Device-managed ZAC devices just set the zoned capabilities field in INQUIRY byte 69 (cf ACS-4). This corresponds to the 'zoned' field in the block device characteristics VPD page. As this is only defined in SPC-5/SBC-4 we also need to update the supported SCSI version descriptor. Reviewed-by: Shaun Tancheff Tested-by: Shaun Tancheff Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 19 ++++++++++--------- include/linux/ata.h | 5 +++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 43403aa0801f..96abd42c9985 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2082,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[] = { @@ -2109,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); @@ -2126,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)); @@ -2322,12 +2320,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; } diff --git a/include/linux/ata.h b/include/linux/ata.h index e62703201e84..ac1cb9310dea 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -935,6 +935,11 @@ static inline bool ata_id_is_ssd(const u16 *id) return id[ATA_ID_ROT_SPEED] == 0x01; } +static inline u8 ata_id_zoned_cap(const u16 *id) +{ + return (id[ATA_ID_ADDITIONAL_SUPP] & 0x3); +} + static inline bool ata_id_pio_need_iordy(const u16 *id, const u8 pio) { /* CF spec. r4.1 Table 22 says no IORDY on PIO5 and PIO6. */ From 6d1003ae8db228b74ef61536364cd2a1bd973dd8 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 25 Apr 2016 12:45:56 +0200 Subject: [PATCH 28/28] libata: support host-aware and host-managed ZAC devices Byte 69 bits 0:1 in the IDENTIFY DEVICE data indicate a host-aware ZAC device. Host-managed ZAC devices have their own individual signature, and to not set the bits in the IDENTIFY DEVICE data. And whenever we detect a ZAC-compatible device we should be displaying the zoned block characteristics VPD page. Signed-off-by: Hannes Reinecke Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 94 +++++++++++++++++++++++++++++++++++++++ drivers/ata/libata-scsi.c | 38 +++++++++++++++- include/linux/ata.h | 1 + include/linux/libata.h | 7 +++ 4 files changed, 138 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 1528c7cc0089..97f31707b570 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2227,6 +2227,99 @@ static void ata_dev_config_sense_reporting(struct ata_device *dev) } } +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; + } + + /* + * 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; + } +} + /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -2450,6 +2543,7 @@ int ata_dev_configure(struct ata_device *dev) } } ata_dev_config_sense_reporting(dev); + ata_dev_config_zac(dev); dev->cdb_len = 16; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 96abd42c9985..b86af1416dce 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2144,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 */ @@ -2152,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; } @@ -2343,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. @@ -2661,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; } @@ -4046,6 +4074,12 @@ 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(dev, cmd, 2); break; diff --git a/include/linux/ata.h b/include/linux/ata.h index ac1cb9310dea..83e2a99866c2 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -338,6 +338,7 @@ enum { ATA_LOG_NCQ_SEND_RECV = 0x13, ATA_LOG_SATA_ID_DEV_DATA = 0x30, ATA_LOG_SATA_SETTINGS = 0x08, + ATA_LOG_ZONED_INFORMATION = 0x09, ATA_LOG_DEVSLP_OFFSET = 0x30, ATA_LOG_DEVSLP_SIZE = 0x08, ATA_LOG_DEVSLP_MDAT = 0x00, diff --git a/include/linux/libata.h b/include/linux/libata.h index 0019d4b51b11..d15c19e331d1 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -181,6 +181,7 @@ enum { 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_ZAC = (1 << 30), /* ZAC device */ ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ @@ -731,6 +732,12 @@ struct ata_device { u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE]; u8 ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_SIZE]; + /* ZAC zone configuration */ + u32 zac_zoned_cap; + u32 zac_zones_optimal_open; + u32 zac_zones_optimal_nonseq; + u32 zac_zones_max_open; + /* error history */ int spdn_cnt; /* ering is CLEAR_END, read comment above CLEAR_END */