[SCSI] mpt2sas: add missing allocation check
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / mpt2sas / mpt2sas_base.c
index 6825772cfd6a16199ff8b8ab1f53e85ada6b62cb..82fa6ce481f0c131855667fbfda247d620cb2bfa 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/sort.h>
 #include <linux/io.h>
 #include <linux/time.h>
+#include <linux/kthread.h>
 #include <linux/aer.h>
 
 #include "mpt2sas_base.h"
@@ -65,6 +66,8 @@ static MPT_CALLBACK   mpt_callbacks[MPT_MAX_CALLBACKS];
 
 #define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
 
+#define MAX_HBA_QUEUE_DEPTH    30000
+#define MAX_CHAIN_DEPTH                100000
 static int max_queue_depth = -1;
 module_param(max_queue_depth, int, 0);
 MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
@@ -81,21 +84,9 @@ static int missing_delay[2] = {-1, -1};
 module_param_array(missing_delay, int, NULL, 0);
 MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");
 
-/* diag_buffer_enable is bitwise
- * bit 0 set = TRACE
- * bit 1 set = SNAPSHOT
- * bit 2 set = EXTENDED
- *
- * Either bit can be set, or both
- */
-static int diag_buffer_enable;
-module_param(diag_buffer_enable, int, 0);
-MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
-    "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
-
 static int mpt2sas_fwfault_debug;
 MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
-    "and halt firmware - (default=0)");
+       "and halt firmware - (default=0)");
 
 static int disable_discovery = -1;
 module_param(disable_discovery, int, 0);
@@ -119,9 +110,33 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
                ioc->fwfault_debug = mpt2sas_fwfault_debug;
        return 0;
 }
+
 module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug,
     param_get_int, &mpt2sas_fwfault_debug, 0644);
 
+/**
+ *  mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc
+ * @arg: input argument, used to derive ioc
+ *
+ * Return 0 if controller is removed from pci subsystem.
+ * Return -1 for other case.
+ */
+static int mpt2sas_remove_dead_ioc_func(void *arg)
+{
+               struct MPT2SAS_ADAPTER *ioc = (struct MPT2SAS_ADAPTER *)arg;
+               struct pci_dev *pdev;
+
+               if ((ioc == NULL))
+                       return -1;
+
+               pdev = ioc->pdev;
+               if ((pdev == NULL))
+                       return -1;
+               pci_remove_bus_device(pdev);
+               return 0;
+}
+
+
 /**
  * _base_fault_reset_work - workq handling ioc fault conditions
  * @work: input argument, used to derive ioc
@@ -137,6 +152,7 @@ _base_fault_reset_work(struct work_struct *work)
        unsigned long    flags;
        u32 doorbell;
        int rc;
+       struct task_struct *p;
 
        spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
        if (ioc->shost_recovery)
@@ -144,6 +160,39 @@ _base_fault_reset_work(struct work_struct *work)
        spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
 
        doorbell = mpt2sas_base_get_iocstate(ioc, 0);
+       if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) {
+               printk(MPT2SAS_INFO_FMT "%s : SAS host is non-operational !!!!\n",
+                       ioc->name, __func__);
+
+               /*
+                * Call _scsih_flush_pending_cmds callback so that we flush all
+                * pending commands back to OS. This call is required to aovid
+                * deadlock at block layer. Dead IOC will fail to do diag reset,
+                * and this call is safe since dead ioc will never return any
+                * command back from HW.
+                */
+               ioc->schedule_dead_ioc_flush_running_cmds(ioc);
+               /*
+                * Set remove_host flag early since kernel thread will
+                * take some time to execute.
+                */
+               ioc->remove_host = 1;
+               /*Remove the Dead Host */
+               p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc,
+                   "mpt2sas_dead_ioc_%d", ioc->id);
+               if (IS_ERR(p)) {
+                       printk(MPT2SAS_ERR_FMT
+                       "%s: Running mpt2sas_dead_ioc thread failed !!!!\n",
+                       ioc->name, __func__);
+               } else {
+                   printk(MPT2SAS_ERR_FMT
+                       "%s: Running mpt2sas_dead_ioc thread success !!!!\n",
+                       ioc->name, __func__);
+               }
+
+               return; /* don't rearm timer */
+       }
+
        if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
                rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
                    FORCE_BIG_HAMMER);
@@ -691,6 +740,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
                memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
        }
        ioc->base_cmds.status &= ~MPT2_CMD_PENDING;
+
        complete(&ioc->base_cmds.done);
        return 1;
 }
@@ -833,25 +883,31 @@ union reply_descriptor {
 static irqreturn_t
 _base_interrupt(int irq, void *bus_id)
 {
+       struct adapter_reply_queue *reply_q = bus_id;
        union reply_descriptor rd;
        u32 completed_cmds;
        u8 request_desript_type;
        u16 smid;
        u8 cb_idx;
        u32 reply;
-       u8 msix_index;
-       struct MPT2SAS_ADAPTER *ioc = bus_id;
+       u8 msix_index = reply_q->msix_index;
+       struct MPT2SAS_ADAPTER *ioc = reply_q->ioc;
        Mpi2ReplyDescriptorsUnion_t *rpf;
        u8 rc;
 
        if (ioc->mask_interrupts)
                return IRQ_NONE;
 
-       rpf = &ioc->reply_post_free[ioc->reply_post_host_index];
+       if (!atomic_add_unless(&reply_q->busy, 1, 1))
+               return IRQ_NONE;
+
+       rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index];
        request_desript_type = rpf->Default.ReplyFlags
             & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
-       if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+       if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) {
+               atomic_dec(&reply_q->busy);
                return IRQ_NONE;
+       }
 
        completed_cmds = 0;
        cb_idx = 0xFF;
@@ -860,9 +916,7 @@ _base_interrupt(int irq, void *bus_id)
                if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
                        goto out;
                reply = 0;
-               cb_idx = 0xFF;
                smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1);
