megaraid_sas: Dual queue depth support
authorSumit Saxena <sumit.saxena@avagotech.com>
Thu, 28 Jan 2016 15:34:30 +0000 (21:04 +0530)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 24 Feb 2016 02:27:02 +0000 (21:27 -0500)
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 <sumit.saxena@avagotech.com>
Signed-off-by: Kashyap Desai <kashyap.desai@avagotech.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/megaraid/megaraid_sas.h
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/megaraid/megaraid_sas_fusion.c

index 3b1ed2d86efebfc4da9fe43ef835b755c0c6a8dc..2a2f49134491ebcb754b99c3e7bbc9a4b5418801 100644 (file)
@@ -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;
index edf8911bdb125b103075ddcf77ad5c91b25e7018..961c024d11ca6d216a8a955698b2c83817cf9414 100644 (file)
@@ -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
         */
index f5538303a9bc484d9afa46c26b64712c0c92390c..6b8547cf9de4151ff535ca26fe77ffe8625b2335 100644 (file)
@@ -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!"