diff options
Diffstat (limited to 'drivers/scsi/megaraid')
-rw-r--r-- | drivers/scsi/megaraid/megaraid_mm.c | 2 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.h | 114 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_base.c | 815 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fp.c | 11 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fusion.c | 272 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fusion.h | 3 |
6 files changed, 1022 insertions, 195 deletions
diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index dfffd0f37916..a70692779a16 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -486,6 +486,8 @@ mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc) pthru32->dataxferaddr = kioc->buf_paddr; if (kioc->data_dir & UIOC_WR) { + if (pthru32->dataxferlen > kioc->xferlen) + return -EINVAL; if (copy_from_user(kioc->buf_vaddr, kioc->user_data, pthru32->dataxferlen)) { return (-EFAULT); diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 34452ea386ac..32166c2c7854 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -33,9 +33,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "06.700.06.00-rc1" -#define MEGASAS_RELDATE "Aug. 31, 2013" -#define MEGASAS_EXT_VERSION "Sat. Aug. 31 17:00:00 PDT 2013" +#define MEGASAS_VERSION "06.803.01.00-rc1" +#define MEGASAS_RELDATE "Mar. 10, 2014" +#define MEGASAS_EXT_VERSION "Mon. Mar. 10 17:00:00 PDT 2014" /* * Device IDs @@ -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) @@ -1347,9 +1367,15 @@ struct megasas_cmd; union megasas_evt_class_locale { struct { +#ifndef __BIG_ENDIAN_BITFIELD u16 locale; u8 reserved; s8 class; +#else + s8 class; + u8 reserved; + u16 locale; +#endif } __attribute__ ((packed)) members; u32 word; @@ -1523,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; @@ -1598,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, @@ -1609,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, }; @@ -1728,7 +1824,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, struct IO_REQUEST_INFO *io_info, struct RAID_CONTEXT *pRAID_Context, struct MR_FW_RAID_MAP_ALL *map, u8 **raidLUN); -u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map); +u8 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map); struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_FW_RAID_MAP_ALL *map); u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_FW_RAID_MAP_ALL *map); u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_FW_RAID_MAP_ALL *map); diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 3b7ad10497fe..d84d02c2aad9 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FILE: megaraid_sas_base.c - * Version : 06.700.06.00-rc1 + * Version : 06.803.01.00-rc1 * * Authors: LSI Corporation * Sreenivas Bagalkote @@ -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,13 +904,18 @@ 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); } /** * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds * @instance: Adapter soft state * @cmd: Command to be issued + * @timeout: Timeout in seconds * * This function waits on an event for the command to be returned from ISR. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs @@ -905,13 +923,20 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) */ static int megasas_issue_blocked_cmd(struct megasas_instance *instance, - struct megasas_cmd *cmd) + struct megasas_cmd *cmd, int timeout) { + int ret = 0; cmd->cmd_status = ENODATA; instance->instancet->issue_dcmd(instance, cmd); - - wait_event(instance->int_cmd_wait_q, cmd->cmd_status != ENODATA); + if (timeout) { + ret = wait_event_timeout(instance->int_cmd_wait_q, + cmd->cmd_status != ENODATA, timeout * HZ); + if (!ret) + return 1; + } else + wait_event(instance->int_cmd_wait_q, + cmd->cmd_status != ENODATA); return 0; } @@ -920,18 +945,20 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd * @instance: Adapter soft state * @cmd_to_abort: Previously issued cmd to be aborted + * @timeout: Timeout in seconds * - * MFI firmware can abort previously issued AEN command (automatic event + * MFI firmware can abort previously issued AEN comamnd (automatic event * notification). The megasas_issue_blocked_abort_cmd() issues such abort * cmd and waits for return status. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs */ static int megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, - struct megasas_cmd *cmd_to_abort) + struct megasas_cmd *cmd_to_abort, int timeout) { struct megasas_cmd *cmd; struct megasas_abort_frame *abort_fr; + int ret = 0; cmd = megasas_get_cmd(instance); @@ -957,10 +984,18 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, instance->instancet->issue_dcmd(instance, cmd); - /* - * Wait for this cmd to complete - */ - wait_event(instance->abort_cmd_wait_q, cmd->cmd_status != 0xFF); + if (timeout) { + ret = wait_event_timeout(instance->abort_cmd_wait_q, + cmd->cmd_status != ENODATA, timeout * HZ); + if (!ret) { + dev_err(&instance->pdev->dev, "Command timedout" + "from %s\n", __func__); + return 1; + } + } else + wait_event(instance->abort_cmd_wait_q, + cmd->cmd_status != ENODATA); + cmd->sync_cmd = 0; megasas_return_cmd(instance, cmd); @@ -1514,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; } @@ -1641,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); } @@ -1730,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); @@ -1752,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 @@ -2014,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); @@ -2731,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)) { @@ -2755,6 +3120,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)) { @@ -2780,6 +3147,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)) { @@ -2788,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)) { @@ -3014,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)) @@ -3620,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); @@ -3642,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; @@ -3696,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; @@ -3763,7 +4138,10 @@ static int megasas_init_fw(struct megasas_instance *instance) memset(instance->pd_list, 0 , (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); - megasas_get_pd_list(instance); + if (megasas_get_pd_list(instance) < 0) { + printk(KERN_ERR "megasas: failed to get PD list\n"); + goto fail_init_adapter; + } memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); if (megasas_ld_list_query(instance, @@ -3807,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) { @@ -3819,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; @@ -3851,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: @@ -3933,16 +4337,19 @@ megasas_get_seq_num(struct megasas_instance *instance, dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(el_info_h); dcmd->sgl.sge32[0].length = cpu_to_le32(sizeof(struct megasas_evt_log_info)); - megasas_issue_blocked_cmd(instance, cmd); - - /* - * Copy the data back into callers buffer - */ - eli->newest_seq_num = le32_to_cpu(el_info->newest_seq_num); - eli->oldest_seq_num = le32_to_cpu(el_info->oldest_seq_num); - eli->clear_seq_num = le32_to_cpu(el_info->clear_seq_num); - eli->shutdown_seq_num = le32_to_cpu(el_info->shutdown_seq_num); - eli->boot_seq_num = le32_to_cpu(el_info->boot_seq_num); + if (megasas_issue_blocked_cmd(instance, cmd, 30)) + dev_err(&instance->pdev->dev, "Command timedout" + "from %s\n", __func__); + else { + /* + * Copy the data back into callers buffer + */ + eli->newest_seq_num = le32_to_cpu(el_info->newest_seq_num); + eli->oldest_seq_num = le32_to_cpu(el_info->oldest_seq_num); + eli->clear_seq_num = le32_to_cpu(el_info->clear_seq_num); + eli->shutdown_seq_num = le32_to_cpu(el_info->shutdown_seq_num); + eli->boot_seq_num = le32_to_cpu(el_info->boot_seq_num); + } pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info), el_info, el_info_h); @@ -4018,7 +4425,7 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, instance->aen_cmd->abort_aen = 1; ret_val = megasas_issue_blocked_abort_cmd(instance, instance-> - aen_cmd); + aen_cmd, 30); if (ret_val) { printk(KERN_DEBUG "megasas: Failed to abort " @@ -4160,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; @@ -4197,6 +4605,19 @@ megasas_set_dma_mask(struct pci_dev *pdev) if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) goto fail_set_dma_mask; } + /* + * Ensure that all data structures are allocated in 32-bit + * memory. + */ + if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) { + /* Try 32bit DMA mask and 32 bit Consistent dma mask */ + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) + && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) + dev_info(&pdev->dev, "set 32bit DMA mask" + "and 32 bit consistent mask\n"); + else + goto fail_set_dma_mask; + } return 0; @@ -4212,7 +4633,7 @@ fail_set_dma_mask: static int megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { - int rval, pos, i, j; + int rval, pos, i, j, cpu; struct Scsi_Host *host; struct megasas_instance *instance; u16 control = 0; @@ -4272,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: { @@ -4368,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); @@ -4380,12 +4803,33 @@ 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 */ if (instance->msix_vectors) { - for (i = 0 ; i < instance->msix_vectors; i++) { + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < instance->msix_vectors; i++) { instance->irq_context[i].instance = instance; instance->irq_context[i].MSIxIndex = i; if (request_irq(instance->msixentry[i].vector, @@ -4394,14 +4838,22 @@ retry_irq_register: &instance->irq_context[i])) { printk(KERN_DEBUG "megasas: Failed to " "register IRQ for vector %d.\n", i); - for (j = 0 ; j < i ; j++) + for (j = 0; j < i; j++) { + irq_set_affinity_hint( + instance->msixentry[j].vector, NULL); free_irq( instance->msixentry[j].vector, &instance->irq_context[j]); + } /* Retry irq register for IO_APIC */ instance->msix_vectors = 0; goto retry_irq_register; } + if (irq_set_affinity_hint(instance->msixentry[i].vector, + get_cpu_mask(cpu))) + dev_err(&instance->pdev->dev, "Error setting" + "affinity hint for cpu %d\n", cpu); + cpu = cpumask_next(cpu, cpu_online_mask); } } else { instance->irq_context[0].instance = instance; @@ -4455,13 +4907,17 @@ retry_irq_register: instance->instancet->disable_intr(instance); if (instance->msix_vectors) - for (i = 0 ; i < instance->msix_vectors; i++) + for (i = 0; i < instance->msix_vectors; i++) { + irq_set_affinity_hint( + instance->msixentry[i].vector, NULL); free_irq(instance->msixentry[i].vector, &instance->irq_context[i]); + } else 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); @@ -4522,7 +4978,9 @@ static void megasas_flush_cache(struct megasas_instance *instance) dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_CACHE_FLUSH); dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; - megasas_issue_blocked_cmd(instance, cmd); + if (megasas_issue_blocked_cmd(instance, cmd, 30)) + dev_err(&instance->pdev->dev, "Command timedout" + " from %s\n", __func__); megasas_return_cmd(instance, cmd); @@ -4549,10 +5007,11 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, return; if (instance->aen_cmd) - megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); + megasas_issue_blocked_abort_cmd(instance, + instance->aen_cmd, 30); if (instance->map_update_cmd) megasas_issue_blocked_abort_cmd(instance, - instance->map_update_cmd); + instance->map_update_cmd, 30); dcmd = &cmd->frame->dcmd; memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -4566,7 +5025,9 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, dcmd->data_xfer_len = 0; dcmd->opcode = cpu_to_le32(opcode); - megasas_issue_blocked_cmd(instance, cmd); + if (megasas_issue_blocked_cmd(instance, cmd, 30)) + dev_err(&instance->pdev->dev, "Command timedout" + "from %s\n", __func__); megasas_return_cmd(instance, cmd); @@ -4590,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); @@ -4606,9 +5071,12 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) instance->instancet->disable_intr(instance); if (instance->msix_vectors) - for (i = 0 ; i < instance->msix_vectors; i++) + for (i = 0; i < instance->msix_vectors; i++) { + irq_set_affinity_hint( + instance->msixentry[i].vector, NULL); free_irq(instance->msixentry[i].vector, &instance->irq_context[i]); + } else free_irq(instance->pdev->irq, &instance->irq_context[0]); if (instance->msix_vectors) @@ -4629,7 +5097,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) static int megasas_resume(struct pci_dev *pdev) { - int rval, i, j; + int rval, i, j, cpu; struct Scsi_Host *host; struct megasas_instance *instance; @@ -4673,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: { @@ -4701,6 +5170,7 @@ megasas_resume(struct pci_dev *pdev) * Register IRQ */ if (instance->msix_vectors) { + cpu = cpumask_first(cpu_online_mask); for (i = 0 ; i < instance->msix_vectors; i++) { instance->irq_context[i].instance = instance; instance->irq_context[i].MSIxIndex = i; @@ -4710,12 +5180,21 @@ megasas_resume(struct pci_dev *pdev) &instance->irq_context[i])) { printk(KERN_DEBUG "megasas: Failed to " "register IRQ for vector %d.\n", i); - for (j = 0 ; j < i ; j++) + for (j = 0; j < i; j++) { + irq_set_affinity_hint( + instance->msixentry[j].vector, NULL); free_irq( instance->msixentry[j].vector, &instance->irq_context[j]); + } goto fail_irq; } + + if (irq_set_affinity_hint(instance->msixentry[i].vector, + get_cpu_mask(cpu))) + dev_err(&instance->pdev->dev, "Error setting" + "affinity hint for cpu %d\n", cpu); + cpu = cpumask_next(cpu, cpu_online_mask); } } else { instance->irq_context[0].instance = instance; @@ -4728,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; @@ -4782,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); @@ -4793,6 +5287,9 @@ static void megasas_detach_one(struct pci_dev *pdev) instance->ev = NULL; } + /* cancel all wait events */ + wake_up_all(&instance->int_cmd_wait_q); + tasklet_kill(&instance->isr_tasklet); /* @@ -4811,9 +5308,12 @@ static void megasas_detach_one(struct pci_dev *pdev) instance->instancet->disable_intr(instance); if (instance->msix_vectors) - for (i = 0 ; i < instance->msix_vectors; i++) + for (i = 0; i < instance->msix_vectors; i++) { + irq_set_affinity_hint( + instance->msixentry[i].vector, NULL); free_irq(instance->msixentry[i].vector, &instance->irq_context[i]); + } else free_irq(instance->pdev->irq, &instance->irq_context[0]); if (instance->msix_vectors) @@ -4821,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); @@ -4847,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); @@ -4868,9 +5387,12 @@ static void megasas_shutdown(struct pci_dev *pdev) megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); instance->instancet->disable_intr(instance); if (instance->msix_vectors) - for (i = 0 ; i < instance->msix_vectors; i++) + for (i = 0; i < instance->msix_vectors; i++) { + irq_set_affinity_hint( + instance->msixentry[i].vector, NULL); free_irq(instance->msixentry[i].vector, &instance->irq_context[i]); + } else free_irq(instance->pdev->irq, &instance->irq_context[0]); if (instance->msix_vectors) @@ -5045,7 +5567,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * cmd to the SCSI mid-layer */ cmd->sync_cmd = 1; - megasas_issue_blocked_cmd(instance, cmd); + megasas_issue_blocked_cmd(instance, cmd, 0); cmd->sync_cmd = 0; /* @@ -5132,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; @@ -5441,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) { @@ -5449,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) { @@ -5515,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: @@ -5591,50 +6139,55 @@ megasas_aen_polling(struct work_struct *work) } if (doscan) { - printk(KERN_INFO "scanning ...\n"); - megasas_get_pd_list(instance); - for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { - for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { - pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; - sdev1 = scsi_device_lookup(host, i, j, 0); - if (instance->pd_list[pd_index].driveState == - MR_PD_STATE_SYSTEM) { - if (!sdev1) { - scsi_add_device(host, i, j, 0); - } - if (sdev1) - scsi_device_put(sdev1); - } else { - if (sdev1) { - scsi_remove_device(sdev1); - scsi_device_put(sdev1); + 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++) { + pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + if (!sdev1) { + scsi_add_device(host, i, j, 0); + } + if (sdev1) + scsi_device_put(sdev1); + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } } } } } - 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); + } } } } diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index e24b6eb645b5..081bfff12d00 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -143,12 +143,12 @@ u16 MR_PdDevHandleGet(u32 pd, struct MR_FW_RAID_MAP_ALL *map) u16 MR_GetLDTgtId(u32 ld, struct MR_FW_RAID_MAP_ALL *map) { - return map->raidMap.ldSpanMap[ld].ldRaid.targetId; + return le16_to_cpu(map->raidMap.ldSpanMap[ld].ldRaid.targetId); } -u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map) +u8 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map) { - return le16_to_cpu(map->raidMap.ldTgtIdToLd[ldTgtId]); + return map->raidMap.ldTgtIdToLd[ldTgtId]; } static struct MR_LD_SPAN *MR_LdSpanPtrGet(u32 ld, u32 span, @@ -975,7 +975,10 @@ MR_BuildRaidContext(struct megasas_instance *instance, regSize += stripSize; } - pRAID_Context->timeoutValue = cpu_to_le16(map->raidMap.fpPdIoTimeoutSec); + pRAID_Context->timeoutValue = + cpu_to_le16(raid->fpIoTimeoutForLd ? + raid->fpIoTimeoutForLd : + map->raidMap.fpPdIoTimeoutSec); if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) pRAID_Context->regLockFlags = (isRead) ? diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index f6555921fd7a..22600419ae9f 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -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 @@ -585,7 +594,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) struct megasas_cmd *cmd; u8 ret; struct fusion_context *fusion; - union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc; int i; struct megasas_header *frame_hdr; @@ -644,18 +653,18 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) /* Convert capability to LE32 */ cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities); - init_frame->queue_info_new_phys_addr_lo = cpu_to_le32((u32)ioc_init_handle); + init_frame->queue_info_new_phys_addr_hi = + cpu_to_le32(upper_32_bits(ioc_init_handle)); + init_frame->queue_info_new_phys_addr_lo = + cpu_to_le32(lower_32_bits(ioc_init_handle)); init_frame->data_xfer_len = cpu_to_le32(sizeof(struct MPI2_IOC_INIT_REQUEST)); - req_desc = - (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)fusion->req_frames_desc; - - req_desc->Words = 0; - req_desc->MFAIo.RequestFlags = + req_desc.Words = 0; + req_desc.MFAIo.RequestFlags = (MEGASAS_REQ_DESCRIPT_FLAGS_MFA << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - cpu_to_le32s((u32 *)&req_desc->MFAIo); - req_desc->Words |= cpu_to_le64(cmd->frame_phys_addr); + cpu_to_le32s((u32 *)&req_desc.MFAIo); + req_desc.Words |= cpu_to_le64(cmd->frame_phys_addr); /* * disable the intr before firing the init frame @@ -669,10 +678,10 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) break; } - instance->instancet->fire_cmd(instance, req_desc->u.low, - req_desc->u.high, instance->reg_set); + 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) { @@ -723,7 +732,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance) if (!fusion) { megasas_return_cmd(instance, cmd); - return 1; + return -ENXIO; } dcmd = &cmd->frame->dcmd; @@ -1604,13 +1613,15 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - io_request->IoFlags |= - MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH; + io_request->IoFlags |= cpu_to_le16( + MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); cmd->request_desc->SCSIIO.DevHandle = local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; + cmd->request_desc->SCSIIO.MSIxIndex = + instance->msix_vectors ? smp_processor_id() % instance->msix_vectors : 0; /* * If the command is for the tape device, set the * FP timeout to the os layer timeout value. @@ -1770,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; @@ -2038,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; @@ -2210,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++) { @@ -2221,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); } @@ -2241,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: @@ -2263,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; @@ -2277,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); @@ -2313,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, @@ -2346,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; } } @@ -2368,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; } } @@ -2390,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]; @@ -2422,7 +2562,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost) instance-> cmd_list[cmd_fusion->sync_cmd_idx]; if (cmd_mfi->frame->dcmd.opcode == - MR_DCMD_LD_MAP_GET_INFO) { + cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)) { megasas_return_cmd(instance, cmd_mfi); megasas_return_cmd_fusion( @@ -2433,11 +2573,15 @@ int megasas_reset_fusion(struct Scsi_Host *shost) instance, cmd_mfi->context.smid -1); - if (!req_desc) + if (!req_desc) { printk(KERN_WARNING "req_desc NULL" - "\n"); - else { + " for scsi%d\n", + instance->host->host_no); + /* Return leaked MPT + frame */ + megasas_return_cmd_fusion(instance, cmd_fusion); + } else { instance->instancet-> fire_cmd(instance, req_desc-> @@ -2451,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) @@ -2459,18 +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; @@ -2487,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 = { diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 35a51397b364..e76af5459a09 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -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; |