-               msix_index = rpf->Default.MSIxIndex;
                if (request_desript_type ==
                    MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
                        reply = le32_to_cpu
@@ -906,31 +960,85 @@ _base_interrupt(int irq, void *bus_id)
  next:
 
                rpf->Words = cpu_to_le64(ULLONG_MAX);
-               ioc->reply_post_host_index = (ioc->reply_post_host_index ==
+               reply_q->reply_post_host_index =
+                   (reply_q->reply_post_host_index ==
                    (ioc->reply_post_queue_depth - 1)) ? 0 :
-                   ioc->reply_post_host_index + 1;
+                   reply_q->reply_post_host_index + 1;
                request_desript_type =
-                   ioc->reply_post_free[ioc->reply_post_host_index].Default.
-                   ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+                   reply_q->reply_post_free[reply_q->reply_post_host_index].
+                   Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
                completed_cmds++;
                if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
                        goto out;
-               if (!ioc->reply_post_host_index)
-                       rpf = ioc->reply_post_free;
+               if (!reply_q->reply_post_host_index)
+                       rpf = reply_q->reply_post_free;
                else
                        rpf++;
        } while (1);
 
  out:
 
-       if (!completed_cmds)
+       if (!completed_cmds) {
+               atomic_dec(&reply_q->busy);
                return IRQ_NONE;
-
+       }
        wmb();
-       writel(ioc->reply_post_host_index, &ioc->chip->ReplyPostHostIndex);
+       if (ioc->is_warpdrive) {
+               writel(reply_q->reply_post_host_index,
+               ioc->reply_post_host_index[msix_index]);
+               atomic_dec(&reply_q->busy);
+               return IRQ_HANDLED;
+       }
+       writel(reply_q->reply_post_host_index | (msix_index <<
+           MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex);
+       atomic_dec(&reply_q->busy);
        return IRQ_HANDLED;
 }
 
+/**
+ * _base_is_controller_msix_enabled - is controller support muli-reply queues
+ * @ioc: per adapter object
+ *
+ */
+static inline int
+_base_is_controller_msix_enabled(struct MPT2SAS_ADAPTER *ioc)
+{
+       return (ioc->facts.IOCCapabilities &
+           MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable;
+}
+
+/**
+ * mpt2sas_base_flush_reply_queues - flushing the MSIX reply queues
+ * @ioc: per adapter object
+ * Context: ISR conext
+ *
+ * Called when a Task Management request has completed. We want
+ * to flush the other reply queues so all the outstanding IO has been
+ * completed back to OS before we process the TM completetion.
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc)
+{
+       struct adapter_reply_queue *reply_q;
+
+       /* If MSIX capability is turned off
+        * then multi-queues are not enabled
+        */
+       if (!_base_is_controller_msix_enabled(ioc))
+               return;
+
+       list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+               if (ioc->shost_recovery)
+                       return;
+               /* TMs are on msix_index == 0 */
+               if (reply_q->msix_index == 0)
+                       continue;
+               _base_interrupt(reply_q->vector, (void *)reply_q);
+       }
+}
+
 /**
  * mpt2sas_base_release_callback_handler - clear interrupt callback handler
  * @cb_idx: callback index
@@ -1081,74 +1189,171 @@ _base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev)
 }
 
 /**
- * _base_save_msix_table - backup msix vector table
+ * _base_check_enable_msix - checks MSIX capabable.
  * @ioc: per adapter object
  *
- * This address an errata where diag reset clears out the table
+ * Check to see if card is capable of MSIX, and set number
+ * of available msix vectors
  */
-static void
-_base_save_msix_table(struct MPT2SAS_ADAPTER *ioc)
+static int
+_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
 {
-       int i;
+       int base;
+       u16 message_control;
 
-       if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
-               return;
 
-       for (i = 0; i < ioc->msix_vector_count; i++)
-               ioc->msix_table_backup[i] = ioc->msix_table[i];
+       base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
+       if (!base) {
+               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
+                   "supported\n", ioc->name));
+               return -EINVAL;
+       }
+
+       /* get msix vector count */
+       /* NUMA_IO not supported for older controllers */
+       if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 ||
+           ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2)
+               ioc->msix_vector_count = 1;
+       else {
+               pci_read_config_word(ioc->pdev, base + 2, &message_control);
+               ioc->msix_vector_count = (message_control & 0x3FF) + 1;
+       }
+       dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, "
+           "vector_count(%d)\n", ioc->name, ioc->msix_vector_count));
+
+       return 0;
 }
 
 /**
- * _base_restore_msix_table - this restores the msix vector table
+ * _base_free_irq - free irq
  * @ioc: per adapter object
  *
+ * Freeing respective reply_queue from the list.
  */
 static void
-_base_restore_msix_table(struct MPT2SAS_ADAPTER *ioc)
+_base_free_irq(struct MPT2SAS_ADAPTER *ioc)
 {
-       int i;
+       struct adapter_reply_queue *reply_q, *next;
 
-       if (!ioc->msix_enable || ioc->msix_table_backup == NULL)
+       if (list_empty(&ioc->reply_queue_list))
                return;
 
-       for (i = 0; i < ioc->msix_vector_count; i++)
-               ioc->msix_table[i] = ioc->msix_table_backup[i];
+       list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) {
+               list_del(&reply_q->list);
+               synchronize_irq(reply_q->vector);
+               free_irq(reply_q->vector, reply_q);
+               kfree(reply_q);
+       }
 }
 
 /**
- * _base_check_enable_msix - checks MSIX capabable.
+ * _base_request_irq - request irq
  * @ioc: per adapter object
+ * @index: msix index into vector table
+ * @vector: irq vector
  *
- * Check to see if card is capable of MSIX, and set number
- * of available msix vectors
+ * Inserting respective reply_queue into the list.
  */
 static int
