From 1dd0c0e4fd02dc5e5bfaf89bd4656aabe4ae3cb3 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Wed, 12 Apr 2017 14:16:02 -0500 Subject: [PATCH] scsi: cxlflash: Introduce hardware queue steering As an enhancement to distribute requests to multiple hardware queues, add the infrastructure to hash a SCSI command into a particular hardware queue. Support the following scenarios when deriving which queue to use: single queue, tagging when SCSI-MQ enabled, and simple hash via CPU ID when SCSI-MQ is disabled. Rather than altering the existing send API, the derived hardware queue is stored in the AFU command where it can be used for sending a command to the chosen hardware queue. Signed-off-by: Matthew R. Ochs Signed-off-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/common.h | 12 +++- drivers/scsi/cxlflash/main.c | 120 +++++++++++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 8fd7a1fa235e..256af819377d 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -96,6 +96,13 @@ enum cxlflash_state { STATE_FAILTERM /* Failed/terminating state, error out users/threads */ }; +enum cxlflash_hwq_mode { + HWQ_MODE_RR, /* Roundrobin (default) */ + HWQ_MODE_TAG, /* Distribute based on block MQ tag */ + HWQ_MODE_CPU, /* CPU affinity */ + MAX_HWQ_MODE +}; + /* * Each context has its own set of resource handles that is visible * only from that context. @@ -146,9 +153,9 @@ struct afu_cmd { struct scsi_cmnd *scp; struct completion cevent; struct list_head queue; + u32 hwq_index; u8 cmd_tmf:1; - u32 hwq_index; /* As per the SISLITE spec the IOARCB EA has to be 16-byte aligned. * However for performance reasons the IOARCB/IOASA should be @@ -213,8 +220,11 @@ struct afu { atomic_t cmds_active; /* Number of currently active AFU commands */ u64 hb; u32 internal_lun; /* User-desired LUN mode for this AFU */ + u32 num_hwqs; /* Number of hardware queues */ u32 desired_hwqs; /* Desired h/w queues, effective on AFU reset */ + enum cxlflash_hwq_mode hwq_mode; /* Steering mode for h/w queues */ + u32 hwq_rr_count; /* Count to distribute traffic for roundrobin */ char version[16]; u64 interface_version; diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 113797aafd7e..a7d57c343492 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -357,6 +357,43 @@ static int wait_resp(struct afu *afu, struct afu_cmd *cmd) return rc; } +/** + * cmd_to_target_hwq() - selects a target hardware queue for a SCSI command + * @host: SCSI host associated with device. + * @scp: SCSI command to send. + * @afu: SCSI command to send. + * + * Hashes a command based upon the hardware queue mode. + * + * Return: Trusted index of target hardware queue + */ +static u32 cmd_to_target_hwq(struct Scsi_Host *host, struct scsi_cmnd *scp, + struct afu *afu) +{ + u32 tag; + u32 hwq = 0; + + if (afu->num_hwqs == 1) + return 0; + + switch (afu->hwq_mode) { + case HWQ_MODE_RR: + hwq = afu->hwq_rr_count++ % afu->num_hwqs; + break; + case HWQ_MODE_TAG: + tag = blk_mq_unique_tag(scp->request); + hwq = blk_mq_unique_tag_to_hwq(tag); + break; + case HWQ_MODE_CPU: + hwq = smp_processor_id() % afu->num_hwqs; + break; + default: + WARN_ON_ONCE(1); + } + + return hwq; +} + /** * send_tmf() - sends a Task Management Function (TMF) * @afu: AFU to checkout from. @@ -368,10 +405,12 @@ static int wait_resp(struct afu *afu, struct afu_cmd *cmd) */ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) { - struct cxlflash_cfg *cfg = shost_priv(scp->device->host); + struct Scsi_Host *host = scp->device->host; + struct cxlflash_cfg *cfg = shost_priv(host); struct afu_cmd *cmd = sc_to_afucz(scp); struct device *dev = &cfg->dev->dev; - struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ); + int hwq_index = cmd_to_target_hwq(host, scp, afu); + struct hwq *hwq = get_hwq(afu, hwq_index); ulong lock_flags; int rc = 0; ulong to; @@ -388,7 +427,7 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) cmd->scp = scp; cmd->parent = afu; cmd->cmd_tmf = true; - cmd->hwq_index = hwq->index; + cmd->hwq_index = hwq_index; cmd->rcb.ctx_id = hwq->ctx_hndl; cmd->rcb.msi = SISL_MSI_RRQ_UPDATED; @@ -448,7 +487,8 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) struct device *dev = &cfg->dev->dev; struct afu_cmd *cmd = sc_to_afucz(scp); struct scatterlist *sg = scsi_sglist(scp); - struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ); + int hwq_index = cmd_to_target_hwq(host, scp, afu); + struct hwq *hwq = get_hwq(afu, hwq_index); u16 req_flags = SISL_REQ_FLAGS_SUP_UNDERRUN; ulong lock_flags; int rc = 0; @@ -498,7 +538,7 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) cmd->scp = scp; cmd->parent = afu; - cmd->hwq_index = hwq->index; + cmd->hwq_index = hwq_index; cmd->rcb.ctx_id = hwq->ctx_hndl; cmd->rcb.msi = SISL_MSI_RRQ_UPDATED; @@ -2654,6 +2694,74 @@ retry: return count; } +static const char *hwq_mode_name[MAX_HWQ_MODE] = { "rr", "tag", "cpu" }; + +/** + * hwq_mode_show() - presents the HWQ steering mode for the host + * @dev: Generic device associated with the host. + * @attr: Device attribute representing the HWQ steering mode. + * @buf: Buffer of length PAGE_SIZE to report back the HWQ steering mode + * as a character string. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t hwq_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cxlflash_cfg *cfg = shost_priv(class_to_shost(dev)); + struct afu *afu = cfg->afu; + + return scnprintf(buf, PAGE_SIZE, "%s\n", hwq_mode_name[afu->hwq_mode]); +} + +/** + * hwq_mode_store() - sets the HWQ steering mode for the host + * @dev: Generic device associated with the host. + * @attr: Device attribute representing the HWQ steering mode. + * @buf: Buffer of length PAGE_SIZE containing the HWQ steering mode + * as a character string. + * @count: Length of data resizing in @buf. + * + * rr = Round-Robin + * tag = Block MQ Tagging + * cpu = CPU Affinity + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t hwq_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = shost_priv(shost); + struct device *cfgdev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + int i; + u32 mode = MAX_HWQ_MODE; + + for (i = 0; i < MAX_HWQ_MODE; i++) { + if (!strncmp(hwq_mode_name[i], buf, strlen(hwq_mode_name[i]))) { + mode = i; + break; + } + } + + if (mode >= MAX_HWQ_MODE) { + dev_info(cfgdev, "Invalid HWQ steering mode.\n"); + return -EINVAL; + } + + if ((mode == HWQ_MODE_TAG) && !shost_use_blk_mq(shost)) { + dev_info(cfgdev, "SCSI-MQ is not enabled, use a different " + "HWQ steering mode.\n"); + return -EINVAL; + } + + afu->hwq_mode = mode; + + return count; +} + /** * mode_show() - presents the current mode of the device * @dev: Generic device associated with the device. @@ -2686,6 +2794,7 @@ static DEVICE_ATTR_RO(port2_lun_table); static DEVICE_ATTR_RO(port3_lun_table); static DEVICE_ATTR_RW(irqpoll_weight); static DEVICE_ATTR_RW(num_hwqs); +static DEVICE_ATTR_RW(hwq_mode); static struct device_attribute *cxlflash_host_attrs[] = { &dev_attr_port0, @@ -2700,6 +2809,7 @@ static struct device_attribute *cxlflash_host_attrs[] = { &dev_attr_port3_lun_table, &dev_attr_irqpoll_weight, &dev_attr_num_hwqs, + &dev_attr_hwq_mode, NULL };