From: Sumit Saxena Date: Thu, 28 Jan 2016 15:34:30 +0000 (+0530) Subject: megaraid_sas: Dual queue depth support X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=308ec459bc1975d9856cfeb3d1cd6461794a3976;p=linux-beck.git megaraid_sas: Dual queue depth support 1. For iMR controllers, firmware will report two queue depths: - Controller-wide queue depth - LDIO queue depth (240) Controller-wide queue depth will be greater among the two. Using this new feature, iMR can provide larger Queue depth(QD) for JBOD and limited QD for Virtual Disk(VD). 2. megaraid_sas driver will throttle read/write LDIOs based on "LDIO Queue Depth". 3. Dual queue depth can be enabled/disabled via module parameter. It is enabled by default if the firmware supports it. Only specific firmware builds will enable the feature. 4. Added sysfs parameter "ldio_outstanding" which permits querying the number of outstanding LDIO requests at runtime. Signed-off-by: Sumit Saxena Signed-off-by: Kashyap Desai Signed-off-by: Martin K. Petersen --- diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 3b1ed2d86efe..2a2f49134491 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1353,6 +1353,12 @@ enum DCMD_TIMEOUT_ACTION { KILL_ADAPTER = 1, IGNORE_TIMEOUT = 2, }; + +enum FW_BOOT_CONTEXT { + PROBE_CONTEXT = 0, + OCR_CONTEXT = 1, +}; + /* Frame Type */ #define IO_FRAME 0 #define PTHRU_FRAME 1 @@ -2038,6 +2044,8 @@ struct megasas_instance { u16 max_fw_cmds; u16 max_mfi_cmds; u16 max_scsi_cmds; + u16 ldio_threshold; + u16 cur_can_queue; u32 max_sectors_per_req; struct megasas_aen_event *ev; @@ -2068,6 +2076,7 @@ struct megasas_instance { u32 fw_support_ieee; atomic_t fw_outstanding; + atomic_t ldio_outstanding; atomic_t fw_reset_no_pci_access; struct megasas_instance_template *instancet; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index edf8911bdb12..961c024d11ca 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -96,6 +96,10 @@ int rdpq_enable = 1; module_param(rdpq_enable, int, S_IRUGO); MODULE_PARM_DESC(rdpq_enable, " Allocate reply queue in chunks for large queue depth enable/disable Default: disable(0)"); +unsigned int dual_qdepth_disable; +module_param(dual_qdepth_disable, int, S_IRUGO); +MODULE_PARM_DESC(dual_qdepth_disable, "Disable dual queue depth feature. Default: 0"); + MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux.pdl@avagotech.com"); @@ -1976,7 +1980,7 @@ megasas_check_and_restore_queue_depth(struct megasas_instance *instance) spin_lock_irqsave(instance->host->host_lock, flags); instance->flag &= ~MEGASAS_FW_BUSY; - instance->host->can_queue = instance->max_scsi_cmds; + instance->host->can_queue = instance->cur_can_queue; spin_unlock_irqrestore(instance->host->host_lock, flags); } } @@ -2941,6 +2945,16 @@ megasas_page_size_show(struct device *cdev, return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)PAGE_SIZE - 1); } +static ssize_t +megasas_ldio_outstanding_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; + + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->ldio_outstanding)); +} + static DEVICE_ATTR(fw_crash_buffer, S_IRUGO | S_IWUSR, megasas_fw_crash_buffer_show, megasas_fw_crash_buffer_store); static DEVICE_ATTR(fw_crash_buffer_size, S_IRUGO, @@ -2949,12 +2963,15 @@ static DEVICE_ATTR(fw_crash_state, S_IRUGO | S_IWUSR, megasas_fw_crash_state_show, megasas_fw_crash_state_store); static DEVICE_ATTR(page_size, S_IRUGO, megasas_page_size_show, NULL); +static DEVICE_ATTR(ldio_outstanding, S_IRUGO, + megasas_ldio_outstanding_show, NULL); struct device_attribute *megaraid_host_attrs[] = { &dev_attr_fw_crash_buffer_size, &dev_attr_fw_crash_buffer, &dev_attr_fw_crash_state, &dev_attr_page_size, + &dev_attr_ldio_outstanding, NULL, }; @@ -4749,6 +4766,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS)); } + instance->cur_can_queue = instance->max_scsi_cmds; /* * Create a pool of commands */ diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index f5538303a9bc..6b8547cf9de4 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -92,6 +92,7 @@ void megasas_start_timer(struct megasas_instance *instance, void *fn, unsigned long interval); extern struct megasas_mgmt_info megasas_mgmt_info; extern int resetwaittime; +extern unsigned int dual_qdepth_disable; static void megasas_free_rdpq_fusion(struct megasas_instance *instance); static void megasas_free_reply_fusion(struct megasas_instance *instance); @@ -207,6 +208,67 @@ megasas_fire_cmd_fusion(struct megasas_instance *instance, #endif } +/** + * megasas_fusion_update_can_queue - Do all Adapter Queue depth related calculations here + * @instance: Adapter soft state + * fw_boot_context: Whether this function called during probe or after OCR + * + * This function is only for fusion controllers. + * Update host can queue, if firmware downgrade max supported firmware commands. + * Firmware upgrade case will be skiped because underlying firmware has + * more resource than exposed to the OS. + * + */ +static void +megasas_fusion_update_can_queue(struct megasas_instance *instance, int fw_boot_context) +{ + u16 cur_max_fw_cmds = 0; + u16 ldio_threshold = 0; + struct megasas_register_set __iomem *reg_set; + + reg_set = instance->reg_set; + + cur_max_fw_cmds = readl(&instance->reg_set->outbound_scratch_pad_3) & 0x00FFFF; + + if (dual_qdepth_disable || !cur_max_fw_cmds) + cur_max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; + else + ldio_threshold = + (instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF) - MEGASAS_FUSION_IOCTL_CMDS; + + dev_info(&instance->pdev->dev, + "Current firmware maximum commands: %d\t LDIO threshold: %d\n", + cur_max_fw_cmds, ldio_threshold); + + if (fw_boot_context == OCR_CONTEXT) { + cur_max_fw_cmds = cur_max_fw_cmds - 1; + if (cur_max_fw_cmds <= instance->max_fw_cmds) { + instance->cur_can_queue = + cur_max_fw_cmds - (MEGASAS_FUSION_INTERNAL_CMDS + + MEGASAS_FUSION_IOCTL_CMDS); + instance->host->can_queue = instance->cur_can_queue; + instance->ldio_threshold = ldio_threshold; + } + } else { + instance->max_fw_cmds = cur_max_fw_cmds; + instance->ldio_threshold = ldio_threshold; + + if (!instance->is_rdpq) + instance->max_fw_cmds = min_t(u16, instance->max_fw_cmds, 1024); + + /* + * Reduce the max supported cmds by 1. This is to ensure that the + * reply_q_sz (1 more than the max cmd that driver may send) + * does not exceed max cmds that the FW can support + */ + instance->max_fw_cmds = instance->max_fw_cmds-1; + + instance->max_scsi_cmds = instance->max_fw_cmds - + (MEGASAS_FUSION_INTERNAL_CMDS + + MEGASAS_FUSION_IOCTL_CMDS); + instance->cur_can_queue = instance->max_scsi_cmds; + } +} /** * megasas_free_cmds_fusion - Free all the cmds in the free cmd pool * @instance: Adapter soft state @@ -738,6 +800,8 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) drv_ops->mfi_capabilities.support_ext_io_size = 1; drv_ops->mfi_capabilities.support_fp_rlbypass = 1; + if (!dual_qdepth_disable) + drv_ops->mfi_capabilities.support_ext_queue_depth = 1; /* Convert capability to LE32 */ cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities); @@ -1153,15 +1217,7 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) reg_set = instance->reg_set; - /* - * Get various operational parameters from status register - */ - instance->max_fw_cmds = - instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; - dev_info(&instance->pdev->dev, - "firmware support max fw cmd\t: (%d)\n", instance->max_fw_cmds); - if (!instance->is_rdpq) - instance->max_fw_cmds = min_t(u16, instance->max_fw_cmds, 1024); + megasas_fusion_update_can_queue(instance, PROBE_CONTEXT); /* * Reduce the max supported cmds by 1. This is to ensure that the @@ -2119,6 +2175,14 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, fusion = instance->ctrl_context; + if ((megasas_cmd_type(scmd) == READ_WRITE_LDIO) && + instance->ldio_threshold && + (atomic_inc_return(&instance->ldio_outstanding) > + instance->ldio_threshold)) { + atomic_dec(&instance->ldio_outstanding); + return SCSI_MLQUEUE_DEVICE_BUSY; + } + cmd = megasas_get_cmd_fusion(instance, scmd->request->tag); index = cmd->index; @@ -2249,6 +2313,8 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) map_cmd_status(cmd_fusion, status, extStatus); scsi_io_req->RaidContext.status = 0; scsi_io_req->RaidContext.exStatus = 0; + if (megasas_cmd_type(scmd_local) == READ_WRITE_LDIO) + atomic_dec(&instance->ldio_outstanding); megasas_return_cmd_fusion(instance, cmd_fusion); scsi_dma_unmap(scmd_local); scmd_local->scsi_done(scmd_local); @@ -3367,6 +3433,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) scmd_local->result = megasas_check_mpio_paths(instance, scmd_local); + if (megasas_cmd_type(scmd_local) == READ_WRITE_LDIO) + atomic_dec(&instance->ldio_outstanding); megasas_return_cmd_fusion(instance, cmd_fusion); scsi_dma_unmap(scmd_local); scmd_local->scsi_done(scmd_local); @@ -3459,6 +3527,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int reason) } megasas_reset_reply_desc(instance); + megasas_fusion_update_can_queue(instance, OCR_CONTEXT); + if (megasas_ioc_init_fusion(instance)) { dev_warn(&instance->pdev->dev, "megasas_ioc_init_fusion() failed!"