-_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
+_base_request_irq(struct MPT2SAS_ADAPTER *ioc, u8 index, u32 vector)
 {
-       int base;
-       u16 message_control;
-       u32 msix_table_offset;
+       struct adapter_reply_queue *reply_q;
+       int r;
 
-       base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
-       if (!base) {
-               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
-                   "supported\n", ioc->name));
-               return -EINVAL;
+       reply_q =  kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL);
+       if (!reply_q) {
+               printk(MPT2SAS_ERR_FMT "unable to allocate memory %d!\n",
+                   ioc->name, (int)sizeof(struct adapter_reply_queue));
+               return -ENOMEM;
+       }
+       reply_q->ioc = ioc;
+       reply_q->msix_index = index;
+       reply_q->vector = vector;
+       atomic_set(&reply_q->busy, 0);
+       if (ioc->msix_enable)
+               snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d",
+                   MPT2SAS_DRIVER_NAME, ioc->id, index);
+       else
+               snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d",
+                   MPT2SAS_DRIVER_NAME, ioc->id);
+       r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name,
+           reply_q);
+       if (r) {
+               printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n",
+                   reply_q->name, vector);
+               kfree(reply_q);
+               return -EBUSY;
        }
 
-       /* get msix vector count */
-       pci_read_config_word(ioc->pdev, base + 2, &message_control);
-       ioc->msix_vector_count = (message_control & 0x3FF) + 1;
+       INIT_LIST_HEAD(&reply_q->list);
+       list_add_tail(&reply_q->list, &ioc->reply_queue_list);
+       return 0;
+}
 
-       /* get msix table  */
-       pci_read_config_dword(ioc->pdev, base + 4, &msix_table_offset);
-       msix_table_offset &= 0xFFFFFFF8;
-       ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset);
+/**
+ * _base_assign_reply_queues - assigning msix index for each cpu
+ * @ioc: per adapter object
+ *
+ * The enduser would need to set the affinity via /proc/irq/#/smp_affinity
+ *
+ * It would nice if we could call irq_set_affinity, however it is not
+ * an exported symbol
+ */
+static void
+_base_assign_reply_queues(struct MPT2SAS_ADAPTER *ioc)
+{
+       struct adapter_reply_queue *reply_q;
+       int cpu_id;
+       int cpu_grouping, loop, grouping, grouping_mod;
 
-       dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, "
-           "vector_count(%d), table_offset(0x%08x), table(%p)\n", ioc->name,
-           ioc->msix_vector_count, msix_table_offset, ioc->msix_table));
-       return 0;
+       if (!_base_is_controller_msix_enabled(ioc))
+               return;
+
+       memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz);
+       /* when there are more cpus than available msix vectors,
+        * then group cpus togeather on same irq
+        */
+       if (ioc->cpu_count > ioc->msix_vector_count) {
+               grouping = ioc->cpu_count / ioc->msix_vector_count;
+               grouping_mod = ioc->cpu_count % ioc->msix_vector_count;
+               if (grouping < 2 || (grouping == 2 && !grouping_mod))
+                       cpu_grouping = 2;
+               else if (grouping < 4 || (grouping == 4 && !grouping_mod))
+                       cpu_grouping = 4;
+               else if (grouping < 8 || (grouping == 8 && !grouping_mod))
+                       cpu_grouping = 8;
+               else
+                       cpu_grouping = 16;
+       } else
+               cpu_grouping = 0;
+
+       loop = 0;
+       reply_q = list_entry(ioc->reply_queue_list.next,
+            struct adapter_reply_queue, list);
+       for_each_online_cpu(cpu_id) {
+               if (!cpu_grouping) {
+                       ioc->cpu_msix_table[cpu_id] = reply_q->msix_index;
+                       reply_q = list_entry(reply_q->list.next,
+                           struct adapter_reply_queue, list);
+               } else {
+                       if (loop < cpu_grouping) {
+                               ioc->cpu_msix_table[cpu_id] =
+                                       reply_q->msix_index;
+                               loop++;
+                       } else {
+                               reply_q = list_entry(reply_q->list.next,
+                                   struct adapter_reply_queue, list);
+                               ioc->cpu_msix_table[cpu_id] =
+                                       reply_q->msix_index;
+                               loop = 1;
+                       }
+               }
+       }
 }
 
 /**
@@ -1161,8 +1366,6 @@ _base_disable_msix(struct MPT2SAS_ADAPTER *ioc)
 {
        if (ioc->msix_enable) {
                pci_disable_msix(ioc->pdev);
-               kfree(ioc->msix_table_backup);
-               ioc->msix_table_backup = NULL;
                ioc->msix_enable = 0;
        }
 }
@@ -1175,10 +1378,13 @@ _base_disable_msix(struct MPT2SAS_ADAPTER *ioc)
 static int
 _base_enable_msix(struct MPT2SAS_ADAPTER *ioc)
 {
-       struct msix_entry entries;
+       struct msix_entry *entries, *a;
        int r;
+       int i;
        u8 try_msix = 0;
 
+       INIT_LIST_HEAD(&ioc->reply_queue_list);
+
        if (msix_disable == -1 || msix_disable == 0)
                try_msix = 1;
 
@@ -1188,51 +1394,48 @@ _base_enable_msix(struct MPT2SAS_ADAPTER *ioc)
        if (_base_check_enable_msix(ioc) != 0)
                goto try_ioapic;
 
-       ioc->msix_table_backup = kcalloc(ioc->msix_vector_count,
-           sizeof(u32), GFP_KERNEL);
-       if (!ioc->msix_table_backup) {
-               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for "
-                   "msix_table_backup failed!!!\n", ioc->name));
+       ioc->reply_queue_count = min_t(int, ioc->cpu_count,
+           ioc->msix_vector_count);
+
+       entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry),
+           GFP_KERNEL);
+       if (!entries) {
+               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "kcalloc "
+                   "failed @ at %s:%d/%s() !!!\n", ioc->name, __FILE__,
+                   __LINE__, __func__));
                goto try_ioapic;
        }
 
-       memset(&entries, 0, sizeof(struct msix_entry));
-       r = pci_enable_msix(ioc->pdev, &entries, 1);
+       for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++)
+               a->entry = i;
+
+       r = pci_enable_msix(ioc->pdev, entries, ioc->reply_queue_count);
        if (r) {
                dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "pci_enable_msix "
                    "failed (r=%d) !!!\n", ioc->name, r));
+               kfree(entries);
                goto try_ioapic;
        }
 
-       r = request_irq(entries.vector, _base_interrupt, IRQF_SHARED,
-           ioc->name, ioc);
-       if (r) {
-               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "unable to allocate "
-                   "interrupt %d !!!\n", ioc->name, entries.vector));
-               pci_disable_msix(ioc->pdev);
-               goto try_ioapic;
+       ioc->msix_enable = 1;
+       for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) {
+               r = _base_request_irq(ioc, i, a->vector);
+               if (r) {
+                       _base_free_irq(ioc);
+                       _base_disable_msix(ioc);
+                       kfree(entries);
+                       goto try_ioapic;
+               }
        }
 
-       ioc->pci_irq = entries.vector;
-       ioc->msix_enable = 1;
+       kfree(entries);
        return 0;
 
 /* failback to io_apic interrupt routing */
  try_ioapic:
 
