]> git.karo-electronics.de Git - linux-beck.git/commitdiff
[SCSI] megaraid_sas: Add Dell PowerEdge VRTX SR-IOV VF support
authoradam radford <aradford@gmail.com>
Mon, 10 Mar 2014 09:51:56 +0000 (02:51 -0700)
committerJames Bottomley <JBottomley@Parallels.com>
Sat, 15 Mar 2014 17:19:21 +0000 (10:19 -0700)
The following patch for megaraid_sas adds Dell PowerEdge VRTS SR-IOV VF
support (Device ID 0x002f).

This patch has some > 80 column lines that need to be left in place
for code readability purposes.

Signed-off-by: Adam Radford <aradford@gmail.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/megaraid/megaraid_sas.h
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/megaraid/megaraid_sas_fusion.c
drivers/scsi/megaraid/megaraid_sas_fusion.h

index a80e13ef9ffa74a4f858bd2c884ebdfba6f10a16..66be91249bfa4dfca780c6c493f67bab663ba801 100644 (file)
@@ -48,6 +48,7 @@
 #define        PCI_DEVICE_ID_LSI_SAS0073SKINNY         0x0073
 #define        PCI_DEVICE_ID_LSI_SAS0071SKINNY         0x0071
 #define        PCI_DEVICE_ID_LSI_FUSION                0x005b
+#define PCI_DEVICE_ID_LSI_PLASMA               0x002f
 #define PCI_DEVICE_ID_LSI_INVADER              0x005d
 #define PCI_DEVICE_ID_LSI_FURY                 0x005f
 
@@ -559,7 +560,8 @@ struct megasas_ctrl_info {
                u8 PCIE:1;
                u8 iSCSI:1;
                u8 SAS_3G:1;
-               u8 reserved_0:4;
+               u8 SRIOV:1;
+               u8 reserved_0:3;
                u8 reserved_1[6];
                u8 port_count;
                u64 port_addr[8];
@@ -839,7 +841,12 @@ struct megasas_ctrl_info {
 
        struct {                                /*7A4h */
 #if   defined(__BIG_ENDIAN_BITFIELD)
-               u32     reserved:11;
+               u32     reserved:5;
+               u32     activePassive:2;
+               u32     supportConfigAutoBalance:1;
+               u32     mpio:1;
+               u32     supportDataLDonSSCArray:1;
+               u32     supportPointInTimeProgress:1;
                u32     supportUnevenSpans:1;
                u32     dedicatedHotSparesLimited:1;
                u32     headlessMode:1;
@@ -886,7 +893,12 @@ struct megasas_ctrl_info {
 
 
                u32     supportUnevenSpans:1;
-               u32     reserved:11;
+               u32     supportPointInTimeProgress:1;
+               u32     supportDataLDonSSCArray:1;
+               u32     mpio:1;
+               u32     supportConfigAutoBalance:1;
+               u32     activePassive:2;
+               u32     reserved:5;
 #endif
        } adapterOperations2;
 
@@ -914,8 +926,14 @@ struct megasas_ctrl_info {
        } cluster;
 
        char clusterId[16];                     /*7D4h */
+       struct {
+               u8  maxVFsSupported;            /*0x7E4*/
+               u8  numVFsEnabled;              /*0x7E5*/
+               u8  requestorId;                /*0x7E6 0:PF, 1:VF1, 2:VF2*/
+               u8  reserved;                   /*0x7E7*/
+       } iov;
 
-       u8          pad[0x800-0x7E4];           /*7E4 */
+       u8          pad[0x800-0x7E8];           /*0x7E8 pad to 2k */
 } __packed;
 
 /*
@@ -986,7 +1004,9 @@ struct megasas_ctrl_info {
 
 #define MFI_OB_INTR_STATUS_MASK                        0x00000002
 #define MFI_POLL_TIMEOUT_SECS                  60
-
+#define MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF    (5 * HZ)
+#define MEGASAS_OCR_SETTLE_TIME_VF             (1000 * 30)
+#define MEGASAS_ROUTINE_WAIT_TIME_VF           300
 #define MFI_REPLY_1078_MESSAGE_INTERRUPT       0x80000000
 #define MFI_REPLY_GEN2_MESSAGE_INTERRUPT       0x00000001
 #define MFI_GEN2_ENABLE_INTERRUPT_MASK         (0x00000001 | 0x00000004)
@@ -1529,6 +1549,12 @@ struct megasas_instance {
        dma_addr_t producer_h;
        u32 *consumer;
        dma_addr_t consumer_h;
+       struct MR_LD_VF_AFFILIATION *vf_affiliation;
+       dma_addr_t vf_affiliation_h;
+       struct MR_LD_VF_AFFILIATION_111 *vf_affiliation_111;
+       dma_addr_t vf_affiliation_111_h;
+       struct MR_CTRL_HB_HOST_MEM *hb_host_mem;
+       dma_addr_t hb_host_mem_h;
 
        u32 *reply_queue;
        dma_addr_t reply_queue_h;
@@ -1604,10 +1630,73 @@ struct megasas_instance {
        unsigned long bar;
        long reset_flags;
        struct mutex reset_mutex;
+       struct timer_list sriov_heartbeat_timer;
+       char skip_heartbeat_timer_del;
+       u8 requestorId;
+       u64 initiator_sas_address;
+       u64 ld_sas_address[64];
+       char PlasmaFW111;
+       char mpio;
        int throttlequeuedepth;
        u8 mask_interrupts;
        u8 is_imr;
 };
+struct MR_LD_VF_MAP {
+       u32 size;
+       union MR_LD_REF ref;
+       u8 ldVfCount;
+       u8 reserved[6];
+       u8 policy[1];
+};
+
+struct MR_LD_VF_AFFILIATION {
+       u32 size;
+       u8 ldCount;
+       u8 vfCount;
+       u8 thisVf;
+       u8 reserved[9];
+       struct MR_LD_VF_MAP map[1];
+};
+
+/* Plasma 1.11 FW backward compatibility structures */
+#define IOV_111_OFFSET 0x7CE
+#define MAX_VIRTUAL_FUNCTIONS 8
+
+struct IOV_111 {
+       u8 maxVFsSupported;
+       u8 numVFsEnabled;
+       u8 requestorId;
+       u8 reserved[5];
+};
+
+struct MR_LD_VF_MAP_111 {
+       u8 targetId;
+       u8 reserved[3];
+       u8 policy[MAX_VIRTUAL_FUNCTIONS];
+};
+
+struct MR_LD_VF_AFFILIATION_111 {
+       u8 vdCount;
+       u8 vfCount;
+       u8 thisVf;
+       u8 reserved[5];
+       struct MR_LD_VF_MAP_111 map[MAX_LOGICAL_DRIVES];
+};
+
+struct MR_CTRL_HB_HOST_MEM {
+       struct {
+               u32 fwCounter;  /* Firmware heart beat counter */
+               struct {
+                       u32 debugmode:1; /* 1=Firmware is in debug mode.
+                                           Heart beat will not be updated. */
+                       u32 reserved:31;
+               } debug;
+               u32 reserved_fw[6];
+               u32 driverCounter; /* Driver heart beat counter.  0x20 */
+               u32 reserved_driver[7];
+       } HB;
+       u8 pad[0x400-0x40];
+};
 
 enum {
        MEGASAS_HBA_OPERATIONAL                 = 0,
@@ -1615,6 +1704,7 @@ enum {
        MEGASAS_ADPRESET_SM_FW_RESET_SUCCESS    = 2,
        MEGASAS_ADPRESET_SM_OPERATIONAL         = 3,
        MEGASAS_HW_CRITICAL_ERROR               = 4,
+       MEGASAS_ADPRESET_SM_POLLING             = 5,
        MEGASAS_ADPRESET_INPROG_SIGN            = 0xDEADDEAD,
 };
 
index c6203e677ab36fc675b4e699b00c3e7999e828c0..1fb7ab6e77bdd0107ad4567f6731fc54f5bfd6ba 100644 (file)
@@ -75,6 +75,10 @@ static unsigned int msix_vectors;
 module_param(msix_vectors, int, S_IRUGO);
 MODULE_PARM_DESC(msix_vectors, "MSI-X max vector count. Default: Set by FW");
 
+static int allow_vf_ioctls;
+module_param(allow_vf_ioctls, int, S_IRUGO);
+MODULE_PARM_DESC(allow_vf_ioctls, "Allow ioctls in SR-IOV VF mode. Default: 0");
+
 static int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH;
 module_param(throttlequeuedepth, int, S_IRUGO);
 MODULE_PARM_DESC(throttlequeuedepth,
@@ -122,6 +126,8 @@ static struct pci_device_id megasas_pci_table[] = {
        /* xscale IOP */
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)},
        /* Fusion */
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_PLASMA)},
+       /* Plasma */
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INVADER)},
        /* Invader */
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FURY)},
@@ -132,7 +138,7 @@ static struct pci_device_id megasas_pci_table[] = {
 MODULE_DEVICE_TABLE(pci, megasas_pci_table);
 
 static int megasas_mgmt_majorno;
-static struct megasas_mgmt_info megasas_mgmt_info;
+struct megasas_mgmt_info megasas_mgmt_info;
 static struct fasync_struct *megasas_async_queue;
 static DEFINE_MUTEX(megasas_async_queue_mutex);
 
@@ -171,10 +177,15 @@ megasas_get_map_info(struct megasas_instance *instance);
 int
 megasas_sync_map_info(struct megasas_instance *instance);
 int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd);
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+       int seconds);
 void megasas_reset_reply_desc(struct megasas_instance *instance);
