|
@@ -47,6 +47,14 @@
|
|
#include <scsi/scsi_host.h>
|
|
#include <scsi/scsi_host.h>
|
|
#include "megaraid_sas.h"
|
|
#include "megaraid_sas.h"
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * poll_mode_io:1- schedule complete completion from q cmd
|
|
|
|
+ */
|
|
|
|
+static unsigned int poll_mode_io;
|
|
|
|
+module_param_named(poll_mode_io, poll_mode_io, int, 0);
|
|
|
|
+MODULE_PARM_DESC(poll_mode_io,
|
|
|
|
+ "Complete cmds from IO path, (default=0)");
|
|
|
|
+
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(MEGASAS_VERSION);
|
|
MODULE_VERSION(MEGASAS_VERSION);
|
|
MODULE_AUTHOR("megaraidlinux@lsi.com");
|
|
MODULE_AUTHOR("megaraidlinux@lsi.com");
|
|
@@ -860,6 +868,12 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
|
|
atomic_inc(&instance->fw_outstanding);
|
|
atomic_inc(&instance->fw_outstanding);
|
|
|
|
|
|
instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
|
|
instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
|
|
|
|
+ /*
|
|
|
|
+ * Check if we have pend cmds to be completed
|
|
|
|
+ */
|
|
|
|
+ if (poll_mode_io && atomic_read(&instance->fw_outstanding))
|
|
|
|
+ tasklet_schedule(&instance->isr_tasklet);
|
|
|
|
+
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -1891,6 +1905,47 @@ fail_fw_init:
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * megasas_start_timer - Initializes a timer object
|
|
|
|
+ * @instance: Adapter soft state
|
|
|
|
+ * @timer: timer object to be initialized
|
|
|
|
+ * @fn: timer function
|
|
|
|
+ * @interval: time interval between timer function call
|
|
|
|
+ */
|
|
|
|
+static inline void
|
|
|
|
+megasas_start_timer(struct megasas_instance *instance,
|
|
|
|
+ struct timer_list *timer,
|
|
|
|
+ void *fn, unsigned long interval)
|
|
|
|
+{
|
|
|
|
+ init_timer(timer);
|
|
|
|
+ timer->expires = jiffies + interval;
|
|
|
|
+ timer->data = (unsigned long)instance;
|
|
|
|
+ timer->function = fn;
|
|
|
|
+ add_timer(timer);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * megasas_io_completion_timer - Timer fn
|
|
|
|
+ * @instance_addr: Address of adapter soft state
|
|
|
|
+ *
|
|
|
|
+ * Schedules tasklet for cmd completion
|
|
|
|
+ * if poll_mode_io is set
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+megasas_io_completion_timer(unsigned long instance_addr)
|
|
|
|
+{
|
|
|
|
+ struct megasas_instance *instance =
|
|
|
|
+ (struct megasas_instance *)instance_addr;
|
|
|
|
+
|
|
|
|
+ if (atomic_read(&instance->fw_outstanding))
|
|
|
|
+ tasklet_schedule(&instance->isr_tasklet);
|
|
|
|
+
|
|
|
|
+ /* Restart timer */
|
|
|
|
+ if (poll_mode_io)
|
|
|
|
+ mod_timer(&instance->io_completion_timer,
|
|
|
|
+ jiffies + MEGASAS_COMPLETION_TIMER_INTERVAL);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* megasas_init_mfi - Initializes the FW
|
|
* megasas_init_mfi - Initializes the FW
|
|
* @instance: Adapter soft state
|
|
* @instance: Adapter soft state
|
|
@@ -2017,8 +2072,14 @@ static int megasas_init_mfi(struct megasas_instance *instance)
|
|
* Setup tasklet for cmd completion
|
|
* Setup tasklet for cmd completion
|
|
*/
|
|
*/
|
|
|
|
|
|
- tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
|
|
|
|
- (unsigned long)instance);
|
|
|
|
|
|
+ tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,
|
|
|
|
+ (unsigned long)instance);
|
|
|
|
+
|
|
|
|
+ /* Initialize the cmd completion timer */
|
|
|
|
+ if (poll_mode_io)
|
|
|
|
+ megasas_start_timer(instance, &instance->io_completion_timer,
|
|
|
|
+ megasas_io_completion_timer,
|
|
|
|
+ MEGASAS_COMPLETION_TIMER_INTERVAL);
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
fail_fw_init:
|
|
fail_fw_init:
|
|
@@ -2578,8 +2639,8 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * megasas_suspend - driver suspend entry point
|
|
|
|
- * @pdev: PCI device structure
|
|
|
|
|
|
+ * megasas_suspend - driver suspend entry point
|
|
|
|
+ * @pdev: PCI device structure
|
|
* @state: PCI power state to suspend routine
|
|
* @state: PCI power state to suspend routine
|
|
*/
|
|
*/
|
|
static int __devinit
|
|
static int __devinit
|
|
@@ -2591,6 +2652,9 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
instance = pci_get_drvdata(pdev);
|
|
instance = pci_get_drvdata(pdev);
|
|
host = instance->host;
|
|
host = instance->host;
|
|
|
|
|
|
|
|
+ if (poll_mode_io)
|
|
|
|
+ del_timer_sync(&instance->io_completion_timer);
|
|
|
|
+
|
|
megasas_flush_cache(instance);
|
|
megasas_flush_cache(instance);
|
|
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
|
|
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
|
|
tasklet_kill(&instance->isr_tasklet);
|
|
tasklet_kill(&instance->isr_tasklet);
|
|
@@ -2677,6 +2741,11 @@ megasas_resume(struct pci_dev *pdev)
|
|
if (megasas_start_aen(instance))
|
|
if (megasas_start_aen(instance))
|
|
printk(KERN_ERR "megasas: Start AEN failed\n");
|
|
printk(KERN_ERR "megasas: Start AEN failed\n");
|
|
|
|
|
|
|
|
+ /* Initialize the cmd completion timer */
|
|
|
|
+ if (poll_mode_io)
|
|
|
|
+ megasas_start_timer(instance, &instance->io_completion_timer,
|
|
|
|
+ megasas_io_completion_timer,
|
|
|
|
+ MEGASAS_COMPLETION_TIMER_INTERVAL);
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
fail_irq:
|
|
fail_irq:
|
|
@@ -2715,6 +2784,9 @@ static void megasas_detach_one(struct pci_dev *pdev)
|
|
instance = pci_get_drvdata(pdev);
|
|
instance = pci_get_drvdata(pdev);
|
|
host = instance->host;
|
|
host = instance->host;
|
|
|
|
|
|
|
|
+ if (poll_mode_io)
|
|
|
|
+ del_timer_sync(&instance->io_completion_timer);
|
|
|
|
+
|
|
scsi_remove_host(instance->host);
|
|
scsi_remove_host(instance->host);
|
|
megasas_flush_cache(instance);
|
|
megasas_flush_cache(instance);
|
|
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
|
|
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
|
|
@@ -3188,7 +3260,7 @@ static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
|
|
static ssize_t
|
|
static ssize_t
|
|
megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
|
|
megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
|
|
{
|
|
{
|
|
- return sprintf(buf,"%u",megasas_dbg_lvl);
|
|
|
|
|
|
+ return sprintf(buf, "%u\n", megasas_dbg_lvl);
|
|
}
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
static ssize_t
|
|
@@ -3203,7 +3275,65 @@ megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t coun
|
|
}
|
|
}
|
|
|
|
|
|
static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUGO, megasas_sysfs_show_dbg_lvl,
|
|
static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUGO, megasas_sysfs_show_dbg_lvl,
|
|
- megasas_sysfs_set_dbg_lvl);
|
|
|
|
|
|
+ megasas_sysfs_set_dbg_lvl);
|
|
|
|
+
|
|
|
|
+static ssize_t
|
|
|
|
+megasas_sysfs_show_poll_mode_io(struct device_driver *dd, char *buf)
|
|
|
|
+{
|
|
|
|
+ return sprintf(buf, "%u\n", poll_mode_io);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ssize_t
|
|
|
|
+megasas_sysfs_set_poll_mode_io(struct device_driver *dd,
|
|
|
|
+ const char *buf, size_t count)
|
|
|
|
+{
|
|
|
|
+ int retval = count;
|
|
|
|
+ int tmp = poll_mode_io;
|
|
|
|
+ int i;
|
|
|
|
+ struct megasas_instance *instance;
|
|
|
|
+
|
|
|
|
+ if (sscanf(buf, "%u", &poll_mode_io) < 1) {
|
|
|
|
+ printk(KERN_ERR "megasas: could not set poll_mode_io\n");
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check if poll_mode_io is already set or is same as previous value
|
|
|
|
+ */
|
|
|
|
+ if ((tmp && poll_mode_io) || (tmp == poll_mode_io))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if (poll_mode_io) {
|
|
|
|
+ /*
|
|
|
|
+ * Start timers for all adapters
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
|
|
|
|
+ instance = megasas_mgmt_info.instance[i];
|
|
|
|
+ if (instance) {
|
|
|
|
+ megasas_start_timer(instance,
|
|
|
|
+ &instance->io_completion_timer,
|
|
|
|
+ megasas_io_completion_timer,
|
|
|
|
+ MEGASAS_COMPLETION_TIMER_INTERVAL);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * Delete timers for all adapters
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
|
|
|
|
+ instance = megasas_mgmt_info.instance[i];
|
|
|
|
+ if (instance)
|
|
|
|
+ del_timer_sync(&instance->io_completion_timer);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO,
|
|
|
|
+ megasas_sysfs_show_poll_mode_io,
|
|
|
|
+ megasas_sysfs_set_poll_mode_io);
|
|
|
|
|
|
/**
|
|
/**
|
|
* megasas_init - Driver load entry point
|
|
* megasas_init - Driver load entry point
|
|
@@ -3254,8 +3384,16 @@ static int __init megasas_init(void)
|
|
&driver_attr_dbg_lvl);
|
|
&driver_attr_dbg_lvl);
|
|
if (rval)
|
|
if (rval)
|
|
goto err_dcf_dbg_lvl;
|
|
goto err_dcf_dbg_lvl;
|
|
|
|
+ rval = driver_create_file(&megasas_pci_driver.driver,
|
|
|
|
+ &driver_attr_poll_mode_io);
|
|
|
|
+ if (rval)
|
|
|
|
+ goto err_dcf_poll_mode_io;
|
|
|
|
|
|
return rval;
|
|
return rval;
|
|
|
|
+
|
|
|
|
+err_dcf_poll_mode_io:
|
|
|
|
+ driver_remove_file(&megasas_pci_driver.driver,
|
|
|
|
+ &driver_attr_dbg_lvl);
|
|
err_dcf_dbg_lvl:
|
|
err_dcf_dbg_lvl:
|
|
driver_remove_file(&megasas_pci_driver.driver,
|
|
driver_remove_file(&megasas_pci_driver.driver,
|
|
&driver_attr_release_date);
|
|
&driver_attr_release_date);
|
|
@@ -3273,6 +3411,8 @@ err_pcidrv:
|
|
*/
|
|
*/
|
|
static void __exit megasas_exit(void)
|
|
static void __exit megasas_exit(void)
|
|
{
|
|
{
|
|
|
|
+ driver_remove_file(&megasas_pci_driver.driver,
|
|
|
|
+ &driver_attr_poll_mode_io);
|
|
driver_remove_file(&megasas_pci_driver.driver,
|
|
driver_remove_file(&megasas_pci_driver.driver,
|
|
&driver_attr_dbg_lvl);
|
|
&driver_attr_dbg_lvl);
|
|
driver_remove_file(&megasas_pci_driver.driver,
|
|
driver_remove_file(&megasas_pci_driver.driver,
|