-       r = request_irq(ioc->pdev->irq, _base_interrupt, IRQF_SHARED,
-           ioc->name, ioc);
-       if (r) {
-               printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n",
-                   ioc->name, ioc->pdev->irq);
-               r = -EBUSY;
-               goto out_fail;
-       }
-
-       ioc->pci_irq = ioc->pdev->irq;
-       return 0;
+       r = _base_request_irq(ioc, 0, ioc->pdev->irq);
 
- out_fail:
        return r;
 }
 
@@ -1251,6 +1454,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
        int i, r = 0;
        u64 pio_chip = 0;
        u64 chip_phys = 0;
+       struct adapter_reply_queue *reply_q;
 
        dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n",
            ioc->name, __func__));
@@ -1313,9 +1517,11 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
        if (r)
                goto out_fail;
 
-       printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n",
-           ioc->name,  ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
-           "IO-APIC enabled"), ioc->pci_irq);
+       list_for_each_entry(reply_q, &ioc->reply_queue_list, list)
+               printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n",
+                   reply_q->name,  ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
+                   "IO-APIC enabled"), reply_q->vector);
+
        printk(MPT2SAS_INFO_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
            ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz);
        printk(MPT2SAS_INFO_FMT "ioport(0x%016llx), size(%d)\n",
@@ -1330,7 +1536,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc)
        if (ioc->chip_phys)
                iounmap(ioc->chip);
        ioc->chip_phys = 0;
-       ioc->pci_irq = -1;
        pci_release_selected_regions(ioc->pdev, ioc->bars);
        pci_disable_pcie_error_reporting(pdev);
        pci_disable_device(pdev);
@@ -1577,6 +1782,12 @@ static inline void _base_writeq(__u64 b, volatile void __iomem *addr,
 }
 #endif
 
+static inline u8
+_base_get_msix_index(struct MPT2SAS_ADAPTER *ioc)
+{
+       return ioc->cpu_msix_table[smp_processor_id()];
+}
+
 /**
  * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware
  * @ioc: per adapter object
@@ -1593,7 +1804,7 @@ mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle)
 
 
        descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
-       descriptor.SCSIIO.MSIxIndex = 0; /* TODO */
+       descriptor.SCSIIO.MSIxIndex =  _base_get_msix_index(ioc);
        descriptor.SCSIIO.SMID = cpu_to_le16(smid);
        descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
        descriptor.SCSIIO.LMID = 0;
@@ -1617,7 +1828,7 @@ mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid)
 
        descriptor.HighPriority.RequestFlags =
            MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
-       descriptor.HighPriority.MSIxIndex = 0; /* TODO */
+       descriptor.HighPriority.MSIxIndex =  0;
        descriptor.HighPriority.SMID = cpu_to_le16(smid);
        descriptor.HighPriority.LMID = 0;
        descriptor.HighPriority.Reserved1 = 0;
@@ -1639,7 +1850,7 @@ mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid)
        u64 *request = (u64 *)&descriptor;
 
        descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
-       descriptor.Default.MSIxIndex = 0; /* TODO */
+       descriptor.Default.MSIxIndex =  _base_get_msix_index(ioc);
        descriptor.Default.SMID = cpu_to_le16(smid);
        descriptor.Default.LMID = 0;
        descriptor.Default.DescriptorTypeDependent = 0;
@@ -1664,7 +1875,7 @@ mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid,
 
        descriptor.SCSITarget.RequestFlags =
            MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET;
-       descriptor.SCSITarget.MSIxIndex = 0; /* TODO */
+       descriptor.SCSITarget.MSIxIndex =  _base_get_msix_index(ioc);
        descriptor.SCSITarget.SMID = cpu_to_le16(smid);
        descriptor.SCSITarget.LMID = 0;
        descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index);
@@ -1753,6 +1964,10 @@ _base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc)
                        printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
                            MPT2SAS_INTEL_RMS2LL040_BRANDING);
                        break;
+               case MPT2SAS_INTEL_RAMSDALE_SSDID:
+                       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
+                           MPT2SAS_INTEL_RAMSDALE_BRANDING);
+                       break;
                default:
                        break;
                }
@@ -1762,6 +1977,22 @@ _base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc)
                        printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
                            MPT2SAS_INTEL_RS25GB008_BRANDING);
                        break;
+               case MPT2SAS_INTEL_RMS25JB080_SSDID:
+                       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
+                           MPT2SAS_INTEL_RMS25JB080_BRANDING);
+                       break;
+               case MPT2SAS_INTEL_RMS25JB040_SSDID:
+                       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
+                           MPT2SAS_INTEL_RMS25JB040_BRANDING);
+                       break;
+               case MPT2SAS_INTEL_RMS25KB080_SSDID:
+                       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
+                           MPT2SAS_INTEL_RMS25KB080_BRANDING);
+                       break;
+               case MPT2SAS_INTEL_RMS25KB040_SSDID:
+                       printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
+                           MPT2SAS_INTEL_RMS25KB040_BRANDING);
+                       break;
                default:
                        break;
                }
@@ -2148,8 +2379,6 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc)
                }
                if (ioc->chain_dma_pool)
                        pci_pool_destroy(ioc->chain_dma_pool);
-       }
-       if (ioc->chain_lookup) {
                free_pages((ulong)ioc->chain_lookup, ioc->chain_pages);
                ioc->chain_lookup = NULL;
        }
