*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2003-2005 LSI Logic Corporation.
+ * Copyright (c) 2003-2005 LSI Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 2 of the License, or (at your option) any later version.
*
* FILE : megaraid_sas.c
- * Version : v00.00.03.10-rc5
+ * Version : v00.00.03.16-rc1
*
* Authors:
* (email-id : megaraidlinux@lsi.com)
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uio.h>
#include <scsi/scsi_host.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_VERSION(MEGASAS_VERSION);
MODULE_AUTHOR("megaraidlinux@lsi.com");
-MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
+MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
/*
* PCI ID table for all supported controllers
static u32 megasas_dbg_lvl;
+static void
+megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
+ u8 alt_status);
+
/**
* megasas_get_cmd - Get a command from the free pool
* @instance: Adapter soft state
atomic_inc(&instance->fw_outstanding);
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;
}
+/**
+ * megasas_complete_cmd_dpc - Returns FW's controller structure
+ * @instance_addr: Address of adapter soft state
+ *
+ * Tasklet to complete cmds
+ */
+static void megasas_complete_cmd_dpc(unsigned long instance_addr)
+{
+ u32 producer;
+ u32 consumer;
+ u32 context;
+ struct megasas_cmd *cmd;
+ struct megasas_instance *instance =
+ (struct megasas_instance *)instance_addr;
+ unsigned long flags;
+
+ /* If we have already declared adapter dead, donot complete cmds */
+ if (instance->hw_crit_error)
+ return;
+
+ spin_lock_irqsave(&instance->completion_lock, flags);
+
+ producer = *instance->producer;
+ consumer = *instance->consumer;
+
+ while (consumer != producer) {
+ context = instance->reply_queue[consumer];
+
+ cmd = instance->cmd_list[context];
+
+ megasas_complete_cmd(instance, cmd, DID_OK);
+
+ consumer++;
+ if (consumer == (instance->max_fw_cmds + 1)) {
+ consumer = 0;
+ }
+ }
+
+ *instance->consumer = producer;
+
+ spin_unlock_irqrestore(&instance->completion_lock, flags);
+
+ /*
+ * Check if we can restore can_queue
+ */
+ if (instance->flag & MEGASAS_FW_BUSY
+ && time_after(jiffies, instance->last_time + 5 * HZ)
+ && atomic_read(&instance->fw_outstanding) < 17) {
+
+ spin_lock_irqsave(instance->host->host_lock, flags);
+ instance->flag &= ~MEGASAS_FW_BUSY;
+ instance->host->can_queue =
+ instance->max_fw_cmds - MEGASAS_INT_CMDS;
+
+ spin_unlock_irqrestore(instance->host->host_lock, flags);
+ }
+}
+
/**
* megasas_wait_for_outstanding - Wait for all outstanding cmds
* @instance: Adapter soft state
if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
"commands to complete\n",i,outstanding);
+ /*
+ * Call cmd completion routine. Cmd to be
+ * be completed directly without depending on isr.
+ */
+ megasas_complete_cmd_dpc((unsigned long)instance);
}
msleep(1000);
static struct scsi_host_template megasas_template = {
.module = THIS_MODULE,
- .name = "LSI Logic SAS based MegaRAID driver",
+ .name = "LSI SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
.slave_configure = megasas_slave_configure,
.queuecommand = megasas_queue_command,
.eh_timed_out = megasas_reset_timer,
.bios_param = megasas_bios_param,
.use_clustering = ENABLE_CLUSTERING,
- .use_sg_chaining = ENABLE_SG_CHAINING,
};
/**
return ret;
}
-/**
- * megasas_complete_cmd_dpc - Returns FW's controller structure
- * @instance_addr: Address of adapter soft state
- *
- * Tasklet to complete cmds
- */
-static void megasas_complete_cmd_dpc(unsigned long instance_addr)
-{
- u32 producer;
- u32 consumer;
- u32 context;
- struct megasas_cmd *cmd;
- struct megasas_instance *instance = (struct megasas_instance *)instance_addr;
- unsigned long flags;
-
- /* If we have already declared adapter dead, donot complete cmds */
- if (instance->hw_crit_error)
- return;
-
- producer = *instance->producer;
- consumer = *instance->consumer;
-
- while (consumer != producer) {
- context = instance->reply_queue[consumer];
-
- cmd = instance->cmd_list[context];
-
- megasas_complete_cmd(instance, cmd, DID_OK);
-
- consumer++;
- if (consumer == (instance->max_fw_cmds + 1)) {
- consumer = 0;
- }
- }
-
- *instance->consumer = producer;
-
- /*
- * Check if we can restore can_queue
- */
- if (instance->flag & MEGASAS_FW_BUSY
- && time_after(jiffies, instance->last_time + 5 * HZ)
- && atomic_read(&instance->fw_outstanding) < 17) {
-
- spin_lock_irqsave(instance->host->host_lock, flags);
- instance->flag &= ~MEGASAS_FW_BUSY;
- instance->host->can_queue =
- instance->max_fw_cmds - MEGASAS_INT_CMDS;
-
- spin_unlock_irqrestore(instance->host->host_lock, flags);
- }
-
-}
-
/**
* megasas_issue_init_mfi - Initializes the FW
* @instance: Adapter soft state
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
* @instance: Adapter soft state
*/
instance->base_addr = pci_resource_start(instance->pdev, 0);
- if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) {
+ if (pci_request_regions(instance->pdev, "megasas: LSI")) {
printk(KERN_DEBUG "megasas: IO memory region busy!\n");
return -EBUSY;
}
* 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;
fail_fw_init:
init_waitqueue_head(&instance->abort_cmd_wait_q);
spin_lock_init(&instance->cmd_pool_lock);
+ spin_lock_init(&instance->completion_lock);
mutex_init(&instance->aen_mutex);
sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
}
/**
- * 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
*/
static int __devinit
instance = pci_get_drvdata(pdev);
host = instance->host;
+ if (poll_mode_io)
+ del_timer_sync(&instance->io_completion_timer);
+
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
tasklet_kill(&instance->isr_tasklet);
if (megasas_start_aen(instance))
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;
fail_irq:
instance = pci_get_drvdata(pdev);
host = instance->host;
+ if (poll_mode_io)
+ del_timer_sync(&instance->io_completion_timer);
+
scsi_remove_host(instance->host);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
static ssize_t
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 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
&driver_attr_dbg_lvl);
if (rval)
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;
+
+err_dcf_poll_mode_io:
+ driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_dbg_lvl);
err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
*/
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_attr_dbg_lvl);
driver_remove_file(&megasas_pci_driver.driver,