-int megasas_reset_fusion(struct Scsi_Host *shost);
+int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout);
 void megasas_fusion_ocr_wq(struct work_struct *work);
+static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance,
+                                        int initial);
+int megasas_check_mpio_paths(struct megasas_instance *instance,
+                            struct scsi_cmnd *scmd);
 
 void
 megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
@@ -224,6 +235,7 @@ megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
        cmd->scmd = NULL;
        cmd->frame_count = 0;
        if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) &&
+           (instance->pdev->device != PCI_DEVICE_ID_LSI_PLASMA) &&
            (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) &&
            (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) &&
            (reset_devices))
@@ -877,6 +889,7 @@ extern struct megasas_instance_template megasas_instance_template_fusion;
 int
 megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
 {
+       int seconds;
 
        struct megasas_header *frame_hdr = &cmd->frame->hdr;
 
@@ -891,7 +904,11 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
        /*
         * Wait for cmd_status to change
         */
-       return wait_and_poll(instance, cmd);
+       if (instance->requestorId)
+               seconds = MEGASAS_ROUTINE_WAIT_TIME_VF;
+       else
+               seconds = MFI_POLL_TIMEOUT_SECS;
+       return wait_and_poll(instance, cmd, seconds);
 }
 
 /**
@@ -1532,9 +1549,23 @@ megasas_queue_command_lck(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd
 
        spin_lock_irqsave(&instance->hba_lock, flags);
 
+       /* Check for an mpio path and adjust behavior */
+       if (instance->adprecovery == MEGASAS_ADPRESET_SM_INFAULT) {
+               if (megasas_check_mpio_paths(instance, scmd) ==
+                   (DID_RESET << 16)) {
+                       spin_unlock_irqrestore(&instance->hba_lock, flags);
+                       return SCSI_MLQUEUE_HOST_BUSY;
+               } else {
+                       spin_unlock_irqrestore(&instance->hba_lock, flags);
+                       scmd->result = DID_NO_CONNECT << 16;
+                       done(scmd);
+                       return 0;
+               }
+       }
+
        if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
                spin_unlock_irqrestore(&instance->hba_lock, flags);
-               scmd->result = DID_ERROR << 16;
+               scmd->result = DID_NO_CONNECT << 16;
                done(scmd);
                return 0;
        }
@@ -1659,9 +1690,14 @@ void megaraid_sas_kill_hba(struct megasas_instance *instance)
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+           (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) {
                writel(MFI_STOP_ADP, &instance->reg_set->doorbell);
+               /* Flush */
+               readl(&instance->reg_set->doorbell);
+               if (instance->mpio && instance->requestorId)
+                       memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
        } else {
                writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell);
        }
@@ -1748,6 +1784,25 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
        megasas_check_and_restore_queue_depth(instance);
 }
 
+/**
+ * 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
+ *
+ */
+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);
+}
+
 static void
 megasas_internal_reset_defer_cmds(struct megasas_instance *instance);
 
@@ -1770,6 +1825,295 @@ void megasas_do_ocr(struct megasas_instance *instance)
        process_fw_state_change_wq(&instance->work_init);
 }
 