@@ -2167,11 +2396,9 @@ static int
 _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc,  int sleep_flag)
 {
        struct mpt2sas_facts *facts;
-       u32 queue_size, queue_diff;
        u16 max_sge_elements;
-       u16 num_of_reply_frames;
        u16 chains_needed_per_io;
-       u32 sz, total_sz;
+       u32 sz, total_sz, reply_post_free_sz;
        u32 retry_sz;
        u16 max_request_credit;
        int i;
@@ -2196,7 +2423,8 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc,  int sleep_flag)
                max_request_credit = (max_queue_depth < facts->RequestCredit)
                    ? max_queue_depth : facts->RequestCredit;
        else
-               max_request_credit = facts->RequestCredit;
+               max_request_credit = min_t(u16, facts->RequestCredit,
+                   MAX_HBA_QUEUE_DEPTH);
 
        ioc->hba_queue_depth = max_request_credit;
        ioc->hi_priority_depth = facts->HighPriorityCredit;
@@ -2237,50 +2465,25 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc,  int sleep_flag)
        }
        ioc->chains_needed_per_io = chains_needed_per_io;
 
-       /* reply free queue sizing - taking into account for events */
-       num_of_reply_frames = ioc->hba_queue_depth + 32;
-
-       /* number of replies frames can't be a multiple of 16 */
-       /* decrease number of reply frames by 1 */
-       if (!(num_of_reply_frames % 16))
-               num_of_reply_frames--;
-
-       /* calculate number of reply free queue entries
-        *  (must be multiple of 16)
-        */
-
-       /* (we know reply_free_queue_depth is not a multiple of 16) */
-       queue_size = num_of_reply_frames;
-       queue_size += 16 - (queue_size % 16);
-       ioc->reply_free_queue_depth = queue_size;
-
-       /* reply descriptor post queue sizing */
-       /* this size should be the number of request frames + number of reply
-        * frames
-        */
+       /* reply free queue sizing - taking into account for 64 FW events */
+       ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
 
-       queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1;
-       /* round up to 16 byte boundary */
-       if (queue_size % 16)
-               queue_size += 16 - (queue_size % 16);
-
-       /* check against IOC maximum reply post queue depth */
-       if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) {
-               queue_diff = queue_size -
-                   facts->MaxReplyDescriptorPostQueueDepth;
-
-               /* round queue_diff up to multiple of 16 */
-               if (queue_diff % 16)
-                       queue_diff += 16 - (queue_diff % 16);
-
-               /* adjust hba_queue_depth, reply_free_queue_depth,
-                * and queue_size
-                */
-               ioc->hba_queue_depth -= (queue_diff / 2);
-               ioc->reply_free_queue_depth -= (queue_diff / 2);
-               queue_size = facts->MaxReplyDescriptorPostQueueDepth;
+       /* align the reply post queue on the next 16 count boundary */
+       if (!ioc->reply_free_queue_depth % 16)
+               ioc->reply_post_queue_depth = ioc->reply_free_queue_depth + 16;
+       else
+               ioc->reply_post_queue_depth = ioc->reply_free_queue_depth +
+                               32 - (ioc->reply_free_queue_depth % 16);
+       if (ioc->reply_post_queue_depth >
+           facts->MaxReplyDescriptorPostQueueDepth) {
+               ioc->reply_post_queue_depth = min_t(u16,
+                   (facts->MaxReplyDescriptorPostQueueDepth -
+                   (facts->MaxReplyDescriptorPostQueueDepth % 16)),
+                   (ioc->hba_queue_depth - (ioc->hba_queue_depth % 16)));
+               ioc->reply_free_queue_depth = ioc->reply_post_queue_depth - 16;
+               ioc->hba_queue_depth = ioc->reply_free_queue_depth - 64;
        }
-       ioc->reply_post_queue_depth = queue_size;
+
 
        dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: "
            "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), "
@@ -2366,15 +2569,17 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc,  int sleep_flag)
            "depth(%d)\n", ioc->name, ioc->request,
            ioc->scsiio_depth));
 
-       /* loop till the allocation succeeds */
-       do {
-               sz = ioc->chain_depth * sizeof(struct chain_tracker);
-               ioc->chain_pages = get_order(sz);
-               ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
-                   GFP_KERNEL, ioc->chain_pages);
-               if (ioc->chain_lookup == NULL)
-                       ioc->chain_depth -= 100;
-       } while (ioc->chain_lookup == NULL);
+       ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH);
+       sz = ioc->chain_depth * sizeof(struct chain_tracker);
+       ioc->chain_pages = get_order(sz);
+
+       ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
+           GFP_KERNEL, ioc->chain_pages);
+       if (!ioc->chain_lookup) {
+               printk(MPT2SAS_ERR_FMT "chain_lookup: get_free_pages failed, "
+                   "sz(%d)\n", ioc->name, (int)sz);
+               goto out;
+       }
        ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev,
            ioc->request_sz, 16, 0);
        if (!ioc->chain_dma_pool) {
@@ -2498,7 +2703,12 @@ chain_done:
        total_sz += sz;
 
        /* reply post queue, 16 byte align */
-       sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t);
+       reply_post_free_sz = ioc->reply_post_queue_depth *
+           sizeof(Mpi2DefaultReplyDescriptor_t);
+       if (_base_is_controller_msix_enabled(ioc))
+               sz = reply_post_free_sz * ioc->reply_queue_count;
+       else
+               sz = reply_post_free_sz;
        ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
            ioc->pdev, sz, 16, 0);
        if (!ioc->reply_post_free_dma_pool) {
@@ -2968,8 +3178,8 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc,
        if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
            mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
                ioc->ioc_link_reset_in_progress = 1;
-       mpt2sas_base_put_smid_default(ioc, smid);
        init_completion(&ioc->base_cmds.done);
+       mpt2sas_base_put_smid_default(ioc, smid);
        timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
            msecs_to_jiffies(10000));
        if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
@@ -3070,8 +3280,8 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
        request = mpt2sas_base_get_msg_frame(ioc, smid);
        ioc->base_cmds.smid = smid;
        memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
-       mpt2sas_base_put_smid_default(ioc, smid);
        init_completion(&ioc->base_cmds.done);
