ata: Enabling ATA Command Priorities
authorAdam Manzanares <adam.manzanares@hgst.com>
Mon, 17 Oct 2016 18:27:29 +0000 (11:27 -0700)
committerTejun Heo <tj@kernel.org>
Wed, 19 Oct 2016 18:34:36 +0000 (14:34 -0400)
This patch checks to see if an ATA device supports NCQ command priorities.
If so and the user has specified an iocontext that indicates
IO_PRIO_CLASS_RT then we build a tf with a high priority command.

This is done to improve the tail latency of commands that are high
priority by passing priority to the device.

tj: Removed trivial ata_ncq_prio_enabled() and open-coded the test.

Signed-off-by: Adam Manzanares <adam.manzanares@hgst.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
drivers/ata/libata-core.c
drivers/ata/libata-scsi.c
drivers/ata/libata.h
include/linux/ata.h
include/linux/libata.h

index 223a770f78f36557f64d9db66635bd0cae8b4f42..8346faf63337afb45b9817983956ef4bde173c15 100644 (file)
@@ -739,6 +739,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
  *     @n_block: Number of blocks
  *     @tf_flags: RW/FUA etc...
  *     @tag: tag
+ *     @class: IO priority class
  *
  *     LOCKING:
  *     None.
@@ -753,7 +754,7 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
  */
 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)
+                   unsigned int tag, int class)
 {
        tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
        tf->flags |= tf_flags;
@@ -785,6 +786,12 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
                tf->device = ATA_LBA;
                if (tf->flags & ATA_TFLAG_FUA)
                        tf->device |= 1 << 7;
+
+               if (dev->flags & ATA_DFLAG_NCQ_PRIO) {
+                       if (class == IOPRIO_CLASS_RT)
+                               tf->hob_nsect |= ATA_PRIO_HIGH <<
+                                                ATA_SHIFT_PRIO;
+               }
        } else if (dev->flags & ATA_DFLAG_LBA) {
                tf->flags |= ATA_TFLAG_LBA;
 
@@ -2156,6 +2163,30 @@ static void ata_dev_config_ncq_non_data(struct ata_device *dev)
        }
 }
 
+static void ata_dev_config_ncq_prio(struct ata_device *dev)
+{
+       struct ata_port *ap = dev->link->ap;
+       unsigned int err_mask;
+
+       err_mask = ata_read_log_page(dev,
+                                    ATA_LOG_SATA_ID_DEV_DATA,
+                                    ATA_LOG_SATA_SETTINGS,
+                                    ap->sector_buf,
+                                    1);
+       if (err_mask) {
+               ata_dev_dbg(dev,
+                           "failed to get Identify Device data, Emask 0x%x\n",
+                           err_mask);
+               return;
+       }
+
+       if (ap->sector_buf[ATA_LOG_NCQ_PRIO_OFFSET] & BIT(3))
+               dev->flags |= ATA_DFLAG_NCQ_PRIO;
+       else
+               ata_dev_dbg(dev, "SATA page does not support priority\n");
+
+}
+
 static int ata_dev_config_ncq(struct ata_device *dev,
                               char *desc, size_t desc_sz)
 {
@@ -2205,6 +2236,8 @@ static int ata_dev_config_ncq(struct ata_device *dev,
                        ata_dev_config_ncq_send_recv(dev);
                if (ata_id_has_ncq_non_data(dev->id))
                        ata_dev_config_ncq_non_data(dev);
+               if (ata_id_has_ncq_prio(dev->id))
+                       ata_dev_config_ncq_prio(dev);
        }
 
        return 0;
index 9cceb4a875a58caa19fdd809ad82181f22676fc2..2bccc3c7de486c418fad85b5cfc0b714bb8abfa2 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/suspend.h>
 #include <asm/unaligned.h>
+#include <linux/ioprio.h>
 
 #include "libata.h"
 #include "libata-transport.h"
@@ -1755,6 +1756,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
 {
        struct scsi_cmnd *scmd = qc->scsicmd;
        const u8 *cdb = scmd->cmnd;
+       struct request *rq = scmd->request;
+       int class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
        unsigned int tf_flags = 0;
        u64 block;
        u32 n_block;
@@ -1821,7 +1824,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
        qc->nbytes = n_block * scmd->device->sector_size;
 
        rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags,
-                            qc->tag);
+                            qc->tag, class);
+
        if (likely(rc == 0))
                return 0;
 
index 3b301a48007c02e30da91f4dd1676710471f5cbe..8f3a5596dd676b95ee6bf6500d1e8be560b3c73c 100644 (file)
@@ -66,7 +66,7 @@ extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf);
 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);
+                          unsigned int tag, int class);
 extern u64 ata_tf_read_block(const struct ata_taskfile *tf,
                             struct ata_device *dev);
 extern unsigned ata_exec_internal(struct ata_device *dev,
index fdb180367ba134aaa8b54e372c44c8680161fd2f..af6859b3a93d75194eab0817607fd02366a8a61d 100644 (file)
@@ -348,6 +348,7 @@ enum {
        ATA_LOG_DEVSLP_DETO       = 0x01,
        ATA_LOG_DEVSLP_VALID      = 0x07,
        ATA_LOG_DEVSLP_VALID_MASK = 0x80,
+       ATA_LOG_NCQ_PRIO_OFFSET   = 0x09,
 
        /* NCQ send and receive log */
        ATA_LOG_NCQ_SEND_RECV_SUBCMDS_OFFSET    = 0x00,
@@ -940,6 +941,11 @@ 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_ncq_prio(const u16 *id)
+{
+       return id[ATA_ID_SATA_CAPABILITY] & BIT(12);
+}
+
 static inline bool ata_id_has_trim(const u16 *id)
 {
        if (ata_id_major_version(id) >= 7 &&
index 616eef4d81ea38af48cf809ecdbc2a721aff4640..90b69a6293a3499c52d539481b1f429cbc2a9a6a 100644 (file)
@@ -166,6 +166,7 @@ enum {
        ATA_DFLAG_NO_UNLOAD     = (1 << 17), /* device doesn't support unload */
        ATA_DFLAG_UNLOCK_HPA    = (1 << 18), /* unlock HPA */
        ATA_DFLAG_NCQ_SEND_RECV = (1 << 19), /* device supports NCQ SEND and RECV */
+       ATA_DFLAG_NCQ_PRIO      = (1 << 20), /* device supports NCQ priority */
        ATA_DFLAG_INIT_MASK     = (1 << 24) - 1,
 
        ATA_DFLAG_DETACH        = (1 << 24),
@@ -342,7 +343,9 @@ enum {
        ATA_SHIFT_PIO           = 0,
        ATA_SHIFT_MWDMA         = ATA_SHIFT_PIO + ATA_NR_PIO_MODES,
        ATA_SHIFT_UDMA          = ATA_SHIFT_MWDMA + ATA_NR_MWDMA_MODES,
+       ATA_SHIFT_PRIO          = 6,
 
+       ATA_PRIO_HIGH           = 2,
        /* size of buffer to pad xfers ending on unaligned boundaries */
        ATA_DMA_PAD_SZ          = 4,