]> err.no Git - linux-2.6/blobdiff - drivers/scsi/megaraid/megaraid_sas.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/hpa/linux-2.6-inttypes
[linux-2.6] / drivers / scsi / megaraid / megaraid_sas.c
index 9aee2544798c32b1c6df8c681b1945485546e239..7d84c8bbcf3fc1a55b8a9c04cbc65e2e05b33902 100644 (file)
@@ -2,7 +2,7 @@
  *
  *             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
@@ -10,7 +10,7 @@
  *        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.20-rc1
  *
  * Authors:
  *     (email-id : megaraidlinux@lsi.com)
@@ -31,7 +31,6 @@
 #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
@@ -61,6 +68,8 @@ static struct pci_device_id megasas_pci_table[] = {
        /* xscale IOP */
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)},
        /* ppc IOP */
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078DE)},
+       /* ppc IOP */
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
        /* xscale IOP, vega */
        {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
@@ -77,6 +86,10 @@ static DEFINE_MUTEX(megasas_async_queue_mutex);
 
 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
@@ -477,12 +490,13 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
 
  /**
  * megasas_get_frame_count - Computes the number of frames
+ * @frame_type         : type of frame- io or pthru frame
  * @sge_count          : number of sg elements
  *
  * Returns the number of frames required for numnber of sge's (sge_count)
  */
 
-static u32 megasas_get_frame_count(u8 sge_count)
+static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
 {
        int num_cnt;
        int sge_bytes;
@@ -493,13 +507,22 @@ static u32 megasas_get_frame_count(u8 sge_count)
            sizeof(struct megasas_sge32);
 
        /*
-       * Main frame can contain 2 SGEs for 64-bit SGLs and
-       * 3 SGEs for 32-bit SGLs
-       */
-       if (IS_DMA64)
-               num_cnt = sge_count - 2;
-       else
-               num_cnt = sge_count - 3;
+        * Main frame can contain 2 SGEs for 64-bit SGLs and
+        * 3 SGEs for 32-bit SGLs for ldio &
+        * 1 SGEs for 64-bit SGLs and
+        * 2 SGEs for 32-bit SGLs for pthru frame
+        */
+       if (unlikely(frame_type == PTHRU_FRAME)) {
+               if (IS_DMA64)
+                       num_cnt = sge_count - 1;
+               else
+                       num_cnt = sge_count - 2;
+       } else {
+               if (IS_DMA64)
+                       num_cnt = sge_count - 2;
+               else
+                       num_cnt = sge_count - 3;
+       }
 
        if(num_cnt>0){
                sge_bytes = sge_sz * num_cnt;
@@ -581,7 +604,8 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
         * Compute the total number of frames this command consumes. FW uses
         * this number to pull sufficient number of frames from host memory.
         */
-       cmd->frame_count = megasas_get_frame_count(pthru->sge_count);
+       cmd->frame_count = megasas_get_frame_count(pthru->sge_count,
+                                                       PTHRU_FRAME);
 
        return cmd->frame_count;
 }
@@ -698,7 +722,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
         * Compute the total number of frames this command consumes. FW uses
         * this number to pull sufficient number of frames from host memory.
         */
-       cmd->frame_count = megasas_get_frame_count(ldio->sge_count);
+       cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME);
 
        return cmd->frame_count;
 }
@@ -856,6 +880,12 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
        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;
 
@@ -886,6 +916,64 @@ static int megasas_slave_configure(struct scsi_device *sdev)
        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
@@ -909,6 +997,11 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
                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);
@@ -1101,7 +1194,7 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
 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,