+       mpt2sas_base_put_smid_default(ioc, smid);
        timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
            msecs_to_jiffies(10000));
        if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
@@ -3186,6 +3396,7 @@ _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        facts->MaxChainDepth = mpi_reply.MaxChainDepth;
        facts->WhoInit = mpi_reply.WhoInit;
        facts->NumberOfPorts = mpi_reply.NumberOfPorts;
+       facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors;
        facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit);
        facts->MaxReplyDescriptorPostQueueDepth =
            le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth);
@@ -3243,7 +3454,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION);
        mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
 
-
+       if (_base_is_controller_msix_enabled(ioc))
+               mpi_request.HostMSIxVectors = ioc->reply_queue_count;
        mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4);
        mpi_request.ReplyDescriptorPostQueueDepth =
            cpu_to_le16(ioc->reply_post_queue_depth);
@@ -3301,6 +3513,58 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        return 0;
 }
 
+/**
+ * mpt2sas_port_enable_done - command completion routine for port enable
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
+ */
+u8
+mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+       u32 reply)
+{
+       MPI2DefaultReply_t *mpi_reply;
+       u16 ioc_status;
+
+       mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
+               return 1;
+
+       if (ioc->port_enable_cmds.status == MPT2_CMD_NOT_USED)
+               return 1;
+
+       ioc->port_enable_cmds.status |= MPT2_CMD_COMPLETE;
+       if (mpi_reply) {
+               ioc->port_enable_cmds.status |= MPT2_CMD_REPLY_VALID;
+               memcpy(ioc->port_enable_cmds.reply, mpi_reply,
+                   mpi_reply->MsgLength*4);
+       }
+       ioc->port_enable_cmds.status &= ~MPT2_CMD_PENDING;
+
+       ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+               ioc->port_enable_failed = 1;
+
+       if (ioc->is_driver_loading) {
+               if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+                       mpt2sas_port_enable_complete(ioc);
+                       return 1;
+               } else {
+                       ioc->start_scan_failed = ioc_status;
+                       ioc->start_scan = 0;
+                       return 1;
+               }
+       }
+       complete(&ioc->port_enable_cmds.done);
+       return 1;
+}
+
+
 /**
  * _base_send_port_enable - send port_enable(discovery stuff) to firmware
  * @ioc: per adapter object
@@ -3312,66 +3576,150 @@ static int
 _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
 {
        Mpi2PortEnableRequest_t *mpi_request;
-       u32 ioc_state;
+       Mpi2PortEnableReply_t *mpi_reply;
        unsigned long timeleft;
        int r = 0;
        u16 smid;
+       u16 ioc_status;
 
        printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
 
-       if (ioc->base_cmds.status & MPT2_CMD_PENDING) {
+       if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
                printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
                    ioc->name, __func__);
                return -EAGAIN;
        }
 
-       smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx);
+       smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
        if (!smid) {
                printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
                    ioc->name, __func__);
                return -EAGAIN;
        }
 
-       ioc->base_cmds.status = MPT2_CMD_PENDING;
+       ioc->port_enable_cmds.status = MPT2_CMD_PENDING;
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
-       ioc->base_cmds.smid = smid;
+       ioc->port_enable_cmds.smid = smid;
        memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
        mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
-       mpi_request->VF_ID = 0; /* TODO */
-       mpi_request->VP_ID = 0;
 
+       init_completion(&ioc->port_enable_cmds.done);
        mpt2sas_base_put_smid_default(ioc, smid);
-       init_completion(&ioc->base_cmds.done);
-       timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+       timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done,
            300*HZ);
-       if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
+       if (!(ioc->port_enable_cmds.status & MPT2_CMD_COMPLETE)) {
                printk(MPT2SAS_ERR_FMT "%s: timeout\n",
                    ioc->name, __func__);
                _debug_dump_mf(mpi_request,
                    sizeof(Mpi2PortEnableRequest_t)/4);
-               if (ioc->base_cmds.status & MPT2_CMD_RESET)
+               if (ioc->port_enable_cmds.status & MPT2_CMD_RESET)
                        r = -EFAULT;
                else
                        r = -ETIME;
                goto out;
-       } else
-               dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n",
-                   ioc->name, __func__));
+       }
+       mpi_reply = ioc->port_enable_cmds.reply;
 
