summaryrefslogtreecommitdiff
path: root/drivers/ata/libata-sata.c
diff options
context:
space:
mode:
authorNiklas Cassel <niklas.cassel@wdc.com>2023-05-11 04:13:52 +0300
committerMartin K. Petersen <martin.petersen@oracle.com>2023-05-23 00:05:20 +0300
commit18bd7718b5c489b3161b6c2ab4685d57c1e2da3b (patch)
treee610d9db43c4dd75e14f72d12ff5262fbef323df /drivers/ata/libata-sata.c
parenteafe804bda7ba01da562c43351068b8a76a579af (diff)
downloadlinux-18bd7718b5c489b3161b6c2ab4685d57c1e2da3b.tar.xz
scsi: ata: libata: Handle completion of CDL commands using policy 0xD
A CDL timeout for policy 0xF is defined as a NCQ error, just with a CDL specific sk/asc/ascq in the sense data. Therefore, the existing code in libata does not need to be modified to handle a policy 0xF CDL timeout. For Command Duration Limits policy 0xD: The device shall complete the command without error with the additional sense code set to DATA CURRENTLY UNAVAILABLE. Since a CDL timeout for policy 0xD is not an error, we cannot use the NCQ Command Error log (10h). Instead, we need to read the Sense Data for Successful NCQ Commands log (0Fh). In the success case, just like in the error case, we cannot simply read a log page from the interrupt handler itself, since reading a log page involves sending a READ LOG DMA EXT or READ LOG EXT command. Therefore, we add a new EH action ATA_EH_GET_SUCCESS_SENSE. When a command completes without error, and when the ATA_SENSE bit is set, this new action is set as pending, and EH is scheduled. This way, similar to the NCQ error case, the log page will be read from EH context. An alternative would have been to add a new kthread or workqueue to handle this. However, extending EH can be done with minimal changes and avoids the need to synchronize a new kthread/workqueue with EH. Co-developed-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com> Link: https://lore.kernel.org/r/20230511011356.227789-20-nks@flawful.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/ata/libata-sata.c')
-rw-r--r--drivers/ata/libata-sata.c92
1 files changed, 92 insertions, 0 deletions
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c
index 57cb33060c9d..7de4d8901fac 100644
--- a/drivers/ata/libata-sata.c
+++ b/drivers/ata/libata-sata.c
@@ -11,7 +11,9 @@
#include <linux/module.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <linux/libata.h>
+#include <asm/unaligned.h>
#include "libata.h"
#include "libata-transport.h"
@@ -1409,6 +1411,95 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
}
/**
+ * ata_eh_read_sense_success_ncq_log - Read the sense data for successful
+ * NCQ commands log
+ * @link: ATA link to get sense data for
+ *
+ * Read the sense data for successful NCQ commands log page to obtain
+ * sense data for all NCQ commands that completed successfully with
+ * the sense data available bit set.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -errno otherwise.
+ */
+int ata_eh_read_sense_success_ncq_log(struct ata_link *link)
+{
+ struct ata_device *dev = link->device;
+ struct ata_port *ap = dev->link->ap;
+ u8 *buf = ap->ncq_sense_buf;
+ struct ata_queued_cmd *qc;
+ unsigned int err_mask, tag;
+ u8 *sense, sk = 0, asc = 0, ascq = 0;
+ u64 sense_valid, val;
+ int ret = 0;
+
+ err_mask = ata_read_log_page(dev, ATA_LOG_SENSE_NCQ, 0, buf, 2);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Failed to read Sense Data for Successful NCQ Commands log\n");
+ return -EIO;
+ }
+
+ /* Check the log header */
+ val = get_unaligned_le64(&buf[0]);
+ if ((val & 0xffff) != 1 || ((val >> 16) & 0xff) != 0x0f) {
+ ata_dev_err(dev,
+ "Invalid Sense Data for Successful NCQ Commands log\n");
+ return -EIO;
+ }
+
+ sense_valid = (u64)buf[8] | ((u64)buf[9] << 8) |
+ ((u64)buf[10] << 16) | ((u64)buf[11] << 24);
+
+ ata_qc_for_each_raw(ap, qc, tag) {
+ if (!(qc->flags & ATA_QCFLAG_EH) ||
+ !(qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD) ||
+ qc->err_mask ||
+ ata_dev_phys_link(qc->dev) != link)
+ continue;
+
+ /*
+ * If the command does not have any sense data, clear ATA_SENSE.
+ * Keep ATA_QCFLAG_EH_SUCCESS_CMD so that command is finished.
+ */
+ if (!(sense_valid & (1ULL << tag))) {
+ qc->result_tf.status &= ~ATA_SENSE;
+ continue;
+ }
+
+ sense = &buf[32 + 24 * tag];
+ sk = sense[0];
+ asc = sense[1];
+ ascq = sense[2];
+
+ if (!ata_scsi_sense_is_valid(sk, asc, ascq)) {
+ ret = -EIO;
+ continue;
+ }
+
+ /* Set sense without also setting scsicmd->result */
+ scsi_build_sense_buffer(dev->flags & ATA_DFLAG_D_SENSE,
+ qc->scsicmd->sense_buffer, sk,
+ asc, ascq);
+ qc->flags |= ATA_QCFLAG_SENSE_VALID;
+
+ /*
+ * If we have sense data, call scsi_check_sense() in order to
+ * set the correct SCSI ML byte (if any). No point in checking
+ * the return value, since the command has already completed
+ * successfully.
+ */
+ scsi_check_sense(qc->scsicmd);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ata_eh_read_sense_success_ncq_log);
+
+/**
* ata_eh_analyze_ncq_error - analyze NCQ error
* @link: ATA link to analyze NCQ error for
*
@@ -1488,6 +1579,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
ata_qc_for_each_raw(ap, qc, tag) {
if (!(qc->flags & ATA_QCFLAG_EH) ||
+ qc->flags & ATA_QCFLAG_EH_SUCCESS_CMD ||
ata_dev_phys_link(qc->dev) != link)
continue;