@@ -1111,7 +1204,6 @@ static struct scsi_host_template megasas_template = {
        .eh_timed_out = megasas_reset_timer,
        .bios_param = megasas_bios_param,
        .use_clustering = ENABLE_CLUSTERING,
-       .use_sg_chaining = ENABLE_SG_CHAINING,
 };
 
 /**
@@ -1381,7 +1473,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                        instance->instancet->disable_intr(instance->reg_set);
                        writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell);
 
-                       max_wait = 10;
+                       max_wait = 60;
                        cur_state = MFI_STATE_OPERATIONAL;
                        break;
 
@@ -1749,60 +1841,6 @@ megasas_get_ctrl_info(struct megasas_instance *instance,
        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
@@ -1878,6 +1916,47 @@ fail_fw_init:
        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
@@ -1890,6 +1969,7 @@ static int megasas_init_mfi(struct megasas_instance *instance)
        u32 reply_q_sz;
        u32 max_sectors_1;
        u32 max_sectors_2;
+       u32 tmp_sectors;
        struct megasas_register_set __iomem *reg_set;
        struct megasas_ctrl_info *ctrl_info;
        /*
@@ -1897,7 +1977,7 @@ static int megasas_init_mfi(struct megasas_instance *instance)
         */
        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;
        }
@@ -1913,7 +1993,8 @@ static int megasas_init_mfi(struct megasas_instance *instance)
 
        switch(instance->pdev->device)
        {
-               case PCI_DEVICE_ID_LSI_SAS1078R:        
+               case PCI_DEVICE_ID_LSI_SAS1078R:
+               case PCI_DEVICE_ID_LSI_SAS1078DE:
                        instance->instancet = &megasas_instance_template_ppc;
                        break;
                case PCI_DEVICE_ID_LSI_SAS1064R:
@@ -1982,17 +2063,20 @@ static int megasas_init_mfi(struct megasas_instance *instance)
         * Note that older firmwares ( < FW ver 30) didn't report information
         * to calculate max_sectors_1. So the number ended up as zero always.
         */
+       tmp_sectors = 0;
        if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) {
 
                max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *
                    ctrl_info->max_strips_per_io;
                max_sectors_2 = ctrl_info->max_request_size;
 
-               instance->max_sectors_per_req = (max_sectors_1 < max_sectors_2)
-                   ? max_sectors_1 : max_sectors_2;
-       } else
-               instance->max_sectors_per_req = instance->max_num_sge *
-                   PAGE_SIZE / 512;
+               tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2);
+       }
+
+       instance->max_sectors_per_req = instance->max_num_sge *
+                                               PAGE_SIZE / 512;
+       if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
+               instance->max_sectors_per_req = tmp_sectors;
 
        kfree(ctrl_info);
 
@@ -2000,8 +2084,14 @@ static int megasas_init_mfi(struct megasas_instance *instance)
        * 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:
@@ -2391,6 +2481,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        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);
@@ -2559,12 +2650,13 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
        return;
 }
 
+#ifdef CONFIG_PM
 /**
- * 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
+static int
 megasas_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct Scsi_Host *host;
@@ -2573,6 +2665,9 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
        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);
@@ -2593,7 +2688,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
  * megasas_resume-      driver resume entry point
  * @pdev:               PCI device structure
  */
-static int __devinit
+static int
 megasas_resume(struct pci_dev *pdev)
 {
        int rval;
@@ -2659,6 +2754,11 @@ megasas_resume(struct pci_dev *pdev)
        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:
@@ -2683,12 +2783,16 @@ fail_ready_state:
 
        return -ENODEV;
 }
+#else
+#define megasas_suspend        NULL
+#define megasas_resume NULL
+#endif
 
 /**
  * megasas_detach_one -        PCI hot"un"plug entry point
  * @pdev:              PCI device structure
  */
-static void megasas_detach_one(struct pci_dev *pdev)
+static void __devexit megasas_detach_one(struct pci_dev *pdev)
 {
        int i;
        struct Scsi_Host *host;
@@ -2697,6 +2801,9 @@ static void megasas_detach_one(struct pci_dev *pdev)
        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);
@@ -2933,6 +3040,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
 
                if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
                                 sense, ioc->sense_len)) {
+                       printk(KERN_ERR "megasas: Failed to copy out to user "
+                                       "sense data\n");
                        error = -EFAULT;
                        goto out;
                }
@@ -3167,7 +3276,7 @@ static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
 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
@@ -3182,7 +3291,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,
-                  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
@@ -3233,8 +3400,16 @@ static int __init megasas_init(void)
                                  &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);
@@ -3252,6 +3427,8 @@ err_pcidrv:
  */
 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,