mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 10:01:43 +00:00
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 <mrochs@linux.vnet.ibm.com> Signed-off-by: Uma Krishnan <ukrishn@linux.vnet.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
3065267a80
commit
1dd0c0e4fd
@ -96,6 +96,13 @@ enum cxlflash_state {
|
|||||||
STATE_FAILTERM /* Failed/terminating state, error out users/threads */
|
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
|
* Each context has its own set of resource handles that is visible
|
||||||
* only from that context.
|
* only from that context.
|
||||||
@ -146,9 +153,9 @@ struct afu_cmd {
|
|||||||
struct scsi_cmnd *scp;
|
struct scsi_cmnd *scp;
|
||||||
struct completion cevent;
|
struct completion cevent;
|
||||||
struct list_head queue;
|
struct list_head queue;
|
||||||
|
u32 hwq_index;
|
||||||
|
|
||||||
u8 cmd_tmf:1;
|
u8 cmd_tmf:1;
|
||||||
u32 hwq_index;
|
|
||||||
|
|
||||||
/* As per the SISLITE spec the IOARCB EA has to be 16-byte aligned.
|
/* As per the SISLITE spec the IOARCB EA has to be 16-byte aligned.
|
||||||
* However for performance reasons the IOARCB/IOASA should be
|
* 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 */
|
atomic_t cmds_active; /* Number of currently active AFU commands */
|
||||||
u64 hb;
|
u64 hb;
|
||||||
u32 internal_lun; /* User-desired LUN mode for this AFU */
|
u32 internal_lun; /* User-desired LUN mode for this AFU */
|
||||||
|
|
||||||
u32 num_hwqs; /* Number of hardware queues */
|
u32 num_hwqs; /* Number of hardware queues */
|
||||||
u32 desired_hwqs; /* Desired h/w queues, effective on AFU reset */
|
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];
|
char version[16];
|
||||||
u64 interface_version;
|
u64 interface_version;
|
||||||
|
@ -357,6 +357,43 @@ static int wait_resp(struct afu *afu, struct afu_cmd *cmd)
|
|||||||
return rc;
|
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)
|
* send_tmf() - sends a Task Management Function (TMF)
|
||||||
* @afu: AFU to checkout from.
|
* @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)
|
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 afu_cmd *cmd = sc_to_afucz(scp);
|
||||||
struct device *dev = &cfg->dev->dev;
|
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;
|
ulong lock_flags;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
ulong to;
|
ulong to;
|
||||||
@ -388,7 +427,7 @@ static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd)
|
|||||||
cmd->scp = scp;
|
cmd->scp = scp;
|
||||||
cmd->parent = afu;
|
cmd->parent = afu;
|
||||||
cmd->cmd_tmf = true;
|
cmd->cmd_tmf = true;
|
||||||
cmd->hwq_index = hwq->index;
|
cmd->hwq_index = hwq_index;
|
||||||
|
|
||||||
cmd->rcb.ctx_id = hwq->ctx_hndl;
|
cmd->rcb.ctx_id = hwq->ctx_hndl;
|
||||||
cmd->rcb.msi = SISL_MSI_RRQ_UPDATED;
|
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 device *dev = &cfg->dev->dev;
|
||||||
struct afu_cmd *cmd = sc_to_afucz(scp);
|
struct afu_cmd *cmd = sc_to_afucz(scp);
|
||||||
struct scatterlist *sg = scsi_sglist(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;
|
u16 req_flags = SISL_REQ_FLAGS_SUP_UNDERRUN;
|
||||||
ulong lock_flags;
|
ulong lock_flags;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
@ -498,7 +538,7 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
|
|||||||
|
|
||||||
cmd->scp = scp;
|
cmd->scp = scp;
|
||||||
cmd->parent = afu;
|
cmd->parent = afu;
|
||||||
cmd->hwq_index = hwq->index;
|
cmd->hwq_index = hwq_index;
|
||||||
|
|
||||||
cmd->rcb.ctx_id = hwq->ctx_hndl;
|
cmd->rcb.ctx_id = hwq->ctx_hndl;
|
||||||
cmd->rcb.msi = SISL_MSI_RRQ_UPDATED;
|
cmd->rcb.msi = SISL_MSI_RRQ_UPDATED;
|
||||||
@ -2654,6 +2694,74 @@ retry:
|
|||||||
return count;
|
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
|
* mode_show() - presents the current mode of the device
|
||||||
* @dev: Generic device associated with 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_RO(port3_lun_table);
|
||||||
static DEVICE_ATTR_RW(irqpoll_weight);
|
static DEVICE_ATTR_RW(irqpoll_weight);
|
||||||
static DEVICE_ATTR_RW(num_hwqs);
|
static DEVICE_ATTR_RW(num_hwqs);
|
||||||
|
static DEVICE_ATTR_RW(hwq_mode);
|
||||||
|
|
||||||
static struct device_attribute *cxlflash_host_attrs[] = {
|
static struct device_attribute *cxlflash_host_attrs[] = {
|
||||||
&dev_attr_port0,
|
&dev_attr_port0,
|
||||||
@ -2700,6 +2809,7 @@ static struct device_attribute *cxlflash_host_attrs[] = {
|
|||||||
&dev_attr_port3_lun_table,
|
&dev_attr_port3_lun_table,
|
||||||
&dev_attr_irqpoll_weight,
|
&dev_attr_irqpoll_weight,
|
||||||
&dev_attr_num_hwqs,
|
&dev_attr_num_hwqs,
|
||||||
|
&dev_attr_hwq_mode,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user