+/* This function will get the current SR-IOV LD/VF affiliation */
+static int megasas_get_ld_vf_affiliation(struct megasas_instance *instance,
+       int initial)
+{
+       struct megasas_cmd *cmd;
+       struct megasas_dcmd_frame *dcmd;
+       struct MR_LD_VF_AFFILIATION *new_affiliation = NULL;
+       struct MR_LD_VF_AFFILIATION_111 *new_affiliation_111 = NULL;
+       struct MR_LD_VF_MAP *newmap = NULL, *savedmap = NULL;
+       dma_addr_t new_affiliation_h;
+       dma_addr_t new_affiliation_111_h;
+       int ld, retval = 0;
+       u8 thisVf;
+
+       cmd = megasas_get_cmd(instance);
+
+       if (!cmd) {
+               printk(KERN_DEBUG "megasas: megasas_get_ld_vf_"
+                      "affiliation: Failed to get cmd for scsi%d.\n",
+                       instance->host->host_no);
+               return -ENOMEM;
+       }
+
+       dcmd = &cmd->frame->dcmd;
+
+       if (!instance->vf_affiliation && !instance->vf_affiliation_111) {
+               printk(KERN_WARNING "megasas: SR-IOV: Couldn't get LD/VF "
+                      "affiliation for scsi%d.\n", instance->host->host_no);
+               megasas_return_cmd(instance, cmd);
+               return -ENOMEM;
+       }
+
+       if (initial)
+               if (instance->PlasmaFW111)
+                       memset(instance->vf_affiliation_111, 0,
+                              sizeof(struct MR_LD_VF_AFFILIATION_111));
+               else
+                       memset(instance->vf_affiliation, 0,
+                              (MAX_LOGICAL_DRIVES + 1) *
+                              sizeof(struct MR_LD_VF_AFFILIATION));
+       else {
+               if (instance->PlasmaFW111)
+                       new_affiliation_111 =
+                               pci_alloc_consistent(instance->pdev,
+                                                    sizeof(struct MR_LD_VF_AFFILIATION_111),
+                                                    &new_affiliation_111_h);
+               else
+                       new_affiliation =
+                               pci_alloc_consistent(instance->pdev,
+                                                    (MAX_LOGICAL_DRIVES + 1) *
+                                                    sizeof(struct MR_LD_VF_AFFILIATION),
+                                                    &new_affiliation_h);
+               if (!new_affiliation && !new_affiliation_111) {
+                       printk(KERN_DEBUG "megasas: SR-IOV: Couldn't allocate "
+                              "memory for new affiliation for scsi%d.\n",
+                               instance->host->host_no);
+                       megasas_return_cmd(instance, cmd);
+                       return -ENOMEM;
+               }
+               if (instance->PlasmaFW111)
+                       memset(new_affiliation_111, 0,
+                              sizeof(struct MR_LD_VF_AFFILIATION_111));
+               else
+                       memset(new_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) *
+                              sizeof(struct MR_LD_VF_AFFILIATION));
+       }
+
+       memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+       dcmd->cmd = MFI_CMD_DCMD;
+       dcmd->cmd_status = 0xFF;
+       dcmd->sge_count = 1;
+       dcmd->flags = MFI_FRAME_DIR_BOTH;
+       dcmd->timeout = 0;
+       dcmd->pad_0 = 0;
+       if (instance->PlasmaFW111) {
+               dcmd->data_xfer_len = sizeof(struct MR_LD_VF_AFFILIATION_111);
+               dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111;
+       } else {
+               dcmd->data_xfer_len = (MAX_LOGICAL_DRIVES + 1) *
+                       sizeof(struct MR_LD_VF_AFFILIATION);
+               dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS;
+       }
+
+       if (initial) {
+               if (instance->PlasmaFW111)
+                       dcmd->sgl.sge32[0].phys_addr =
+                         instance->vf_affiliation_111_h;
+               else
+                       dcmd->sgl.sge32[0].phys_addr =
+                         instance->vf_affiliation_h;
+       } else {
+               if (instance->PlasmaFW111)
+                       dcmd->sgl.sge32[0].phys_addr = new_affiliation_111_h;
+               else
+                       dcmd->sgl.sge32[0].phys_addr = new_affiliation_h;
+       }
+       if (instance->PlasmaFW111)
+               dcmd->sgl.sge32[0].length =
+                 sizeof(struct MR_LD_VF_AFFILIATION_111);
+       else
+               dcmd->sgl.sge32[0].length = (MAX_LOGICAL_DRIVES + 1) *
+                       sizeof(struct MR_LD_VF_AFFILIATION);
+
+       printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for "
+              "scsi%d\n", instance->host->host_no);
+
+       megasas_issue_blocked_cmd(instance, cmd, 0);
+
+       if (dcmd->cmd_status) {
+               printk(KERN_WARNING "megasas: SR-IOV: LD/VF affiliation DCMD"
+                      " failed with status 0x%x for scsi%d.\n",
+                      dcmd->cmd_status, instance->host->host_no);
+               retval = 1; /* Do a scan if we couldn't get affiliation */
+               goto out;
+       }
+
+       if (!initial) {
+               if (instance->PlasmaFW111) {
+                       if (!new_affiliation_111->vdCount) {
+                               printk(KERN_WARNING "megasas: SR-IOV: Got new "
+                                      "LD/VF affiliation for passive path "
+                                      "for scsi%d.\n",
+                                       instance->host->host_no);
+                               retval = 1;
+                               goto out;
+                       }
+                       thisVf = new_affiliation_111->thisVf;
+                       for (ld = 0 ; ld < new_affiliation_111->vdCount; ld++)
+                               if (instance->vf_affiliation_111->map[ld].policy[thisVf] != new_affiliation_111->map[ld].policy[thisVf]) {
+                                       printk(KERN_WARNING "megasas: SR-IOV: "
+                                              "Got new LD/VF affiliation "
+                                              "for scsi%d.\n",
+                                               instance->host->host_no);
+                                       memcpy(instance->vf_affiliation_111,
+                                              new_affiliation_111,
+                                              sizeof(struct MR_LD_VF_AFFILIATION_111));
+                                       retval = 1;
+                                       goto out;
+                               }
+               } else {
+                       if (!new_affiliation->ldCount) {
+                               printk(KERN_WARNING "megasas: SR-IOV: Got new "
+                                      "LD/VF affiliation for passive "
+                                      "path for scsi%d.\n",
+                                      instance->host->host_no);
+                               retval = 1;
+                               goto out;
+                       }
+                       newmap = new_affiliation->map;
+                       savedmap = instance->vf_affiliation->map;
+                       thisVf = new_affiliation->thisVf;
+                       for (ld = 0 ; ld < new_affiliation->ldCount; ld++) {
+                               if (savedmap->policy[thisVf] !=
+                                   newmap->policy[thisVf]) {
+                                       printk(KERN_WARNING "megasas: SR-IOV: "
+                                              "Got new LD/VF affiliation "
+                                              "for scsi%d.\n",
+                                               instance->host->host_no);
+                                       memcpy(instance->vf_affiliation,
+                                              new_affiliation,
+                                              new_affiliation->size);
+                                       retval = 1;
+                                       goto out;
+                               }
+                               savedmap = (struct MR_LD_VF_MAP *)
+                                       ((unsigned char *)savedmap +
+                                        savedmap->size);
+                               newmap = (struct MR_LD_VF_MAP *)
+                                       ((unsigned char *)newmap +
+                                        newmap->size);
+                       }
+               }
+       }
+out:
+       if (new_affiliation) {
+               if (instance->PlasmaFW111)
+                       pci_free_consistent(instance->pdev,
+                                           sizeof(struct MR_LD_VF_AFFILIATION_111),
+                                           new_affiliation_111,
+                                           new_affiliation_111_h);
+               else
+                       pci_free_consistent(instance->pdev,
+                                           (MAX_LOGICAL_DRIVES + 1) *
+                                           sizeof(struct MR_LD_VF_AFFILIATION),
+                                           new_affiliation, new_affiliation_h);
+       }
+       megasas_return_cmd(instance, cmd);
+
+       return retval;
+}
+
+/* This function will tell FW to start the SR-IOV heartbeat */
+int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
+                                        int initial)
+{
+       struct megasas_cmd *cmd;
+       struct megasas_dcmd_frame *dcmd;
+       int retval = 0;
+
+       cmd = megasas_get_cmd(instance);
+
+       if (!cmd) {
+               printk(KERN_DEBUG "megasas: megasas_sriov_start_heartbeat: "
+                      "Failed to get cmd for scsi%d.\n",
+                      instance->host->host_no);
+               return -ENOMEM;
+       }
+
+       dcmd = &cmd->frame->dcmd;
+
+       if (initial) {
+               instance->hb_host_mem =
+                       pci_alloc_consistent(instance->pdev,
+                                            sizeof(struct MR_CTRL_HB_HOST_MEM),
+                                            &instance->hb_host_mem_h);
+               if (!instance->hb_host_mem) {
+                       printk(KERN_DEBUG "megasas: SR-IOV: Couldn't allocate"
+                              " memory for heartbeat host memory for "
+                              "scsi%d.\n", instance->host->host_no);
+                       retval = -ENOMEM;
+                       goto out;
+               }
+               memset(instance->hb_host_mem, 0,
+                      sizeof(struct MR_CTRL_HB_HOST_MEM));
+       }
+
+       memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+       dcmd->mbox.s[0] = sizeof(struct MR_CTRL_HB_HOST_MEM);
+       dcmd->cmd = MFI_CMD_DCMD;
+       dcmd->cmd_status = 0xFF;
+       dcmd->sge_count = 1;
+       dcmd->flags = MFI_FRAME_DIR_BOTH;
+       dcmd->timeout = 0;
+       dcmd->pad_0 = 0;
+       dcmd->data_xfer_len = sizeof(struct MR_CTRL_HB_HOST_MEM);
+       dcmd->opcode = MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC;
+       dcmd->sgl.sge32[0].phys_addr = instance->hb_host_mem_h;
+       dcmd->sgl.sge32[0].length = sizeof(struct MR_CTRL_HB_HOST_MEM);
+
+       printk(KERN_WARNING "megasas: SR-IOV: Starting heartbeat for scsi%d\n",
+              instance->host->host_no);
+
+       if (!megasas_issue_polled(instance, cmd)) {
+               retval = 0;
+       } else {
+               printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
+                      "_MEM_ALLOC DCMD timed out for scsi%d\n",
+                      instance->host->host_no);
+               retval = 1;
+               goto out;
+       }
+
+
+       if (dcmd->cmd_status) {
+               printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
+                      "_MEM_ALLOC DCMD failed with status 0x%x for scsi%d\n",
+                      dcmd->cmd_status,
+                      instance->host->host_no);
+               retval = 1;
+               goto out;
+       }
+
+out:
+       megasas_return_cmd(instance, cmd);
+
+       return retval;
+}
+
+/* Handler for SR-IOV heartbeat */
+void megasas_sriov_heartbeat_handler(unsigned long instance_addr)
+{
+       struct megasas_instance *instance =
+               (struct megasas_instance *)instance_addr;
+
+       if (instance->hb_host_mem->HB.fwCounter !=
+           instance->hb_host_mem->HB.driverCounter) {
+               instance->hb_host_mem->HB.driverCounter =
+                       instance->hb_host_mem->HB.fwCounter;
+               mod_timer(&instance->sriov_heartbeat_timer,
+                         jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+       } else {
+               printk(KERN_WARNING "megasas: SR-IOV: Heartbeat never "
+                      "completed for scsi%d\n", instance->host->host_no);
+               schedule_work(&instance->work_init);
+       }
+}
+
 /**
  * megasas_wait_for_outstanding -      Wait for all outstanding cmds
  * @instance:                          Adapter soft state
@@ -2032,9 +2376,10 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd)
         * First wait for all commands to complete
         */
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+           (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
-               ret = megasas_reset_fusion(scmd->device->host);
+               ret = megasas_reset_fusion(scmd->device->host, 1);
        else
                ret = megasas_generic_reset(scmd);
 
@@ -2749,6 +3094,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
                                (instance->pdev->device ==
                                PCI_DEVICE_ID_LSI_FUSION) ||
                                (instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_PLASMA) ||
+                               (instance->pdev->device ==
                                PCI_DEVICE_ID_LSI_INVADER) ||
                                (instance->pdev->device ==
                                PCI_DEVICE_ID_LSI_FURY)) {
@@ -2772,6 +3119,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
                                 PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
                            (instance->pdev->device ==
                             PCI_DEVICE_ID_LSI_FUSION) ||
+                           (instance->pdev->device ==
+                            PCI_DEVICE_ID_LSI_PLASMA) ||
                            (instance->pdev->device ==
                             PCI_DEVICE_ID_LSI_INVADER) ||
                            (instance->pdev->device ==
@@ -2797,6 +3146,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
                                PCI_DEVICE_ID_LSI_SAS0071SKINNY)  ||
                                (instance->pdev->device
                                        == PCI_DEVICE_ID_LSI_FUSION) ||
+                               (instance->pdev->device
+                                       == PCI_DEVICE_ID_LSI_PLASMA) ||
                                (instance->pdev->device
                                        == PCI_DEVICE_ID_LSI_INVADER) ||
                                (instance->pdev->device
@@ -2806,6 +3157,8 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
                                if ((instance->pdev->device ==
                                        PCI_DEVICE_ID_LSI_FUSION) ||
                                        (instance->pdev->device ==
+                                       PCI_DEVICE_ID_LSI_PLASMA) ||
+                                       (instance->pdev->device ==
                                        PCI_DEVICE_ID_LSI_INVADER) ||
                                        (instance->pdev->device ==
                                        PCI_DEVICE_ID_LSI_FURY)) {
@@ -3032,6 +3385,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
                cmd->frame->io.context = cpu_to_le32(cmd->index);
                cmd->frame->io.pad_0 = 0;
                if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) &&
+                   (instance->pdev->device != PCI_DEVICE_ID_LSI_PLASMA) &&
                    (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) &&
                        (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) &&
                    (reset_devices))
@@ -3638,6 +3992,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
        struct megasas_ctrl_info *ctrl_info;
        unsigned long bar_list;
        int i, loop, fw_msix_count = 0;
+       struct IOV_111 *iovPtr;
 
        /* Find first memory bar */
        bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM);
@@ -3660,6 +4015,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        switch (instance->pdev->device) {
        case PCI_DEVICE_ID_LSI_FUSION:
+       case PCI_DEVICE_ID_LSI_PLASMA:
        case PCI_DEVICE_ID_LSI_INVADER:
        case PCI_DEVICE_ID_LSI_FURY:
                instance->instancet = &megasas_instance_template_fusion;
@@ -3714,7 +4070,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
                scratch_pad_2 = readl
                        (&instance->reg_set->outbound_scratch_pad_2);
                /* Check max MSI-X vectors */
-               if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) {
+               if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+                   (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA)) {
                        instance->msix_vectors = (scratch_pad_2
                                & MR_MAX_REPLY_QUEUES_OFFSET) + 1;
                        fw_msix_count = instance->msix_vectors;
@@ -3828,6 +4185,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
                ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
                /* adapterOperations2 are converted into CPU arch*/
                le32_to_cpus((u32 *)&ctrl_info->adapterOperations2);
+               instance->mpio = ctrl_info->adapterOperations2.mpio;
                instance->UnevenSpanSupport =
                        ctrl_info->adapterOperations2.supportUnevenSpans;
                if (instance->UnevenSpanSupport) {
@@ -3840,6 +4198,20 @@ static int megasas_init_fw(struct megasas_instance *instance)
                                fusion->fast_path_io = 0;
 
                }
+               if (ctrl_info->host_interface.SRIOV) {
+                       if (!ctrl_info->adapterOperations2.activePassive)
+                               instance->PlasmaFW111 = 1;
+
+                       if (!instance->PlasmaFW111)
+                               instance->requestorId =
+                                       ctrl_info->iov.requestorId;
+                       else {
+                               iovPtr = (struct IOV_111 *)((unsigned char *)ctrl_info + IOV_111_OFFSET);
+                               instance->requestorId = iovPtr->requestorId;
+                       }
+                       printk(KERN_WARNING "megaraid_sas: I am VF "
+                              "requestorId %d\n", instance->requestorId);
+               }
        }
        instance->max_sectors_per_req = instance->max_num_sge *
                                                PAGE_SIZE / 512;
@@ -3872,6 +4244,17 @@ static int megasas_init_fw(struct megasas_instance *instance)
        tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
                (unsigned long)instance);
 