-       ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL,
-           60, sleep_flag);
-       if (ioc_state) {
-               printk(MPT2SAS_ERR_FMT "%s: failed going to operational state "
-                   " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state);
+       ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+               printk(MPT2SAS_ERR_FMT "%s: failed with (ioc_status=0x%08x)\n",
+                   ioc->name, __func__, ioc_status);
                r = -EFAULT;
+               goto out;
        }
  out:
-       ioc->base_cmds.status = MPT2_CMD_NOT_USED;
-       printk(MPT2SAS_INFO_FMT "port enable: %s\n",
-           ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
+       ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED;
+       printk(MPT2SAS_INFO_FMT "port enable: %s\n", ioc->name, ((r == 0) ?
+           "SUCCESS" : "FAILED"));
        return r;
 }
 
+/**
+ * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply)
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc)
+{
+       Mpi2PortEnableRequest_t *mpi_request;
+       u16 smid;
+
+       printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name);
+
+       if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
+               printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n",
+                   ioc->name, __func__);
+               return -EAGAIN;
+       }
+
+       smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+       if (!smid) {
+               printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+                   ioc->name, __func__);
+               return -EAGAIN;
+       }
+
+       ioc->port_enable_cmds.status = MPT2_CMD_PENDING;
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+       ioc->port_enable_cmds.smid = smid;
+       memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+       mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+       mpt2sas_base_put_smid_default(ioc, smid);
+       return 0;
+}
+
+/**
+ * _base_determine_wait_on_discovery - desposition
+ * @ioc: per adapter object
+ *
+ * Decide whether to wait on discovery to complete. Used to either
+ * locate boot device, or report volumes ahead of physical devices.
+ *
+ * Returns 1 for wait, 0 for don't wait
+ */
+static int
+_base_determine_wait_on_discovery(struct MPT2SAS_ADAPTER *ioc)
+{
+       /* We wait for discovery to complete if IR firmware is loaded.
+        * The sas topology events arrive before PD events, so we need time to
+        * turn on the bit in ioc->pd_handles to indicate PD
+        * Also, it maybe required to report Volumes ahead of physical
+        * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set.
+        */
+       if (ioc->ir_firmware)
+               return 1;
+
+       /* if no Bios, then we don't need to wait */
+       if (!ioc->bios_pg3.BiosVersion)
+               return 0;
+
+       /* Bios is present, then we drop down here.
+        *
+        * If there any entries in the Bios Page 2, then we wait
+        * for discovery to complete.
+        */
+
+       /* Current Boot Device */
+       if ((ioc->bios_pg2.CurrentBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+       /* Request Boot Device */
+          (ioc->bios_pg2.ReqBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+       /* Alternate Request Boot Device */
+          (ioc->bios_pg2.ReqAltBootDeviceForm &
+           MPI2_BIOSPAGE2_FORM_MASK) ==
+           MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED)
+               return 0;
+
+       return 1;
+}
+
+
 /**
  * _base_unmask_events - turn on notification for this event
  * @ioc: per adapter object
@@ -3440,8 +3788,8 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
                mpi_request->EventMasks[i] =
                    cpu_to_le32(ioc->event_masks[i]);
-       mpt2sas_base_put_smid_default(ioc, smid);
        init_completion(&ioc->base_cmds.done);
+       mpt2sas_base_put_smid_default(ioc, smid);
        timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
        if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) {
                printk(MPT2SAS_ERR_FMT "%s: timeout\n",
@@ -3512,9 +3860,6 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        u32 hcb_size;
 
        printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name);
-
-       _base_save_msix_table(ioc);
-
        drsprintk(ioc, printk(MPT2SAS_INFO_FMT "clear interrupts\n",
            ioc->name));
 
@@ -3610,7 +3955,6 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
                goto out;
        }
 
-       _base_restore_msix_table(ioc);
        printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name);
        return 0;
 
@@ -3691,6 +4035,9 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        u16 smid;
        struct _tr_list *delayed_tr, *delayed_tr_next;
        u8 hide_flag;
+       struct adapter_reply_queue *reply_q;
+       long reply_post_free;
+       u32 reply_post_free_sz;
 
        dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
            __func__));
@@ -3756,21 +4103,47 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
            ioc->reply_sz)
                ioc->reply_free[i] = cpu_to_le32(reply_address);
 
+       /* initialize reply queues */
+       if (ioc->is_driver_loading)
+               _base_assign_reply_queues(ioc);
+
        /* initialize Reply Post Free Queue */
-       for (i = 0; i < ioc->reply_post_queue_depth; i++)
-               ioc->reply_post_free[i].Words = cpu_to_le64(ULLONG_MAX);
+       reply_post_free = (long)ioc->reply_post_free;
+       reply_post_free_sz = ioc->reply_post_queue_depth *
+           sizeof(Mpi2DefaultReplyDescriptor_t);
+       list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+               reply_q->reply_post_host_index = 0;
+               reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *)
+                   reply_post_free;
+               for (i = 0; i < ioc->reply_post_queue_depth; i++)
+                       reply_q->reply_post_free[i].Words =
+                                                       cpu_to_le64(ULLONG_MAX);
+               if (!_base_is_controller_msix_enabled(ioc))
+                       goto skip_init_reply_post_free_queue;
+               reply_post_free += reply_post_free_sz;
+       }
+ skip_init_reply_post_free_queue:
 
        r = _base_send_ioc_init(ioc, sleep_flag);
        if (r)
                return r;
 
-       /* initialize the index's */
+       /* initialize reply free host index */
        ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1;
-       ioc->reply_post_host_index = 0;
        writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex);
-       writel(0, &ioc->chip->ReplyPostHostIndex);
+
+       /* initialize reply post host index */
+       list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+               writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT,
+                   &ioc->chip->ReplyPostHostIndex);
+               if (!_base_is_controller_msix_enabled(ioc))
+                       goto skip_init_reply_post_host_index;
+       }
+
+ skip_init_reply_post_host_index:
 
        _base_unmask_interrupts(ioc);
+
        r = _base_event_notification(ioc, sleep_flag);
        if (r)
                return r;
@@ -3778,22 +4151,19 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        if (sleep_flag == CAN_SLEEP)
                _base_static_config_pages(ioc);
 
-       if (ioc->wait_for_port_enable_to_complete && ioc->is_warpdrive) {
-               if (ioc->manu_pg10.OEMIdentifier  == 0x80) {
+
+       if (ioc->is_driver_loading) {
+               if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier
+                   == 0x80) {
                        hide_flag = (u8) (ioc->manu_pg10.OEMSpecificFlags0 &
                            MFG_PAGE10_HIDE_SSDS_MASK);
                        if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK)
                                ioc->mfg_pg10_hide_flag = hide_flag;
                }
+               ioc->wait_for_discovery_to_complete =
+                   _base_determine_wait_on_discovery(ioc);
+               return r; /* scan_start and scan_finished support */
        }
-
-       if (ioc->wait_for_port_enable_to_complete) {
-               if (diag_buffer_enable != 0)
-                       mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
-               if (disable_discovery > 0)
-                       return r;
-       }
-
        r = _base_send_port_enable(ioc, sleep_flag);
        if (r)
                return r;
@@ -3819,14 +4189,10 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
        ioc->shost_recovery = 1;
        _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
        ioc->shost_recovery = 0;
-       if (ioc->pci_irq) {
-               synchronize_irq(pdev->irq);
-               free_irq(ioc->pci_irq, ioc);
-       }
+       _base_free_irq(ioc);
        _base_disable_msix(ioc);
        if (ioc->chip_phys)
                iounmap(ioc->chip);
-       ioc->pci_irq = -1;
        ioc->chip_phys = 0;
        pci_release_selected_regions(ioc->pdev, ioc->bars);
        pci_disable_pcie_error_reporting(pdev);