+       /* Launch SR-IOV heartbeat timer */
+       if (instance->requestorId) {
+               if (!megasas_sriov_start_heartbeat(instance, 1))
+                       megasas_start_timer(instance,
+                                           &instance->sriov_heartbeat_timer,
+                                           megasas_sriov_heartbeat_handler,
+                                           MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+               else
+                       instance->skip_heartbeat_timer_del = 1;
+       }
+
        return 0;
 
 fail_init_adapter:
@@ -4184,6 +4567,7 @@ static int megasas_io_attach(struct megasas_instance *instance)
 
        /* Fusion only supports host reset */
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+           (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) {
                host->hostt->eh_device_reset_handler = NULL;
@@ -4309,6 +4693,7 @@ static int megasas_probe_one(struct pci_dev *pdev,
 
        switch (instance->pdev->device) {
        case PCI_DEVICE_ID_LSI_FUSION:
+       case PCI_DEVICE_ID_LSI_PLASMA:
        case PCI_DEVICE_ID_LSI_INVADER:
        case PCI_DEVICE_ID_LSI_FURY:
        {
@@ -4405,6 +4790,7 @@ static int megasas_probe_one(struct pci_dev *pdev,
        instance->UnevenSpanSupport = 0;
 
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+           (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
                INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
@@ -4417,6 +4803,26 @@ static int megasas_probe_one(struct pci_dev *pdev,
        if (megasas_init_fw(instance))
                goto fail_init_mfi;
 
+       if (instance->requestorId) {
+               if (instance->PlasmaFW111) {
+                       instance->vf_affiliation_111 =
+                               pci_alloc_consistent(pdev, sizeof(struct MR_LD_VF_AFFILIATION_111),
+                                                    &instance->vf_affiliation_111_h);
+                       if (!instance->vf_affiliation_111)
+                               printk(KERN_WARNING "megasas: Can't allocate "
+                                      "memory for VF affiliation buffer\n");
+               } else {
+                       instance->vf_affiliation =
+                               pci_alloc_consistent(pdev,
+                                                    (MAX_LOGICAL_DRIVES + 1) *
+                                                    sizeof(struct MR_LD_VF_AFFILIATION),
+                                                    &instance->vf_affiliation_h);
+                       if (!instance->vf_affiliation)
+                               printk(KERN_WARNING "megasas: Can't allocate "
+                                      "memory for VF affiliation buffer\n");
+               }
+       }
+
 retry_irq_register:
        /*
         * Register IRQ
@@ -4511,6 +4917,7 @@ retry_irq_register:
                free_irq(instance->pdev->irq, &instance->irq_context[0]);
 fail_irq:
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+           (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
                megasas_release_fusion(instance);
@@ -4644,6 +5051,10 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
        host = instance->host;
        instance->unload = 1;
 
+       /* Shutdown SR-IOV heartbeat timer */
+       if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+               del_timer_sync(&instance->sriov_heartbeat_timer);
+
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
 
@@ -4730,6 +5141,7 @@ megasas_resume(struct pci_dev *pdev)
 
        switch (instance->pdev->device) {
        case PCI_DEVICE_ID_LSI_FUSION:
+       case PCI_DEVICE_ID_LSI_PLASMA:
        case PCI_DEVICE_ID_LSI_INVADER:
        case PCI_DEVICE_ID_LSI_FURY:
        {
@@ -4795,6 +5207,17 @@ megasas_resume(struct pci_dev *pdev)
                }
        }
 
+       /* Re-launch SR-IOV heartbeat timer */
+       if (instance->requestorId) {
+               if (!megasas_sriov_start_heartbeat(instance, 0))
+                       megasas_start_timer(instance,
+                                           &instance->sriov_heartbeat_timer,
+                                           megasas_sriov_heartbeat_handler,
+                                           MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+               else
+                       instance->skip_heartbeat_timer_del = 1;
+       }
+
        instance->instancet->enable_intr(instance);
        instance->unload = 0;
 
@@ -4849,6 +5272,10 @@ static void megasas_detach_one(struct pci_dev *pdev)
        host = instance->host;
        fusion = instance->ctrl_context;
 
+       /* Shutdown SR-IOV heartbeat timer */
+       if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+               del_timer_sync(&instance->sriov_heartbeat_timer);
+
        scsi_remove_host(instance->host);
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
@@ -4894,6 +5321,7 @@ static void megasas_detach_one(struct pci_dev *pdev)
 
        switch (instance->pdev->device) {
        case PCI_DEVICE_ID_LSI_FUSION:
+       case PCI_DEVICE_ID_LSI_PLASMA:
        case PCI_DEVICE_ID_LSI_INVADER:
        case PCI_DEVICE_ID_LSI_FURY:
                megasas_release_fusion(instance);
@@ -4920,6 +5348,24 @@ static void megasas_detach_one(struct pci_dev *pdev)
        if (instance->evt_detail)
                pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
                                instance->evt_detail, instance->evt_detail_h);
+
+       if (instance->vf_affiliation)
+               pci_free_consistent(pdev, (MAX_LOGICAL_DRIVES + 1) *
+                                   sizeof(struct MR_LD_VF_AFFILIATION),
+                                   instance->vf_affiliation,
+                                   instance->vf_affiliation_h);
+
+       if (instance->vf_affiliation_111)
+               pci_free_consistent(pdev,
+                                   sizeof(struct MR_LD_VF_AFFILIATION_111),
+                                   instance->vf_affiliation_111,
+                                   instance->vf_affiliation_111_h);
+
+       if (instance->hb_host_mem)
+               pci_free_consistent(pdev, sizeof(struct MR_CTRL_HB_HOST_MEM),
+                                   instance->hb_host_mem,
+                                   instance->hb_host_mem_h);
+
        scsi_host_put(host);
 
        pci_disable_device(pdev);
@@ -5208,6 +5654,16 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
                goto out_kfree_ioc;
        }
 
+       /* Adjust ioctl wait time for VF mode */
+       if (instance->requestorId)
+               wait_time = MEGASAS_ROUTINE_WAIT_TIME_VF;
+
+       /* Block ioctls in VF mode */
+       if (instance->requestorId && !allow_vf_ioctls) {
+               error = -ENODEV;
+               goto out_kfree_ioc;
+       }
+
        if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
                printk(KERN_ERR "Controller in crit error\n");
                error = -ENODEV;
@@ -5517,7 +5973,7 @@ megasas_aen_polling(struct work_struct *work)
        u16     pd_index = 0;
        u16     ld_index = 0;
        int     i, j, doscan = 0;
-       u32 seq_num;
+       u32 seq_num, wait_time = MEGASAS_RESET_WAIT_TIME;
        int error;
 
        if (!instance) {
@@ -5525,6 +5981,23 @@ megasas_aen_polling(struct work_struct *work)
                kfree(ev);
                return;
        }
+
+       /* Adjust event workqueue thread wait time for VF mode */
+       if (instance->requestorId)
+               wait_time = MEGASAS_ROUTINE_WAIT_TIME_VF;
+
+       /* Don't run the event workqueue thread if OCR is running */
+       for (i = 0; i < wait_time; i++) {
+               if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL)
+                       break;
+               if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
+                       printk(KERN_NOTICE "megasas: %s waiting for "
+                              "controller reset to finish for scsi%d\n",
+                              __func__, instance->host->host_no);
+               }
+               msleep(1000);
+       }
+
        instance->ev = NULL;
        host = instance->host;
        if (instance->evt_detail) {
@@ -5591,65 +6064,64 @@ megasas_aen_polling(struct work_struct *work)
                case MR_EVT_LD_OFFLINE:
                case MR_EVT_CFG_CLEARED:
                case MR_EVT_LD_DELETED:
-                       if (megasas_ld_list_query(instance,
-                                       MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
-                               megasas_get_ld_list(instance);
-                       for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
-                               for (j = 0;
-                               j < MEGASAS_MAX_DEV_PER_CHANNEL;
-                               j++) {
-
-                               ld_index =
-                               (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
-                               sdev1 = scsi_device_lookup(host,
-                                       MEGASAS_MAX_PD_CHANNELS + i,
-                                       j,
-                                       0);
-
-                               if (instance->ld_ids[ld_index] != 0xff) {
-                                       if (sdev1) {
-                                               scsi_device_put(sdev1);
-                                       }
-                               } else {
-                                       if (sdev1) {
-                                               scsi_remove_device(sdev1);
-                                               scsi_device_put(sdev1);
+                       if (!instance->requestorId ||
+                           (instance->requestorId &&
+                            megasas_get_ld_vf_affiliation(instance, 0))) {
+                               if (megasas_ld_list_query(instance,
+                                                         MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
+                                       megasas_get_ld_list(instance);
+                               for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+                                       for (j = 0;
+                                            j < MEGASAS_MAX_DEV_PER_CHANNEL;
+                                            j++) {
+
+                                               ld_index =
+                                                       (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+
+                                               sdev1 = scsi_device_lookup(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+
+                                               if (instance->ld_ids[ld_index]
+                                                   != 0xff) {
+                                                       if (sdev1)
+                                                               scsi_device_put(sdev1);
+                                               } else {
+                                                       if (sdev1) {
+                                                               scsi_remove_device(sdev1);
+                                                               scsi_device_put(sdev1);
+                                                       }
+                                               }
                                        }
                                }
-                               }
+                               doscan = 0;
                        }
-                       doscan = 0;
                        break;
                case MR_EVT_LD_CREATED:
-                       if (megasas_ld_list_query(instance,
-                                       MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
-                               megasas_get_ld_list(instance);
-                       for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
-                               for (j = 0;
-                                       j < MEGASAS_MAX_DEV_PER_CHANNEL;
-                                       j++) {
-                                       ld_index =
-                                       (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
-
-                                       sdev1 = scsi_device_lookup(host,
-                                               MEGASAS_MAX_PD_CHANNELS + i,
-                                               j, 0);
-
-                                       if (instance->ld_ids[ld_index] !=
-                                                               0xff) {
-                                               if (!sdev1) {
-                                                       scsi_add_device(host,
-                                               MEGASAS_MAX_PD_CHANNELS + i,
-                                                               j, 0);
+                       if (!instance->requestorId ||
+                           (instance->requestorId &&
+                            megasas_get_ld_vf_affiliation(instance, 0))) {
+                               if (megasas_ld_list_query(instance,
+                                                         MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
+                                       megasas_get_ld_list(instance);
+                               for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+                                       for (j = 0;
+                                            j < MEGASAS_MAX_DEV_PER_CHANNEL;
+                                            j++) {
+                                               ld_index =
+                                                       (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+
+                                               sdev1 = scsi_device_lookup(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+
+                                               if (instance->ld_ids[ld_index]
+                                                   != 0xff) {
+                                                       if (!sdev1)
+                                                               scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
                                                }
-                                       }
-                                       if (sdev1) {
-                                               scsi_device_put(sdev1);
+                                               if (sdev1)
+                                                       scsi_device_put(sdev1);
                                        }
                                }
+                               doscan = 0;
                        }
-                       doscan = 0;
                        break;
                case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
                case MR_EVT_FOREIGN_CFG_IMPORTED:
@@ -5667,7 +6139,8 @@ megasas_aen_polling(struct work_struct *work)
        }
 
        if (doscan) {
-               printk(KERN_INFO "scanning ...\n");
+               printk(KERN_INFO "megaraid_sas: scanning for scsi%d...\n",
+                      instance->host->host_no);
                if (megasas_get_pd_list(instance) == 0) {
                        for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
                                for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
@@ -5690,28 +6163,31 @@ megasas_aen_polling(struct work_struct *work)
                        }
                }
 
-               if (megasas_ld_list_query(instance,
-                                         MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
-                       megasas_get_ld_list(instance);
-               for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
-                       for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
-                               ld_index =
-                               (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
+               if (!instance->requestorId ||
+                   (instance->requestorId &&
+                    megasas_get_ld_vf_affiliation(instance, 0))) {
+                       if (megasas_ld_list_query(instance,
+                                                 MR_LD_QUERY_TYPE_EXPOSED_TO_HOST))
+                               megasas_get_ld_list(instance);
+                       for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
+                               for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL;
+                                    j++) {
+                                       ld_index =
+                                               (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
 
-                               sdev1 = scsi_device_lookup(host,
-                                       MEGASAS_MAX_PD_CHANNELS + i, j, 0);
-                               if (instance->ld_ids[ld_index] != 0xff) {
-                                       if (!sdev1) {
-                                               scsi_add_device(host,
-                                               MEGASAS_MAX_PD_CHANNELS + i,
-                                                               j, 0);
+                                       sdev1 = scsi_device_lookup(host,
+                                                                  MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+                                       if (instance->ld_ids[ld_index]
+                                           != 0xff) {
+                                               if (!sdev1)
+                                                       scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
+                                               else
+                                                       scsi_device_put(sdev1);
                                        } else {
-                                               scsi_device_put(sdev1);
-                                       }
-                               } else {
-                                       if (sdev1) {
-                                               scsi_remove_device(sdev1);
-                                               scsi_device_put(sdev1);
+                                               if (sdev1) {
+                                                       scsi_remove_device(sdev1);
+                                                       scsi_device_put(sdev1);
+                                               }
                                        }
                                }
                        }
index 99b7bffb36fe366c02ea3fda1878acf098894e2a..22600419ae9ffa539be2aaf68cc916803ec05161 100644 (file)
@@ -62,7 +62,8 @@ megasas_complete_cmd(struct megasas_instance *instance,
                     struct megasas_cmd *cmd, u8 alt_status);
 int megasas_is_ldio(struct scsi_cmnd *cmd);
 int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd);
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+             int seconds);
 
 void
 megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd);
@@ -81,6 +82,13 @@ int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
 void megaraid_sas_kill_hba(struct megasas_instance *instance);
 
 extern u32 megasas_dbg_lvl;
+void megasas_sriov_heartbeat_handler(unsigned long instance_addr);
+int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
+                                 int initial);
+void megasas_start_timer(struct megasas_instance *instance,
+                       struct timer_list *timer,
+                        void *fn, unsigned long interval);
+extern struct megasas_mgmt_info megasas_mgmt_info;
 extern int resetwaittime;
 
 /**
@@ -549,12 +557,13 @@ fail_req_desc:
  * For polling, MFI requires the cmd_status to be set to 0xFF before posting.
  */
 int
-wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd)
+wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
+       int seconds)
 {
        int i;
        struct megasas_header *frame_hdr = &cmd->frame->hdr;
 
-       u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000;
+       u32 msecs = seconds * 1000;
 
        /*
         * Wait for cmd_status to change
@@ -672,7 +681,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
        instance->instancet->fire_cmd(instance, req_desc.u.low,
                                      req_desc.u.high, instance->reg_set);
 
-       wait_and_poll(instance, cmd);
+       wait_and_poll(instance, cmd, MFI_POLL_TIMEOUT_SECS);
 
        frame_hdr = &cmd->frame->hdr;
        if (frame_hdr->cmd_status != 0) {
@@ -1772,7 +1781,8 @@ megasas_get_request_descriptor(struct megasas_instance *instance, u16 index)
 
        if (index >= instance->max_fw_cmds) {
                printk(KERN_ERR "megasas: Invalid SMID (0x%x)request for "
-                      "descriptor\n", index);
+                      "descriptor for scsi%d\n", index,
+                       instance->host->host_no);
                return NULL;
        }
        fusion = instance->ctrl_context;
@@ -2040,8 +2050,11 @@ irqreturn_t megasas_isr_fusion(int irq, void *devp)
                /* If we didn't complete any commands, check for FW fault */
                fw_state = instance->instancet->read_fw_status_reg(
                        instance->reg_set) & MFI_STATE_MASK;
-               if (fw_state == MFI_STATE_FAULT)
+               if (fw_state == MFI_STATE_FAULT) {
+                       printk(KERN_WARNING "megaraid_sas: Iop2SysDoorbellInt"
+                              "for scsi%d\n", instance->host->host_no);
                        schedule_work(&instance->work_init);
+               }
        }
 
        return IRQ_HANDLED;
@@ -2212,9 +2225,10 @@ megasas_check_reset_fusion(struct megasas_instance *instance,
 }
 
 /* This function waits for outstanding commands on fusion to complete */
-int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance)
+int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
+                                       int iotimeout, int *convert)
 {
-       int i, outstanding, retval = 0;
+       int i, outstanding, retval = 0, hb_seconds_missed = 0;
        u32 fw_state;
 
        for (i = 0; i < resetwaittime; i++) {
@@ -2223,18 +2237,49 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance)
                        instance->reg_set) & MFI_STATE_MASK;
                if (fw_state == MFI_STATE_FAULT) {
                        printk(KERN_WARNING "megasas: Found FW in FAULT state,"
-                              " will reset adapter.\n");
+                              " will reset adapter scsi%d.\n",
+                               instance->host->host_no);
+                       retval = 1;
+                       goto out;
+               }
+               /* If SR-IOV VF mode & heartbeat timeout, don't wait */
+               if (instance->requestorId && !iotimeout) {
                        retval = 1;
                        goto out;
                }
 
+               /* If SR-IOV VF mode & I/O timeout, check for HB timeout */
+               if (instance->requestorId && iotimeout) {
+                       if (instance->hb_host_mem->HB.fwCounter !=
+                           instance->hb_host_mem->HB.driverCounter) {
+                               instance->hb_host_mem->HB.driverCounter =
+                                       instance->hb_host_mem->HB.fwCounter;
+                               hb_seconds_missed = 0;
+                       } else {
+                               hb_seconds_missed++;
+                               if (hb_seconds_missed ==
+                                   (MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF/HZ)) {
+                                       printk(KERN_WARNING "megasas: SR-IOV:"
+                                              " Heartbeat never completed "
+                                              " while polling during I/O "
+                                              " timeout handling for "
+                                              "scsi%d.\n",
+                                              instance->host->host_no);
+                                              *convert = 1;
+                                              retval = 1;
+                                              goto out;
+                               }
+                       }
+               }
+
                outstanding = atomic_read(&instance->fw_outstanding);
                if (!outstanding)
                        goto out;
 
                if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
                        printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
-                              "commands to complete\n", i, outstanding);
+                              "commands to complete for scsi%d\n", i,
+                              outstanding, instance->host->host_no);
                        megasas_complete_cmd_dpc_fusion(
                                (unsigned long)instance);
                }
@@ -2243,7 +2288,8 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance)
 
        if (atomic_read(&instance->fw_outstanding)) {
                printk("megaraid_sas: pending commands remain after waiting, "
-                      "will reset adapter.\n");
+                      "will reset adapter scsi%d.\n",
+                      instance->host->host_no);
                retval = 1;
        }
 out:
@@ -2265,10 +2311,34 @@ void  megasas_reset_reply_desc(struct megasas_instance *instance)
                reply_desc->Words = ULLONG_MAX;
 }
 
+/* Check for a second path that is currently UP */
+int megasas_check_mpio_paths(struct megasas_instance *instance,
+       struct scsi_cmnd *scmd)
+{
+       int i, j, retval = (DID_RESET << 16);
+
+       if (instance->mpio && instance->requestorId) {
+               for (i = 0 ; i < MAX_MGMT_ADAPTERS ; i++)
+                       for (j = 0 ; j < MAX_LOGICAL_DRIVES; j++)
+                               if (megasas_mgmt_info.instance[i] &&
+                                   (megasas_mgmt_info.instance[i] != instance) &&
+                                   megasas_mgmt_info.instance[i]->mpio &&
+                                   megasas_mgmt_info.instance[i]->requestorId
+                                   &&
+                                   (megasas_mgmt_info.instance[i]->ld_ids[j]
+                                    == scmd->device->id)) {
+                                           retval = (DID_NO_CONNECT << 16);
+                                           goto out;
+                               }
+       }
+out:
+       return retval;
+}
+
 /* Core fusion reset function */
-int megasas_reset_fusion(struct Scsi_Host *shost)
+int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
 {
-       int retval = SUCCESS, i, j, retry = 0;
+       int retval = SUCCESS, i, j, retry = 0, convert = 0;
        struct megasas_instance *instance;
        struct megasas_cmd_fusion *cmd_fusion;
        struct fusion_context *fusion;
@@ -2279,28 +2349,39 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
        instance = (struct megasas_instance *)shost->hostdata;
        fusion = instance->ctrl_context;
 
+       mutex_lock(&instance->reset_mutex);
+
        if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
                printk(KERN_WARNING "megaraid_sas: Hardware critical error, "
-                      "returning FAILED.\n");
+                      "returning FAILED for scsi%d.\n",
+                       instance->host->host_no);
                return FAILED;
        }
 
-       mutex_lock(&instance->reset_mutex);
+       if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+               del_timer_sync(&instance->sriov_heartbeat_timer);
        set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
-       instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
+       instance->adprecovery = MEGASAS_ADPRESET_SM_POLLING;
        instance->instancet->disable_intr(instance);
        msleep(1000);
 
        /* First try waiting for commands to complete */
-       if (megasas_wait_for_outstanding_fusion(instance)) {
+       if (megasas_wait_for_outstanding_fusion(instance, iotimeout,
+                                               &convert)) {
+               instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT;
                printk(KERN_WARNING "megaraid_sas: resetting fusion "
-                      "adapter.\n");
+                      "adapter scsi%d.\n", instance->host->host_no);
+               if (convert)
+                       iotimeout = 0;
+
                /* Now return commands back to the OS */
                for (i = 0 ; i < instance->max_fw_cmds; i++) {
                        cmd_fusion = fusion->cmd_list[i];
                        if (cmd_fusion->scmd) {
                                scsi_dma_unmap(cmd_fusion->scmd);
-                               cmd_fusion->scmd->result = (DID_RESET << 16);
+                               cmd_fusion->scmd->result =
+                                       megasas_check_mpio_paths(instance,
+                                                                cmd_fusion->scmd);
                                cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
                                megasas_return_cmd_fusion(instance, cmd_fusion);
                                atomic_dec(&instance->fw_outstanding);
@@ -2315,13 +2396,67 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                    (abs_state == MFI_STATE_FAULT && !reset_adapter)) {
                        /* Reset not supported, kill adapter */
                        printk(KERN_WARNING "megaraid_sas: Reset not supported"
-                              ", killing adapter.\n");
+                              ", killing adapter scsi%d.\n",
+                               instance->host->host_no);
                        megaraid_sas_kill_hba(instance);
+                       instance->skip_heartbeat_timer_del = 1;
                        instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
                        retval = FAILED;
                        goto out;
                }
 
+               /* Let SR-IOV VF & PF sync up if there was a HB failure */
+               if (instance->requestorId && !iotimeout) {
+                       msleep(MEGASAS_OCR_SETTLE_TIME_VF);
+                       /* Look for a late HB update after VF settle time */
+                       if (abs_state == MFI_STATE_OPERATIONAL &&
+                           (instance->hb_host_mem->HB.fwCounter !=
+                            instance->hb_host_mem->HB.driverCounter)) {
+                                       instance->hb_host_mem->HB.driverCounter =
+                                               instance->hb_host_mem->HB.fwCounter;
+                                       printk(KERN_WARNING "megasas: SR-IOV:"
+                                              "Late FW heartbeat update for "
+                                              "scsi%d.\n",
+                                              instance->host->host_no);
+                       } else {
+                               /* In VF mode, first poll for FW ready */
+                               for (i = 0;
+                                    i < (MEGASAS_RESET_WAIT_TIME * 1000);
+                                    i += 20) {
+                                       status_reg =
+                                               instance->instancet->
+                                               read_fw_status_reg(
+                                                       instance->reg_set);
+                                       abs_state = status_reg &
+                                               MFI_STATE_MASK;
+                                       if (abs_state == MFI_STATE_READY) {
+                                               printk(KERN_WARNING "megasas"
+                                                      ": SR-IOV: FW was found"
+                                                      "to be in ready state "
+                                                      "for scsi%d.\n",
+                                                      instance->host->host_no);
+                                               break;
+                                       }
+                                       msleep(20);
+                               }
+                               if (abs_state != MFI_STATE_READY) {
+                                       printk(KERN_WARNING "megasas: SR-IOV: "
+                                              "FW not in ready state after %d"
+                                              " seconds for scsi%d, status_reg = "
+                                              "0x%x.\n",
+                                              MEGASAS_RESET_WAIT_TIME,
+                                              instance->host->host_no,
+                                              status_reg);
+                                       megaraid_sas_kill_hba(instance);
+                                       instance->skip_heartbeat_timer_del = 1;
+                                       instance->adprecovery =
+                                               MEGASAS_HW_CRITICAL_ERROR;
+                                       retval = FAILED;
+                                       goto out;
+                               }
+                       }
+               }
+
                /* Now try to reset the chip */
                for (i = 0; i < MEGASAS_FUSION_MAX_RESET_TRIES; i++) {
                        writel(MPI2_WRSEQ_FLUSH_KEY_VALUE,
@@ -2348,7 +2483,9 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                                readl(&instance->reg_set->fusion_host_diag);
                                if (retry++ == 100) {
                                        printk(KERN_WARNING "megaraid_sas: "
-                                              "Host diag unlock failed!\n");
+                                              "Host diag unlock failed! "
+                                              "for scsi%d\n",
+                                               instance->host->host_no);
                                        break;
                                }
                        }
@@ -2370,7 +2507,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                                if (retry++ == 1000) {
                                        printk(KERN_WARNING "megaraid_sas: "
                                               "Diag reset adapter never "
-                                              "cleared!\n");
+                                              "cleared for scsi%d!\n",
+                                               instance->host->host_no);
                                        break;
                                }
                        }
@@ -2392,29 +2530,29 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                        if (abs_state <= MFI_STATE_FW_INIT) {
                                printk(KERN_WARNING "megaraid_sas: firmware "
                                       "state < MFI_STATE_FW_INIT, state = "
-                                      "0x%x\n", abs_state);
+                                      "0x%x for scsi%d\n", abs_state,
+                                       instance->host->host_no);
                                continue;
                        }
 
                        /* Wait for FW to become ready */
                        if (megasas_transition_to_ready(instance, 1)) {
                                printk(KERN_WARNING "megaraid_sas: Failed to "
-                                      "transition controller to ready.\n");
+                                      "transition controller to ready "
+                                      "for scsi%d.\n",
+                                      instance->host->host_no);
                                continue;
                        }
 
                        megasas_reset_reply_desc(instance);
                        if (megasas_ioc_init_fusion(instance)) {
                                printk(KERN_WARNING "megaraid_sas: "
-                                      "megasas_ioc_init_fusion() failed!\n");
+                                      "megasas_ioc_init_fusion() failed!"
+                                      " for scsi%d\n",
+                                      instance->host->host_no);
                                continue;
                        }
 
-                       clear_bit(MEGASAS_FUSION_IN_RESET,
-                                 &instance->reset_flags);
-                       instance->instancet->enable_intr(instance);
-                       instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
-
                        /* Re-fire management commands */
                        for (j = 0 ; j < instance->max_fw_cmds; j++) {
                                cmd_fusion = fusion->cmd_list[j];
@@ -2438,7 +2576,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                                                if (!req_desc) {
                                                        printk(KERN_WARNING
                                                               "req_desc NULL"
-                                                              "\n");
+                                                              " for scsi%d\n",
+                                                               instance->host->host_no);
                                                        /* Return leaked MPT
                                                           frame */
                                                        megasas_return_cmd_fusion(instance, cmd_fusion);
@@ -2456,6 +2595,11 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                                }
                        }
 
+                       clear_bit(MEGASAS_FUSION_IN_RESET,
+                                 &instance->reset_flags);
+                       instance->instancet->enable_intr(instance);
+                       instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+
                        /* Reset load balance info */
                        memset(fusion->load_balance_info, 0,
                               sizeof(struct LD_LOAD_BALANCE_INFO)
@@ -2464,19 +2608,39 @@ int megasas_reset_fusion(struct Scsi_Host *shost)
                        if (!megasas_get_map_info(instance))
                                megasas_sync_map_info(instance);
 
+                       /* Restart SR-IOV heartbeat */
+                       if (instance->requestorId) {
+                               if (!megasas_sriov_start_heartbeat(instance, 0))
+                                       megasas_start_timer(instance,
+                                                           &instance->sriov_heartbeat_timer,
+                                                           megasas_sriov_heartbeat_handler,
+                                                           MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+                               else
+                                       instance->skip_heartbeat_timer_del = 1;
+                       }
+
                        /* Adapter reset completed successfully */
                        printk(KERN_WARNING "megaraid_sas: Reset "
-                              "successful.\n");
+                              "successful for scsi%d.\n",
+                               instance->host->host_no);
                        retval = SUCCESS;
                        goto out;
                }
                /* Reset failed, kill the adapter */
                printk(KERN_WARNING "megaraid_sas: Reset failed, killing "
-                      "adapter.\n");
+                      "adapter scsi%d.\n", instance->host->host_no);
                megaraid_sas_kill_hba(instance);
+               instance->skip_heartbeat_timer_del = 1;
                instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR;
                retval = FAILED;
        } else {
+               /* For VF: Restart HB timer if we didn't OCR */
+               if (instance->requestorId) {
+                       megasas_start_timer(instance,
+                                           &instance->sriov_heartbeat_timer,
+                                           megasas_sriov_heartbeat_handler,
+                                           MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
+               }
                clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
                instance->instancet->enable_intr(instance);
                instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
@@ -2493,7 +2657,7 @@ void megasas_fusion_ocr_wq(struct work_struct *work)
        struct megasas_instance *instance =
                container_of(work, struct megasas_instance, work_init);
 
-       megasas_reset_fusion(instance->host);
+       megasas_reset_fusion(instance->host, 0);
 }
 
 struct megasas_instance_template megasas_instance_template_fusion = {
index 35a51397b36478828e2649cac833249f0b38b2bb..e76af5459a09fd0a7dee995273992c9e5498f444 100644 (file)
@@ -485,6 +485,9 @@ struct MPI2_IOC_INIT_REQUEST {
 #define MAX_PHYSICAL_DEVICES 256
 #define MAX_RAIDMAP_PHYSICAL_DEVICES (MAX_PHYSICAL_DEVICES)
 #define MR_DCMD_LD_MAP_GET_INFO             0x0300e101
+#define MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC  0x010e8485   /* SR-IOV HB alloc*/
+#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111   0x03200200
+#define MR_DCMD_LD_VF_MAP_GET_ALL_LDS       0x03150200
 
 struct MR_DEV_HANDLE_INFO {
        u16     curDevHdl;