@@ -3844,13 +4210,49 @@ int
 mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
 {
        int r, i;
+       int cpu_id, last_cpu_id = 0;
 
        dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
            __func__));
 
+       /* setup cpu_msix_table */
+       ioc->cpu_count = num_online_cpus();
+       for_each_online_cpu(cpu_id)
+               last_cpu_id = cpu_id;
+       ioc->cpu_msix_table_sz = last_cpu_id + 1;
+       ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL);
+       ioc->reply_queue_count = 1;
+       if (!ioc->cpu_msix_table) {
+               dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for "
+                   "cpu_msix_table failed!!!\n", ioc->name));
+               r = -ENOMEM;
+               goto out_free_resources;
+       }
+
+       if (ioc->is_warpdrive) {
+               ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz,
+                   sizeof(resource_size_t *), GFP_KERNEL);
+               if (!ioc->reply_post_host_index) {
+                       dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation "
+                               "for cpu_msix_table failed!!!\n", ioc->name));
+                       r = -ENOMEM;
+                       goto out_free_resources;
+               }
+       }
+
        r = mpt2sas_base_map_resources(ioc);
        if (r)
-               return r;
+               goto out_free_resources;
+
+       if (ioc->is_warpdrive) {
+               ioc->reply_post_host_index[0] =
+                   (resource_size_t *)&ioc->chip->ReplyPostHostIndex;
+
+               for (i = 1; i < ioc->cpu_msix_table_sz; i++)
+                       ioc->reply_post_host_index[i] = (resource_size_t *)
+                       ((u8 *)&ioc->chip->Doorbell + (0x4000 + ((i - 1)
+                       * 4)));
+       }
 
        pci_set_drvdata(ioc->pdev, ioc->shost);
        r = _base_get_ioc_facts(ioc, CAN_SLEEP);
@@ -3898,6 +4300,10 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
        ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
        ioc->base_cmds.status = MPT2_CMD_NOT_USED;
 
+       /* port_enable command bits */
+       ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+       ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED;
+
        /* transport internal command bits */
        ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
        ioc->transport_cmds.status = MPT2_CMD_NOT_USED;
@@ -3939,8 +4345,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
                goto out_free_resources;
        }
 
-       init_completion(&ioc->shost_recovery_done);
-
        for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
                ioc->event_masks[i] = -1;
 
@@ -3963,7 +4367,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
                _base_update_missing_delay(ioc, missing_delay[0],
                    missing_delay[1]);
 
-       mpt2sas_base_start_watchdog(ioc);
        return 0;
 
  out_free_resources:
@@ -3972,12 +4375,16 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
        mpt2sas_base_free_resources(ioc);
        _base_release_memory_pools(ioc);
        pci_set_drvdata(ioc->pdev, NULL);
+       kfree(ioc->cpu_msix_table);
+       if (ioc->is_warpdrive)
+               kfree(ioc->reply_post_host_index);
        kfree(ioc->pd_handles);
        kfree(ioc->tm_cmds.reply);
        kfree(ioc->transport_cmds.reply);
        kfree(ioc->scsih_cmds.reply);
        kfree(ioc->config_cmds.reply);
        kfree(ioc->base_cmds.reply);
+       kfree(ioc->port_enable_cmds.reply);
        kfree(ioc->ctl_cmds.reply);
        kfree(ioc->ctl_cmds.sense);
        kfree(ioc->pfacts);
@@ -4009,11 +4416,15 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc)
        mpt2sas_base_free_resources(ioc);
        _base_release_memory_pools(ioc);
        pci_set_drvdata(ioc->pdev, NULL);
+       kfree(ioc->cpu_msix_table);
+       if (ioc->is_warpdrive)
+               kfree(ioc->reply_post_host_index);
        kfree(ioc->pd_handles);
        kfree(ioc->pfacts);
        kfree(ioc->ctl_cmds.reply);
        kfree(ioc->ctl_cmds.sense);
        kfree(ioc->base_cmds.reply);
+       kfree(ioc->port_enable_cmds.reply);
        kfree(ioc->tm_cmds.reply);
        kfree(ioc->transport_cmds.reply);
        kfree(ioc->scsih_cmds.reply);
@@ -4055,6 +4466,20 @@ _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
                        mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid);
                        complete(&ioc->base_cmds.done);
                }
+               if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) {
+                       ioc->port_enable_failed = 1;
+                       ioc->port_enable_cmds.status |= MPT2_CMD_RESET;
+                       mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid);
+                       if (ioc->is_driver_loading) {
+                               ioc->start_scan_failed =
+                                   MPI2_IOCSTATUS_INTERNAL_ERROR;
+                               ioc->start_scan = 0;
+                               ioc->port_enable_cmds.status =
+                                               MPT2_CMD_NOT_USED;
+                       } else
+                               complete(&ioc->port_enable_cmds.done);
+
+               }
                if (ioc->config_cmds.status & MPT2_CMD_PENDING) {
                        ioc->config_cmds.status |= MPT2_CMD_RESET;
                        mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid);
@@ -4120,7 +4545,6 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
 {
        int r;
        unsigned long flags;
-       u8 pe_complete = ioc->wait_for_port_enable_to_complete;
 
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name,
            __func__));
@@ -4129,7 +4553,7 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
                printk(MPT2SAS_ERR_FMT "%s: pci error recovery reset\n",
                    ioc->name, __func__);
                r = 0;
-               goto out;
+               goto out_unlocked;
        }
 
        if (mpt2sas_fwfault_debug)
@@ -4167,7 +4591,8 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
        /* If this hard reset is called while port enable is active, then
         * there is no reason to call make_ioc_operational
         */
-       if (pe_complete) {
+       if (ioc->is_driver_loading && ioc->port_enable_failed) {
+               ioc->remove_host = 1;
                r = -EFAULT;
                goto out;
        }
@@ -4181,10 +4606,10 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
        spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
        ioc->ioc_reset_in_progress_status = r;
        ioc->shost_recovery = 0;
-       complete(&ioc->shost_recovery_done);
        spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
        mutex_unlock(&ioc->reset_in_progress_mutex);
 
+ out_unlocked:
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name,
            __func__));
        return r;