/** * megasas_adp_reset_wait_for_ready - initiate chip reset and wait for * controller to come to ready state * @instance: adapter's soft state * @do_adp_reset: If true, do a chip reset * @ocr_context: If called from OCR context this will * be set to 1, else 0 * * This function initiates a chip reset followed by a wait for controller to * transition to ready state. * During this, driver will block all access to PCI config space from userspace
*/ int
megasas_adp_reset_wait_for_ready(struct megasas_instance *instance, bool do_adp_reset, int ocr_context)
{ int ret = FAILED;
/* * Block access to PCI config space from userspace * when diag reset is initiated from driver
*/ if (megasas_dbg_lvl & OCR_DEBUG)
dev_info(&instance->pdev->dev, "Block access to PCI config space %s %d\n",
__func__, __LINE__);
pci_cfg_access_lock(instance->pdev);
if (do_adp_reset) { if (instance->instancet->adp_reset
(instance, instance->reg_set)) goto out;
}
/* Wait for FW to become ready */ if (megasas_transition_to_ready(instance, ocr_context)) {
dev_warn(&instance->pdev->dev, "Failed to transition controller to ready for scsi%d.\n",
instance->host->host_no); goto out;
}
ret = SUCCESS;
out: if (megasas_dbg_lvl & OCR_DEBUG)
dev_info(&instance->pdev->dev, "Unlock access to PCI config space %s %d\n",
__func__, __LINE__);
pci_cfg_access_unlock(instance->pdev);
return ret;
}
/** * megasas_check_same_4gb_region - check if allocation * crosses same 4GB boundary or not * @instance: adapter's soft instance * @start_addr: start address of DMA allocation * @size: size of allocation in bytes * @return: true : allocation does not cross same * 4GB boundary * false: allocation crosses same * 4GB boundary
*/ staticinlinebool megasas_check_same_4gb_region
(struct megasas_instance *instance, dma_addr_t start_addr, size_t size)
{
dma_addr_t end_addr;
end_addr = start_addr + size;
if (upper_32_bits(start_addr) != upper_32_bits(end_addr)) {
dev_err(&instance->pdev->dev, "Failed to get same 4GB boundary: start_addr: 0x%llx end_addr: 0x%llx\n",
(unsignedlonglong)start_addr,
(unsignedlonglong)end_addr); returnfalse;
}
instance->mask_interrupts = 0; /* For Thunderbolt/Invader also clear intr on enable */
writel(~0, ®s->outbound_intr_status);
readl(®s->outbound_intr_status);
/* Dummy readl to force pci flush */
dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n",
__func__, readl(®s->outbound_intr_mask));
}
writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */
dev_info(&instance->pdev->dev, "%s is called outbound_intr_mask:0x%08x\n",
__func__, readl(®s->outbound_intr_mask));
}
int
megasas_clear_intr_fusion(struct megasas_instance *instance)
{
u32 status; struct megasas_register_set __iomem *regs;
regs = instance->reg_set; /* * Check if it is our interrupt
*/
status = megasas_readl(instance,
®s->outbound_intr_status);
if (status & 1) {
writel(status, ®s->outbound_intr_status);
readl(®s->outbound_intr_status); return1;
} if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK)) return0;
/** * megasas_get_cmd_fusion - Get a command from the free pool * @instance: Adapter soft state * @blk_tag: Command tag * * Returns a blk_tag indexed mpt frame
*/ inlinestruct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
*instance, u32 blk_tag)
{ struct fusion_context *fusion;
/** * megasas_fire_cmd_fusion - Sends command to the FW * @instance: Adapter soft state * @req_desc: 32bit or 64bit Request descriptor * * Perform PCI Write. AERO SERIES supports 32 bit Descriptor. * Prior to AERO_SERIES support 64 bit Descriptor.
*/ staticvoid
megasas_fire_cmd_fusion(struct megasas_instance *instance, union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
{ if (instance->atomic_desc_support)
writel(le32_to_cpu(req_desc->u.low),
&instance->reg_set->inbound_single_queue_port); else
megasas_write_64bit_req_desc(instance, req_desc);
}
/** * megasas_fusion_update_can_queue - Do all Adapter Queue depth related calculations here * @instance: Adapter soft state * @fw_boot_context: Whether this function called during probe or after OCR * * This function is only for fusion controllers. * Update host can queue, if firmware downgrade max supported firmware commands. * Firmware upgrade case will be skipped because underlying firmware has * more resource than exposed to the OS. *
*/ staticvoid
megasas_fusion_update_can_queue(struct megasas_instance *instance, int fw_boot_context)
{
u16 cur_max_fw_cmds = 0;
u16 ldio_threshold = 0;
/* ventura FW does not fill outbound_scratch_pad_2 with queue depth */ if (instance->adapter_type < VENTURA_SERIES)
cur_max_fw_cmds =
megasas_readl(instance,
&instance->reg_set->outbound_scratch_pad_2) & 0x00FFFF;
if (reset_devices)
instance->max_fw_cmds = min(instance->max_fw_cmds,
(u16)MEGASAS_KDUMP_QUEUE_DEPTH); /* * Reduce the max supported cmds by 1. This is to ensure that the * reply_q_sz (1 more than the max cmd that driver may send) * does not exceed max cmds that the FW can support
*/
instance->max_fw_cmds = instance->max_fw_cmds-1;
}
}
/** * megasas_free_cmds_fusion - Free all the cmds in the free cmd pool * @instance: Adapter soft state
*/ void
megasas_free_cmds_fusion(struct megasas_instance *instance)
{ int i; struct fusion_context *fusion = instance->ctrl_context; struct megasas_cmd_fusion *cmd;
if (fusion->sense)
dma_pool_free(fusion->sense_dma_pool, fusion->sense,
fusion->sense_phys_addr);
/* SG */ if (fusion->cmd_list) { for (i = 0; i < instance->max_mpt_cmds; i++) {
cmd = fusion->cmd_list[i]; if (cmd) { if (cmd->sg_frame)
dma_pool_free(fusion->sg_dma_pool,
cmd->sg_frame,
cmd->sg_frame_phys_addr);
}
kfree(cmd);
}
kfree(fusion->cmd_list);
}
if (fusion->sg_dma_pool) {
dma_pool_destroy(fusion->sg_dma_pool);
fusion->sg_dma_pool = NULL;
} if (fusion->sense_dma_pool) {
dma_pool_destroy(fusion->sense_dma_pool);
fusion->sense_dma_pool = NULL;
}
/* Reply Frame, Desc*/ if (instance->is_rdpq)
megasas_free_rdpq_fusion(instance); else
megasas_free_reply_fusion(instance);
/* Request Frame, Desc*/ if (fusion->req_frames_desc)
dma_free_coherent(&instance->pdev->dev,
fusion->request_alloc_sz, fusion->req_frames_desc,
fusion->req_frames_desc_phys); if (fusion->io_request_frames)
dma_pool_free(fusion->io_request_frames_pool,
fusion->io_request_frames,
fusion->io_request_frames_phys); if (fusion->io_request_frames_pool) {
dma_pool_destroy(fusion->io_request_frames_pool);
fusion->io_request_frames_pool = NULL;
}
}
/** * megasas_create_sg_sense_fusion - Creates DMA pool for cmd frames * @instance: Adapter soft state *
*/ staticint megasas_create_sg_sense_fusion(struct megasas_instance *instance)
{ int i;
u16 max_cmd; struct fusion_context *fusion; struct megasas_cmd_fusion *cmd; int sense_sz;
u32 offset;
if (!fusion->sense_dma_pool || !fusion->sg_dma_pool) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
fusion->sense = dma_pool_alloc(fusion->sense_dma_pool,
GFP_KERNEL, &fusion->sense_phys_addr); if (!fusion->sense) {
dev_err(&instance->pdev->dev, "failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
/* sense buffer, request frame and reply desc pool requires to be in * same 4 gb region. Below function will check this. * In case of failure, new pci pool will be created with updated * alignment. * Older allocation and pool will be destroyed. * Alignment will be used such a way that next allocation if success, * will always meet same 4gb region requirement. * Actual requirement is not alignment, but we need start and end of * DMA address must have same upper 32 bit address.
*/
fusion->sense_dma_pool =
dma_pool_create("mr_sense_align", &instance->pdev->dev,
sense_sz, roundup_pow_of_two(sense_sz), 0); if (!fusion->sense_dma_pool) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
fusion->sense = dma_pool_alloc(fusion->sense_dma_pool,
GFP_KERNEL,
&fusion->sense_phys_addr); if (!fusion->sense) {
dev_err(&instance->pdev->dev, "failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
}
/* * Allocate and attach a frame to each of the commands in cmd_list
*/ for (i = 0; i < max_cmd; i++) {
cmd = fusion->cmd_list[i];
cmd->sg_frame = dma_pool_alloc(fusion->sg_dma_pool,
GFP_KERNEL, &cmd->sg_frame_phys_addr);
/* * fusion->cmd_list is an array of struct megasas_cmd_fusion pointers. * Allocate the dynamic array first and then allocate individual * commands.
*/
fusion->cmd_list =
kcalloc(max_mpt_cmd, sizeof(struct megasas_cmd_fusion *),
GFP_KERNEL); if (!fusion->cmd_list) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
for (i = 0; i < max_mpt_cmd; i++) {
fusion->cmd_list[i] = kzalloc(sizeof(struct megasas_cmd_fusion),
GFP_KERNEL); if (!fusion->cmd_list[i]) { for (j = 0; j < i; j++)
kfree(fusion->cmd_list[j]);
kfree(fusion->cmd_list);
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
}
if (!fusion->reply_frames_desc[0]) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
}
reply_desc = fusion->reply_frames_desc[0]; for (i = 0; i < fusion->reply_q_depth * count; i++, reply_desc++)
reply_desc->Words = cpu_to_le64(ULLONG_MAX);
/* This is not a rdpq mode, but driver still populate * reply_frame_desc array to use same msix index in ISR path.
*/ for (i = 0; i < (count - 1); i++)
fusion->reply_frames_desc[i + 1] =
fusion->reply_frames_desc[i] +
(fusion->reply_alloc_sz)/sizeof(union MPI2_REPLY_DESCRIPTORS_UNION);
return0;
}
staticint
megasas_alloc_rdpq_fusion(struct megasas_instance *instance)
{ int i, j, k, msix_count; struct fusion_context *fusion; union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; union MPI2_REPLY_DESCRIPTORS_UNION *rdpq_chunk_virt[RDPQ_MAX_CHUNK_COUNT];
dma_addr_t rdpq_chunk_phys[RDPQ_MAX_CHUNK_COUNT];
u8 dma_alloc_count, abs_index;
u32 chunk_size, array_size, offset;
if (!fusion->reply_frames_desc_pool ||
!fusion->reply_frames_desc_pool_align) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
}
/* * For INVADER_SERIES each set of 8 reply queues(0-7, 8-15, ..) and * VENTURA_SERIES each set of 16 reply queues(0-15, 16-31, ..) should be * within 4GB boundary and also reply queues in a set must have same * upper 32-bits in their memory address. so here driver is allocating the * DMA'able memory for reply queues according. Driver uses limitation of * VENTURA_SERIES to manage INVADER_SERIES as well.
*/
dma_alloc_count = DIV_ROUND_UP(msix_count, RDPQ_MAX_INDEX_IN_ONE_CHUNK);
for (i = 0; i < dma_alloc_count; i++) {
rdpq_chunk_virt[i] =
dma_pool_alloc(fusion->reply_frames_desc_pool,
GFP_KERNEL, &rdpq_chunk_phys[i]); if (!rdpq_chunk_virt[i]) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM;
} /* reply desc pool requires to be in same 4 gb region. * Below function will check this. * In case of failure, new pci pool will be created with updated * alignment. * For RDPQ buffers, driver always allocate two separate pci pool. * Alignment will be used such a way that next allocation if * success, will always meet same 4gb region requirement. * rdpq_tracker keep track of each buffer's physical, * virtual address and pci pool descriptor. It will help driver * while freeing the resources. *
*/ if (!megasas_check_same_4gb_region(instance, rdpq_chunk_phys[i],
chunk_size)) {
dma_pool_free(fusion->reply_frames_desc_pool,
rdpq_chunk_virt[i],
rdpq_chunk_phys[i]);
for (i = 0; i < RDPQ_MAX_CHUNK_COUNT; i++) { if (fusion->rdpq_tracker[i].pool_entry_virt)
dma_pool_free(fusion->rdpq_tracker[i].dma_pool_ptr,
fusion->rdpq_tracker[i].pool_entry_virt,
fusion->rdpq_tracker[i].pool_entry_phys);
if (fusion->reply_frames_desc[0])
dma_pool_free(fusion->reply_frames_desc_pool,
fusion->reply_frames_desc[0],
fusion->reply_frames_desc_phys[0]);
dma_pool_destroy(fusion->reply_frames_desc_pool);
}
/** * megasas_alloc_cmds_fusion - Allocates the command packets * @instance: Adapter soft state * * * Each frame has a 32-bit field called context. This context is used to get * back the megasas_cmd_fusion from the frame when a frame gets completed * In this driver, the 32 bit values are the indices into an array cmd_list. * This array is used only to look up the megasas_cmd_fusion given the context. * The free commands themselves are maintained in a linked list called cmd_pool. * * cmds are formed in the io_request and sg_frame members of the * megasas_cmd_fusion. The context field is used to get a request descriptor * and is used as SMID of the cmd. * SMID value range is from 1 to max_fw_cmds.
*/ staticint
megasas_alloc_cmds_fusion(struct megasas_instance *instance)
{ int i; struct fusion_context *fusion; struct megasas_cmd_fusion *cmd;
u32 offset;
dma_addr_t io_req_base_phys;
u8 *io_req_base;
fusion = instance->ctrl_context;
if (megasas_alloc_request_fusion(instance)) goto fail_exit;
if (instance->is_rdpq) { if (megasas_alloc_rdpq_fusion(instance)) goto fail_exit;
} else if (megasas_alloc_reply_fusion(instance)) goto fail_exit;
if (megasas_alloc_cmdlist_fusion(instance)) goto fail_exit;
/* The first 256 bytes (SMID 0) is not used. Don't add to the cmd list */
io_req_base = fusion->io_request_frames + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
io_req_base_phys = fusion->io_request_frames_phys + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
/* * Add all the commands to command pool (fusion->cmd_pool)
*/
/* SMID 0 is reserved. Set SMID/index from 1 */ for (i = 0; i < instance->max_mpt_cmds; i++) {
cmd = fusion->cmd_list[i];
offset = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE * i;
memset(cmd, 0, sizeof(struct megasas_cmd_fusion));
cmd->index = i + 1;
cmd->scmd = NULL;
cmd->sync_cmd_idx =
(i >= instance->max_scsi_cmds && i < instance->max_fw_cmds) ?
(i - instance->max_scsi_cmds) :
(u32)ULONG_MAX; /* Set to Invalid */
cmd->instance = instance;
cmd->io_request =
(struct MPI2_RAID_SCSI_IO_REQUEST *)
(io_req_base + offset);
memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
cmd->io_request_phys_addr = io_req_base_phys + offset;
cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
}
if (megasas_create_sg_sense_fusion(instance)) goto fail_exit;
/** * wait_and_poll - Issues a polling command * @instance: Adapter soft state * @cmd: Command packet to be issued * @seconds: Maximum poll time * * 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, int seconds)
{ int i; struct megasas_header *frame_hdr = &cmd->frame->hdr;
u32 status_reg;
u32 msecs = seconds * 1000;
/* * Wait for cmd_status to change
*/ for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i += 20) {
rmb();
msleep(20); if (!(i % 5000)) {
status_reg = instance->instancet->read_fw_status_reg(instance)
& MFI_STATE_MASK; if (status_reg == MFI_STATE_FAULT) break;
}
}
if (instance->consistent_mask_64bit && !cur_fw_64bit_dma_capable) {
dev_err(&instance->pdev->dev, "Driver was operating on 64bit " "DMA mask, but upcoming FW does not support 64bit DMA mask\n");
megaraid_sas_kill_hba(instance);
ret = 1; goto fail_fw_init;
}
}
if (instance->is_rdpq && !cur_rdpq_mode) {
dev_err(&instance->pdev->dev, "Firmware downgrade *NOT SUPPORTED*" " from RDPQ mode to non RDPQ mode\n");
ret = 1; goto fail_fw_init;
}
/* * Each bit in replyqueue_mask represents one group of MSI-x vectors * (each group has 8 vectors)
*/ switch (instance->perf_mode) { case MR_BALANCED_PERF_MODE:
init_frame->replyqueue_mask =
cpu_to_le16(~(~0 << instance->low_latency_index_start/8)); break; case MR_IOPS_PERF_MODE:
init_frame->replyqueue_mask =
cpu_to_le16(~(~0 << instance->msix_vectors/8)); break;
}
fail_fw_init:
dev_err(&instance->pdev->dev, "Init cmd return status FAILED for SCSI host %d\n",
instance->host->host_no);
return ret;
}
/** * megasas_sync_pd_seq_num - JBOD SEQ MAP * @instance: Adapter soft state * @pend: set to 1, if it is pended jbod map. * * Issue Jbod map to the firmware. If it is pended command, * issue command and return. If it is first instance of jbod map * issue and receive command.
*/ int
megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend) { int ret = 0;
size_t pd_seq_map_sz; struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; struct fusion_context *fusion = instance->ctrl_context; struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync;
dma_addr_t pd_seq_h;
if (pend) {
instance->instancet->issue_dcmd(instance, cmd); return0;
}
/* Below code is only for non pended DCMD */ if (!instance->mask_interrupts)
ret = megasas_issue_blocked_cmd(instance, cmd,
MFI_IO_TIMEOUT_SECS); else
ret = megasas_issue_polled(instance, cmd);
if (le32_to_cpu(pd_sync->count) > MAX_PHYSICAL_DEVICES) {
dev_warn(&instance->pdev->dev, "driver supports max %d JBOD, but FW reports %d\n",
MAX_PHYSICAL_DEVICES, le32_to_cpu(pd_sync->count));
ret = -EINVAL;
}
if (ret == DCMD_TIMEOUT)
dev_warn(&instance->pdev->dev, "%s DCMD timed out, continue without JBOD sequence map\n",
__func__);
if (ret == DCMD_SUCCESS)
instance->pd_seq_map_id++;
megasas_return_cmd(instance, cmd); return ret;
}
/* * megasas_get_ld_map_info - Returns FW's ld_map structure * @instance: Adapter soft state * @pend: Pend the command or not * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. * dcmd.mbox value setting for MR_DCMD_LD_MAP_GET_INFO * dcmd.mbox.b[0] - number of LDs being sync'd * dcmd.mbox.b[1] - 0 - complete command immediately. * - 1 - pend till config change * dcmd.mbox.b[2] - 0 - supports max 64 lds and uses legacy MR_FW_RAID_MAP * - 1 - supports max MAX_LOGICAL_DRIVES_EXT lds and * uses extended struct MR_FW_RAID_MAP_EXT
*/ staticint
megasas_get_ld_map_info(struct megasas_instance *instance)
{ int ret = 0; struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; void *ci;
dma_addr_t ci_h = 0;
u32 size_map_info; struct fusion_context *fusion;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for map info\n"); return -ENOMEM;
}
fusion = instance->ctrl_context;
if (!fusion) {
megasas_return_cmd(instance, cmd); return -ENXIO;
}
fusion->fast_path_io = 0; if (!megasas_get_ld_map_info(instance)) { if (MR_ValidateMapInfo(instance, instance->map_id)) {
fusion->fast_path_io = 1; return0;
}
} return1;
}
/* * megasas_sync_map_info - Returns FW's ld_map structure * @instance: Adapter soft state * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW.
*/ int
megasas_sync_map_info(struct megasas_instance *instance)
{ int i; struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd;
u16 num_lds; struct fusion_context *fusion; struct MR_LD_TARGET_SYNC *ci = NULL; struct MR_DRV_RAID_MAP_ALL *map; struct MR_LD_RAID *raid; struct MR_LD_TARGET_SYNC *ld_sync;
dma_addr_t ci_h = 0;
u32 size_map_info;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for sync info\n"); return -ENOMEM;
}
fusion = instance->ctrl_context;
if (!fusion) {
megasas_return_cmd(instance, cmd); return1;
}
if (!fusion->ld_drv_map[i]) {
fusion->ld_drv_map[i] = vzalloc(fusion->drv_map_sz);
if (!fusion->ld_drv_map[i]) {
dev_err(&instance->pdev->dev, "Could not allocate memory for local map" " size requested: %d\n",
fusion->drv_map_sz); goto ld_drv_map_alloc_fail;
}
}
}
for (i = 0; i < 2; i++) {
fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev,
fusion->max_map_sz,
&fusion->ld_map_phys[i],
GFP_KERNEL); if (!fusion->ld_map[i]) {
dev_err(&instance->pdev->dev, "Could not allocate memory for map info %s:%d\n",
__func__, __LINE__); goto ld_map_alloc_fail;
}
}
return0;
ld_map_alloc_fail: for (i = 0; i < 2; i++) { if (fusion->ld_map[i])
dma_free_coherent(&instance->pdev->dev,
fusion->max_map_sz,
fusion->ld_map[i],
fusion->ld_map_phys[i]);
}
ld_drv_map_alloc_fail: for (i = 0; i < 2; i++) { if (fusion->ld_drv_map[i]) { if (is_vmalloc_addr(fusion->ld_drv_map[i]))
vfree(fusion->ld_drv_map[i]); else
free_pages((ulong)fusion->ld_drv_map[i],
fusion->drv_map_pages);
}
}
if (fusion->ioc_init_cmd && fusion->ioc_init_cmd->frame)
dma_free_coherent(&instance->pdev->dev,
IOC_INIT_FRAME_SIZE,
fusion->ioc_init_cmd->frame,
fusion->ioc_init_cmd->frame_phys_addr);
kfree(fusion->ioc_init_cmd);
}
/** * megasas_init_adapter_fusion - Initializes the FW * @instance: Adapter soft state * * This is the main function for initializing firmware.
*/ static u32
megasas_init_adapter_fusion(struct megasas_instance *instance)
{ struct fusion_context *fusion;
u32 scratch_pad_1; int i = 0, count;
u32 status_reg;
/* * Only Driver's internal DCMDs and IOCTL DCMDs needs to have MFI frames
*/
instance->max_mfi_cmds =
MEGASAS_FUSION_INTERNAL_CMDS + MEGASAS_FUSION_IOCTL_CMDS;
megasas_configure_queue_sizes(instance);
scratch_pad_1 = megasas_readl(instance,
&instance->reg_set->outbound_scratch_pad_1); /* If scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK is set, * Firmware support extended IO chain frame which is 4 times more than * legacy Firmware. * Legacy Firmware - Frame size is (8 * 128) = 1K * 1M IO Firmware - Frame size is (8 * 128 * 4) = 4K
*/ if (scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK)
instance->max_chain_frame_sz =
((scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_MASK) >>
MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_1MB_IO; else
instance->max_chain_frame_sz =
((scratch_pad_1 & MEGASAS_MAX_CHAIN_SIZE_MASK) >>
MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_256K_IO;
if (instance->max_chain_frame_sz < MEGASAS_CHAIN_FRAME_SZ_MIN) {
dev_warn(&instance->pdev->dev, "frame size %d invalid, fall back to legacy max frame size %d\n",
instance->max_chain_frame_sz,
MEGASAS_CHAIN_FRAME_SZ_MIN);
instance->max_chain_frame_sz = MEGASAS_CHAIN_FRAME_SZ_MIN;
}
for (i = 0 ; i < count; i++)
fusion->last_reply_idx[i] = 0;
/* * For fusion adapters, 3 commands for IOCTL and 8 commands * for driver's internal DCMDs.
*/
instance->max_scsi_cmds = instance->max_fw_cmds -
(MEGASAS_FUSION_INTERNAL_CMDS +
MEGASAS_FUSION_IOCTL_CMDS);
sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
for (i = 0; i < MAX_MSIX_QUEUES_FUSION; i++)
atomic_set(&fusion->busy_mq_poll[i], 0);
if (megasas_alloc_ioc_init_frame(instance)) return1;
/* * Allocate memory for descriptors * Create a pool of commands
*/ if (megasas_alloc_cmds(instance)) goto fail_alloc_mfi_cmds; if (megasas_alloc_cmds_fusion(instance)) goto fail_alloc_cmds;
if (megasas_ioc_init_fusion(instance)) {
status_reg = instance->instancet->read_fw_status_reg(instance); if (((status_reg & MFI_STATE_MASK) == MFI_STATE_FAULT) &&
(status_reg & MFI_RESET_ADAPTER)) { /* Do a chip reset and then retry IOC INIT once */ if (megasas_adp_reset_wait_for_ready
(instance, true, 0) == FAILED) goto fail_ioc_init;
megasas_display_intel_branding(instance); if (megasas_get_ctrl_info(instance)) {
dev_err(&instance->pdev->dev, "Could not get controller info. Fail from %s %d\n",
__func__, __LINE__); goto fail_ioc_init;
}
/* Check the fw state */
fw_state = instance->instancet->read_fw_status_reg(instance) &
MFI_STATE_MASK;
if (fw_state == MFI_STATE_FAULT) {
dma_state = instance->instancet->read_fw_status_reg(instance) &
MFI_STATE_DMADONE; /* Start collecting crash, if DMA bit is done */ if (instance->crash_dump_drv_support &&
instance->crash_dump_app_support && dma_state) {
megasas_fusion_crash_dump(instance);
} else { if (instance->unload == 0) {
status = megasas_reset_fusion(instance->host, 0); if (status != SUCCESS) {
dev_err(&instance->pdev->dev, "Failed from %s %d, do not re-arm timer\n",
__func__, __LINE__); return;
}
}
}
}
if (instance->fw_fault_work_q)
queue_delayed_work(instance->fw_fault_work_q,
&instance->fw_fault_work,
msecs_to_jiffies(MEGASAS_WATCHDOG_THREAD_INTERVAL));
}
int
megasas_fusion_start_watchdog(struct megasas_instance *instance)
{ /* Check if the Fault WQ is already started */ if (instance->fw_fault_work_q) return SUCCESS;
if (instance->fw_fault_work_q) {
wq = instance->fw_fault_work_q;
instance->fw_fault_work_q = NULL; if (!cancel_delayed_work_sync(&instance->fw_fault_work))
flush_workqueue(wq);
destroy_workqueue(wq);
}
}
/** * map_cmd_status - Maps FW cmd status to OS cmd status * @fusion: fusion context * @scmd: Pointer to cmd * @status: status of cmd returned by FW * @ext_status: ext status of cmd returned by FW * @data_length: command data length * @sense: command sense data
*/ staticvoid
map_cmd_status(struct fusion_context *fusion, struct scsi_cmnd *scmd, u8 status, u8 ext_status,
u32 data_length, u8 *sense)
{
u8 cmd_type; int resid;
/* * If the IO request is partially completed, then MR FW will * update "io_request->DataLength" field with actual number of * bytes transferred.Driver will set residual bytes count in * SCSI command structure.
*/
resid = (scsi_bufflen(scmd) - data_length);
scsi_set_resid(scmd, resid);
if (resid &&
((cmd_type == READ_WRITE_LDIO) ||
(cmd_type == READ_WRITE_SYSPDIO)))
scmd_printk(KERN_INFO, scmd, "BRCM Debug mfi stat 0x%x, data len" " requested/completed 0x%x/0x%x\n",
status, scsi_bufflen(scmd), data_length); break;
case MFI_STAT_LD_OFFLINE: case MFI_STAT_DEVICE_NOT_FOUND:
scmd->result = DID_BAD_TARGET << 16; break; case MFI_STAT_CONFIG_SEQ_MISMATCH:
scmd->result = DID_IMM_RETRY << 16; break; default:
scmd->result = DID_ERROR << 16; break;
}
}
/** * megasas_is_prp_possible - * Checks if native NVMe PRPs can be built for the IO * * @instance: Adapter soft state * @scmd: SCSI command from the mid-layer * @sge_count: scatter gather element count. * * Returns: true: PRPs can be built * false: IEEE SGLs needs to be built
*/ staticbool
megasas_is_prp_possible(struct megasas_instance *instance, struct scsi_cmnd *scmd, int sge_count)
{
u32 data_length = 0; struct scatterlist *sg_scmd; bool build_prp = false;
u32 mr_nvme_pg_size;
/* * NVMe uses one PRP for each page (or part of a page) * look at the data length - if 4 pages or less then IEEE is OK * if > 5 pages then we need to build a native SGL * if > 4 and <= 5 pages, then check physical address of 1st SG entry * if this first size in the page is >= the residual beyond 4 pages * then use IEEE, otherwise use native SGL
*/
/* * Nvme has a very convoluted prp format. One prp is required * for each page or partial page. Driver need to split up OS sg_list * entries if it is longer than one page or cross a page * boundary. Driver also have to insert a PRP list pointer entry as * the last entry in each physical page of the PRP list. * * NOTE: The first PRP "entry" is actually placed in the first * SGL entry in the main message as IEEE 64 format. The 2nd * entry in the main message is the chain element, and the rest * of the PRP entries are built in the contiguous pcie buffer.
*/
page_mask = mr_nvme_pg_size - 1;
ptr_sgl = (u64 *)cmd->sg_frame;
ptr_sgl_phys = cmd->sg_frame_phys_addr;
memset(ptr_sgl, 0, instance->max_chain_frame_sz);
/* Build chain frame element which holds all prps except first*/
main_chain_element = (struct MPI25_IEEE_SGE_CHAIN64 *)
((u8 *)sgl_ptr + sizeof(struct MPI25_IEEE_SGE_CHAIN64));
/* Build first prp, sge need not to be page aligned*/
ptr_first_sgl = sgl_ptr;
sg_scmd = scsi_sglist(scmd);
sge_addr = sg_dma_address(sg_scmd);
sge_len = sg_dma_len(sg_scmd);
/** * megasas_make_sgl_fusion - Prepares 32-bit SGL * @instance: Adapter soft state * @scp: SCSI command from the mid-layer * @sgl_ptr: SGL to be filled in * @cmd: cmd we are working on * @sge_count: sge count *
*/ staticvoid
megasas_make_sgl_fusion(struct megasas_instance *instance, struct scsi_cmnd *scp, struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr, struct megasas_cmd_fusion *cmd, int sge_count)
{ int i, sg_processed; struct scatterlist *os_sgl; struct fusion_context *fusion;
/** * megasas_make_sgl - Build Scatter Gather List(SGLs) * @scp: SCSI command pointer * @instance: Soft instance of controller * @cmd: Fusion command pointer * * This function will build sgls based on device type. * For nvme drives, there is different way of building sgls in nvme native * format- PRPs(Physical Region Page). * * Returns the number of sg lists actually used, zero if the sg lists * is NULL, or -ENOMEM if the mapping failed
*/ static int megasas_make_sgl(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd_fusion *cmd)
{ int sge_count; bool build_prp = false; struct MPI25_IEEE_SGE_CHAIN64 *sgl_chain64;
sge_count = scsi_dma_map(scp);
if ((sge_count > instance->max_num_sge) || (sge_count <= 0)) return sge_count;
/** * megasas_stream_detect - stream detection on read and and write IOs * @instance: Adapter soft state * @cmd: Command to be prepared * @io_info: IO Request info *
*/
/** stream detection on read and and write IOs */ staticvoid megasas_stream_detect(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd, struct IO_REQUEST_INFO *io_info)
{ struct fusion_context *fusion = instance->ctrl_context;
u32 device_id = io_info->ldTgtId; struct LD_STREAM_DETECT *current_ld_sd
= fusion->stream_detect_by_ld[device_id];
u32 *track_stream = ¤t_ld_sd->mru_bit_map, stream_num;
u32 shifted_values, unshifted_values;
u32 index_value_mask, shifted_values_mask; int i; bool is_read_ahead = false; struct STREAM_DETECT *current_sd; /* find possible stream */ for (i = 0; i < MAX_STREAMS_TRACKED; ++i) {
stream_num = (*track_stream >>
(i * BITS_PER_INDEX_STREAM)) &
STREAM_MASK;
current_sd = ¤t_ld_sd->stream_track[stream_num]; /* if we found a stream, update the raid * context and also update the mruBitMap
*/ /* boundary condition */ if ((current_sd->next_seq_lba) &&
(io_info->ldStartBlock >= current_sd->next_seq_lba) &&
(io_info->ldStartBlock <= (current_sd->next_seq_lba + 32)) &&
(current_sd->is_read == io_info->isRead)) {
if ((io_info->ldStartBlock != current_sd->next_seq_lba) &&
((!io_info->isRead) || (!is_read_ahead))) /* * Once the API is available we need to change this. * At this point we are not allowing any gap
*/ continue;
SET_STREAM_DETECTED(cmd->io_request->RaidContext.raid_context_g35);
current_sd->next_seq_lba =
io_info->ldStartBlock + io_info->numBlocks; /* * update the mruBitMap LRU
*/
shifted_values_mask =
(1 << i * BITS_PER_INDEX_STREAM) - 1;
shifted_values = ((*track_stream & shifted_values_mask)
<< BITS_PER_INDEX_STREAM);
index_value_mask =
STREAM_MASK << i * BITS_PER_INDEX_STREAM;
unshifted_values =
*track_stream & ~(shifted_values_mask |
index_value_mask);
*track_stream =
unshifted_values | shifted_values | stream_num; return;
}
} /* * if we did not find any stream, create a new one * from the least recently used
*/
stream_num = (*track_stream >>
((MAX_STREAMS_TRACKED - 1) * BITS_PER_INDEX_STREAM)) &
STREAM_MASK;
current_sd = ¤t_ld_sd->stream_track[stream_num];
current_sd->is_read = io_info->isRead;
current_sd->next_seq_lba = io_info->ldStartBlock + io_info->numBlocks;
*track_stream = (((*track_stream & ZERO_LAST_STREAM) << 4) | stream_num); return;
}
/** * megasas_set_raidflag_cpu_affinity - This function sets the cpu * affinity (cpu of the controller) and raid_flags in the raid context * based on IO type. * * @fusion: Fusion context * @praid_context: IO RAID context * @raid: LD raid map * @fp_possible: Is fast path possible? * @is_read: Is read IO? * @scsi_buff_len: SCSI command buffer length *
*/ staticvoid
megasas_set_raidflag_cpu_affinity(struct fusion_context *fusion, union RAID_CONTEXT_UNION *praid_context, struct MR_LD_RAID *raid, bool fp_possible,
u8 is_read, u32 scsi_buff_len)
{
u8 cpu_sel = MR_RAID_CTX_CPUSEL_0; struct RAID_CONTEXT_G35 *rctx_g35;
rctx_g35 = &praid_context->raid_context_g35; if (fp_possible) { if (is_read) { if ((raid->cpuAffinity.pdRead.cpu0) &&
(raid->cpuAffinity.pdRead.cpu1))
cpu_sel = MR_RAID_CTX_CPUSEL_FCFS; elseif (raid->cpuAffinity.pdRead.cpu1)
cpu_sel = MR_RAID_CTX_CPUSEL_1;
} else { if ((raid->cpuAffinity.pdWrite.cpu0) &&
(raid->cpuAffinity.pdWrite.cpu1))
cpu_sel = MR_RAID_CTX_CPUSEL_FCFS; elseif (raid->cpuAffinity.pdWrite.cpu1)
cpu_sel = MR_RAID_CTX_CPUSEL_1; /* Fast path cache by pass capable R0/R1 VD */ if ((raid->level <= 1) &&
(raid->capability.fp_cache_bypass_capable)) {
rctx_g35->routing_flags |=
(1 << MR_RAID_CTX_ROUTINGFLAGS_SLD_SHIFT);
rctx_g35->raid_flags =
(MR_RAID_FLAGS_IO_SUB_TYPE_CACHE_BYPASS
<< MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT);
}
}
} else { if (is_read) { if ((raid->cpuAffinity.ldRead.cpu0) &&
(raid->cpuAffinity.ldRead.cpu1))
cpu_sel = MR_RAID_CTX_CPUSEL_FCFS; elseif (raid->cpuAffinity.ldRead.cpu1)
cpu_sel = MR_RAID_CTX_CPUSEL_1;
} else { if ((raid->cpuAffinity.ldWrite.cpu0) &&
(raid->cpuAffinity.ldWrite.cpu1))
cpu_sel = MR_RAID_CTX_CPUSEL_FCFS; elseif (raid->cpuAffinity.ldWrite.cpu1)
cpu_sel = MR_RAID_CTX_CPUSEL_1;
if (instance->adapter_type >= VENTURA_SERIES) { /* FP for Optimal raid level 1. * All large RAID-1 writes (> 32 KiB, both WT and WB modes) * are built by the driver as LD I/Os. * All small RAID-1 WT writes (<= 32 KiB) are built as FP I/Os * (there is never a reason to process these as buffered writes) * All small RAID-1 WB writes (<= 32 KiB) are built as FP I/Os * with the SLD bit asserted.
*/ if (io_info.r1_alt_dev_handle != MR_DEVHANDLE_INVALID) {
mrdev_priv = scp->device->hostdata;
if (!fp_possible ||
(io_info.isRead && io_info.ra_capable)) {
spin_lock_irqsave(&instance->stream_lock,
spinlock_flags);
megasas_stream_detect(instance, cmd, &io_info);
spin_unlock_irqrestore(&instance->stream_lock,
spinlock_flags); /* In ventura if stream detected for a read and it is * read ahead capable make this IO as LDIO
*/ if (is_stream_detected(rctx_g35))
fp_possible = false;
}
/* If raid is NULL, set CPU affinity to default CPU0 */ if (raid)
megasas_set_raidflag_cpu_affinity(fusion, &io_request->RaidContext,
raid, fp_possible, io_info.isRead,
scsi_buff_len); else
rctx_g35->routing_flags |=
(MR_RAID_CTX_CPUSEL_0 << MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_SHIFT);
}
/* set RAID context values */
pRAID_Context->config_seq_num = raid->seqNum; if (instance->adapter_type < VENTURA_SERIES)
pRAID_Context->reg_lock_flags = REGION_TYPE_SHARED_READ;
pRAID_Context->timeout_value =
cpu_to_le16(raid->fpIoTimeoutForLd);
/* get the DevHandle for the PD (since this is
fpNonRWCapable, this is a single disk RAID0) */
span = physArm = 0;
arRef = MR_LdSpanArrayGet(ld, span, local_map_ptr);
pd = MR_ArPdGet(arRef, physArm, local_map_ptr);
devHandle = MR_PdDevHandleGet(pd, local_map_ptr);
/* If FW supports PD sequence number */ if (instance->support_seqnum_jbod_fp) { if (instance->use_seqnum_jbod_fp &&
instance->pd_list[pd_index].driveType == TYPE_DISK) {
/** * megasas_build_io_fusion - Prepares IOs to devices * @instance: Adapter soft state * @scp: SCSI command * @cmd: Command to be prepared * * Invokes helper functions to prepare request frames * and sets flags appropriate for IO/Non-IO cmd
*/ staticint
megasas_build_io_fusion(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd_fusion *cmd)
{ int sge_count;
u16 pd_index = 0;
u8 drive_type = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; struct MR_PRIV_DEVICE *mr_device_priv_data;
mr_device_priv_data = scp->device->hostdata;
/* Zero out some fields so they don't get reused */
memset(io_request->LUN, 0x0, 8);
io_request->CDB.EEDP32.PrimaryReferenceTag = 0;
io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0;
io_request->EEDPFlags = 0;
io_request->Control = 0;
io_request->EEDPBlockSize = 0;
io_request->ChainOffset = 0;
io_request->RaidContext.raid_context.raid_flags = 0;
io_request->RaidContext.raid_context.type = 0;
io_request->RaidContext.raid_context.nseg = 0;
memcpy(io_request->CDB.CDB32, scp->cmnd, scp->cmd_len); /* * Just the CDB length,rest of the Flags are zero * This will be modified for FP in build_ldio_fusion
*/
io_request->IoFlags = cpu_to_le16(scp->cmd_len);
if (sge_count > instance->max_num_sge || (sge_count < 0)) {
dev_err(&instance->pdev->dev, "%s %d sge_count (%d) is out of range. Range is: 0-%d\n",
__func__, __LINE__, sge_count, instance->max_num_sge); return1;
}
if (instance->adapter_type >= VENTURA_SERIES) {
set_num_sge(&io_request->RaidContext.raid_context_g35, sge_count);
cpu_to_le16s(&io_request->RaidContext.raid_context_g35.routing_flags);
cpu_to_le16s(&io_request->RaidContext.raid_context_g35.nseg_type);
} else { /* numSGE store lower 8 bit of sge_count. * numSGEExt store higher 8 bit of sge_count
*/
io_request->RaidContext.raid_context.num_sge = sge_count;
io_request->RaidContext.raid_context.num_sge_ext =
(u8)(sge_count >> 8);
}
if (cmd->io_request->ChainOffset != 0 &&
cmd->io_request->ChainOffset != 0xF)
dev_err(&instance->pdev->dev, "The chain offset value is not " "correct : %x\n", cmd->io_request->ChainOffset); /* * if it is raid 1/10 fp write capable. * try to get second command from pool and construct it. * From FW, it has confirmed that lba values of two PDs * corresponds to single R1/10 LD are always same *
*/ /* driver side count always should be less than max_fw_cmds * to get new command
*/ if (cmd->r1_alt_dev_handle != MR_DEVHANDLE_INVALID) {
r1_cmd = megasas_get_cmd_fusion(instance,
scsi_cmd_to_rq(scmd)->tag + instance->max_fw_cmds);
megasas_prepare_secondRaid1_IO(instance, cmd, r1_cmd);
}
r1_cmd = fusion->cmd_list[peer_smid - 1];
scmd_local = cmd->scmd;
status = rctx_g35->status;
ex_status = rctx_g35->ex_status;
data_length = cmd->io_request->DataLength;
sense = cmd->sense;
cmd->cmd_completed = true;
/* Check if peer command is completed or not*/ if (r1_cmd->cmd_completed) {
rctx_g35 = &r1_cmd->io_request->RaidContext.raid_context_g35; if (rctx_g35->status != MFI_STAT_OK) {
status = rctx_g35->status;
ex_status = rctx_g35->ex_status;
data_length = r1_cmd->io_request->DataLength;
sense = r1_cmd->sense;
}
if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) break; /* * Write to reply post host index register after completing threshold * number of reply counts and still there are more replies in reply queue * pending to be completed
*/ if (threshold_reply_count >= instance->threshold_reply_count) { if (instance->msix_combined)
writel(((MSIxIndex & 0x7) << 24) |
fusion->last_reply_idx[MSIxIndex],
instance->reply_post_host_index_addr[MSIxIndex/8]); else
writel((MSIxIndex << 24) |
fusion->last_reply_idx[MSIxIndex],
instance->reply_post_host_index_addr[0]);
threshold_reply_count = 0; if (irq_context) { if (!irq_context->irq_poll_scheduled) {
irq_context->irq_poll_scheduled = true;
irq_context->irq_line_enable = true;
irq_poll_sched(&irq_context->irqpoll);
}
release_irq_context(irq_context); return num_completed;
}
}
}
for (i = 0; i < count; i++) {
synchronize_irq(pci_irq_vector(instance->pdev, i));
irq_ctx = &instance->irq_context[i];
irq_poll_disable(&irq_ctx->irqpoll); if (irq_ctx->irq_poll_scheduled) {
irq_ctx->irq_poll_scheduled = false;
enable_irq(irq_ctx->os_irq);
complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
}
}
}
/** * megasas_irqpoll() - process a queue for completed reply descriptors * @irqpoll: IRQ poll structure associated with queue to poll. * @budget: Threshold of reply descriptors to process per poll. * * Return: The number of entries processed.
*/
int megasas_irqpoll(struct irq_poll *irqpoll, int budget)
{ struct megasas_irq_context *irq_ctx; struct megasas_instance *instance; int num_entries;
if (irq_context->irq_poll_scheduled) return IRQ_HANDLED;
if (!instance->msix_vectors) {
mfiStatus = instance->instancet->clear_intr(instance); if (!mfiStatus) return IRQ_NONE;
}
/* If we are resetting, bail */ if (test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags)) {
instance->instancet->clear_intr(instance); return IRQ_HANDLED;
}
/* This function waits for outstanding commands on fusion to complete */ staticint
megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, int reason, int *convert)
{ int i, outstanding, retval = 0, hb_seconds_missed = 0;
u32 fw_state, abs_state;
u32 waittime_for_io_completion;
if (reason == MFI_IO_TIMEOUT_OCR) {
dev_info(&instance->pdev->dev, "MFI command is timed out\n");
megasas_complete_cmd_dpc_fusion((unsignedlong)instance); if (instance->snapdump_wait_time)
megasas_trigger_snap_dump(instance);
retval = 1; goto out;
}
for (i = 0; i < waittime_for_io_completion; i++) { /* Check if firmware is in fault state */
abs_state = instance->instancet->read_fw_status_reg(instance);
fw_state = abs_state & MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) {
dev_printk(KERN_ERR, &instance->pdev->dev, "FW in FAULT state Fault code:0x%x subcode:0x%x func:%s\n",
abs_state & MFI_STATE_FAULT_CODE,
abs_state & MFI_STATE_FAULT_SUBCODE, __func__);
megasas_complete_cmd_dpc_fusion((unsignedlong)instance); if (instance->requestorId && reason) {
dev_warn(&instance->pdev->dev, "SR-IOV Found FW in FAULT" " state while polling during" " I/O timeout handling for %d\n",
instance->host->host_no);
*convert = 1;
}
/* If SR-IOV VF mode & I/O timeout, check for HB timeout */ if (instance->requestorId && (reason == SCSIIO_TIMEOUT_OCR)) { 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)) {
dev_warn(&instance->pdev->dev, "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;
}
}
}
megasas_complete_cmd_dpc_fusion((unsignedlong)instance);
outstanding = atomic_read(&instance->fw_outstanding); if (!outstanding) goto out;
if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
dev_notice(&instance->pdev->dev, "[%2d]waiting for %d " "commands to complete for scsi%d\n", i,
outstanding, instance->host->host_no);
}
msleep(1000);
}
if (instance->snapdump_wait_time) {
megasas_trigger_snap_dump(instance);
retval = 1; goto out;
}
out: if (!retval && reason == SCSIIO_TIMEOUT_OCR)
dev_info(&instance->pdev->dev, "IO is completed, no OCR is required\n");
return retval;
}
void megasas_reset_reply_desc(struct megasas_instance *instance)
{ int i, j, count; struct fusion_context *fusion; union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
break; case MFI_CMD_NVME: if (!instance->support_nvme_passthru) {
cmd_mfi->frame->hdr.cmd_status = MFI_STAT_INVALID_CMD;
result = COMPLETE_CMD;
}
break; case MFI_CMD_TOOLBOX: if (!instance->support_pci_lane_margining) {
cmd_mfi->frame->hdr.cmd_status = MFI_STAT_INVALID_CMD;
result = COMPLETE_CMD;
}
break; default: break;
}
if (return_ioctl && cmd_mfi->sync_cmd &&
cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT) {
dev_err(&instance->pdev->dev, "return -EBUSY from %s %d cmd 0x%x opcode 0x%x\n",
__func__, __LINE__, cmd_mfi->frame->hdr.cmd,
le32_to_cpu(cmd_mfi->frame->dcmd.opcode));
cmd_mfi->cmd_status_drv = DCMD_BUSY;
result = COMPLETE_CMD;
}
scsi_io_req = (struct MPI2_RAID_SCSI_IO_REQUEST *)
cmd_fusion->io_request; if (scsi_io_req->Function == MPI2_FUNCTION_SCSI_TASK_MGMT)
result = RETURN_CMD;
switch (result) { case REFIRE_CMD:
megasas_fire_cmd_fusion(instance, req_desc); break; case RETURN_CMD:
megasas_return_cmd(instance, cmd_mfi); break; case COMPLETE_CMD:
megasas_complete_cmd(instance, cmd_mfi, DID_OK); break;
}
}
}
/* * megasas_return_polled_cmds: Return polled mode commands back to the pool * before initiating an OCR. * @instance: Controller's soft instance
*/ staticvoid
megasas_return_polled_cmds(struct megasas_instance *instance)
{ int i; struct megasas_cmd_fusion *cmd_fusion; struct fusion_context *fusion; struct megasas_cmd *cmd_mfi;
fusion = instance->ctrl_context;
for (i = instance->max_scsi_cmds; i < instance->max_fw_cmds; i++) {
cmd_fusion = fusion->cmd_list[i];
cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
/* * megasas_track_scsiio : Track SCSI IOs outstanding to a SCSI device * @instance: per adapter struct * @channel: the channel assigned by the OS * @id: the id assigned by the OS * * Returns SUCCESS if no IOs pending to SCSI device, else return FAILED
*/
staticint megasas_track_scsiio(struct megasas_instance *instance, int id, int channel)
{ int i, found = 0; struct megasas_cmd_fusion *cmd_fusion; struct fusion_context *fusion;
fusion = instance->ctrl_context;
for (i = 0 ; i < instance->max_scsi_cmds; i++) {
cmd_fusion = fusion->cmd_list[i]; if (cmd_fusion->scmd &&
(cmd_fusion->scmd->device->id == id &&
cmd_fusion->scmd->device->channel == channel)) {
dev_info(&instance->pdev->dev, "SCSI commands pending to target" "channel %d id %d \tSMID: 0x%x\n",
channel, id, cmd_fusion->index);
scsi_print_command(cmd_fusion->scmd);
found = 1; break;
}
}
switch (mpi_reply->ResponseCode) { case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
desc = "task management request completed"; break; case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
desc = "invalid frame"; break; case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
desc = "task management request not supported"; break; case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
desc = "task management request failed"; break; case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
desc = "task management request succeeded"; break; case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
desc = "invalid lun"; break; case0xA:
desc = "overlapped tag attempted"; break; case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
desc = "task queued, however not sent to target"; break; default:
desc = "unknown"; break;
}
dev_dbg(&instance->pdev->dev, "response_code(%01x): %s\n",
mpi_reply->ResponseCode, desc);
dev_dbg(&instance->pdev->dev, "TerminationCount/DevHandle/Function/TaskType/IOCStat/IOCLoginfo" " 0x%x/0x%x/0x%x/0x%x/0x%x/0x%x\n",
mpi_reply->TerminationCount, mpi_reply->DevHandle,
mpi_reply->Function, mpi_reply->TaskType,
mpi_reply->IOCStatus, mpi_reply->IOCLogInfo);
}
/** * megasas_issue_tm - main routine for sending tm requests * @instance: per adapter struct * @device_handle: device handle * @channel: the channel assigned by the OS * @id: the id assigned by the OS * @smid_task: smid assigned to the task * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in megaraid_sas_fusion.c) * @mr_device_priv_data: private data * Context: user * * MegaRaid use MPT interface for Task Magement request. * A generic API for sending task management requests to firmware. * * Return SUCCESS or FAILED.
*/ staticint
megasas_issue_tm(struct megasas_instance *instance, u16 device_handle,
uint channel, uint id, u16 smid_task, u8 type, struct MR_PRIV_DEVICE *mr_device_priv_data)
{ struct MR_TASK_MANAGE_REQUEST *mr_request; struct MPI2_SCSI_TASK_MANAGE_REQUEST *mpi_request; unsignedlong timeleft; struct megasas_cmd_fusion *cmd_fusion; struct megasas_cmd *cmd_mfi; union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; struct fusion_context *fusion = NULL; struct megasas_cmd_fusion *scsi_lookup; int rc; int timeout = MEGASAS_DEFAULT_TM_TIMEOUT; struct MPI2_SCSI_TASK_MANAGE_REPLY *mpi_reply;
fusion = instance->ctrl_context;
cmd_mfi = megasas_get_cmd(instance);
if (!cmd_mfi) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n",
__func__, __LINE__); return -ENOMEM;
}
case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: if ((channel == 0xFFFFFFFF) && (id == 0xFFFFFFFF)) break;
instance->instancet->disable_intr(instance);
megasas_sync_irqs((unsignedlong)instance);
rc = megasas_track_scsiio(instance, id, channel);
instance->instancet->enable_intr(instance);
megasas_enable_irq_poll(instance);
break; case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: break; default:
rc = FAILED; break;
}
return rc;
}
/* * megasas_fusion_smid_lookup : Look for fusion command corresponding to SCSI * @instance: per adapter struct * * Return Non Zero index, if SMID found in outstanding commands
*/ static u16 megasas_fusion_smid_lookup(struct scsi_cmnd *scmd)
{ int i, ret = 0; struct megasas_instance *instance; struct megasas_cmd_fusion *cmd_fusion; struct fusion_context *fusion;
if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
dev_err(&instance->pdev->dev, "Controller is not OPERATIONAL," "SCSI host:%d\n", instance->host->host_no);
ret = FAILED; return ret;
}
if (!mr_device_priv_data) {
sdev_printk(KERN_INFO, scmd->device, "device been deleted! " "scmd(%p)\n", scmd);
scmd->result = DID_NO_CONNECT << 16;
ret = SUCCESS; goto out;
}
if (!mr_device_priv_data->is_tm_capable) {
ret = FAILED; goto out;
}
mutex_lock(&instance->reset_mutex);
smid = megasas_fusion_smid_lookup(scmd);
if (!smid) {
ret = SUCCESS;
scmd_printk(KERN_NOTICE, scmd, "Command for which abort is" " issued is not found in outstanding commands\n");
mutex_unlock(&instance->reset_mutex); goto out;
}
if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) {
dev_err(&instance->pdev->dev, "Controller is not OPERATIONAL," "SCSI host:%d\n", instance->host->host_no);
ret = FAILED; return ret;
}
if (!mr_device_priv_data) {
sdev_printk(KERN_INFO, scmd->device, "device been deleted! scmd: (0x%p)\n", scmd);
scmd->result = DID_NO_CONNECT << 16;
ret = SUCCESS; goto out;
}
if (!mr_device_priv_data->is_tm_capable) {
ret = FAILED; goto out;
}
/*SRIOV get other instance in cluster if any*/ staticstruct
megasas_instance *megasas_get_peer_instance(struct megasas_instance *instance)
{ int i;
for (i = 0; i < MAX_MGMT_ADAPTERS; i++) { if (megasas_mgmt_info.instance[i] &&
(megasas_mgmt_info.instance[i] != instance) &&
megasas_mgmt_info.instance[i]->requestorId &&
megasas_mgmt_info.instance[i]->peerIsPresent &&
(memcmp((megasas_mgmt_info.instance[i]->clusterId),
instance->clusterId, MEGASAS_CLUSTER_ID_SIZE) == 0)) return megasas_mgmt_info.instance[i];
} return NULL;
}
/* Check for a second path that is currently UP */ int megasas_check_mpio_paths(struct megasas_instance *instance, struct scsi_cmnd *scmd)
{ struct megasas_instance *peer_instance = NULL; int retval = (DID_REQUEUE << 16);
/* First try waiting for commands to complete */ if (megasas_wait_for_outstanding_fusion(instance, reason,
&convert)) {
atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
dev_warn(&instance->pdev->dev, "resetting fusion " "adapter scsi%d.\n", instance->host->host_no); if (convert)
reason = 0;
if (megasas_dbg_lvl & OCR_DEBUG)
dev_info(&instance->pdev->dev, "\nPending SCSI commands:\n");
/* Now return commands back to the OS */ for (i = 0 ; i < instance->max_scsi_cmds; i++) {
cmd_fusion = fusion->cmd_list[i]; /*check for extra commands issued by driver*/ if (instance->adapter_type >= VENTURA_SERIES) {
r1_cmd = fusion->cmd_list[i + instance->max_fw_cmds];
megasas_return_cmd_fusion(instance, r1_cmd);
}
scmd_local = cmd_fusion->scmd; if (cmd_fusion->scmd) { if (megasas_dbg_lvl & OCR_DEBUG) {
sdev_printk(KERN_INFO,
cmd_fusion->scmd->device, "SMID: 0x%x\n",
cmd_fusion->index);
megasas_dump_fusion_io(cmd_fusion->scmd);
}
if (cmd_fusion->io_request->Function ==
MPI2_FUNCTION_SCSI_IO_REQUEST)
fpio_count++;
/* Let SR-IOV VF & PF sync up if there was a HB failure */ if (instance->requestorId && !reason) {
msleep(MEGASAS_OCR_SETTLE_TIME_VF);
do_adp_reset = false;
max_reset_tries = MEGASAS_SRIOV_MAX_RESET_TRIES_VF;
}
/* Now try to reset the chip */ for (i = 0; i < max_reset_tries; i++) { /* * Do adp reset and wait for * controller to transition to ready
*/ if (megasas_adp_reset_wait_for_ready(instance,
do_adp_reset, 1) == FAILED) continue;
/* Wait for FW to become ready */ if (megasas_transition_to_ready(instance, 1)) {
dev_warn(&instance->pdev->dev, "Failed to transition controller to ready for " "scsi%d.\n", instance->host->host_no); continue;
}
megasas_reset_reply_desc(instance);
megasas_fusion_update_can_queue(instance, OCR_CONTEXT);
if (megasas_ioc_init_fusion(instance)) { continue;
}
if (megasas_get_ctrl_info(instance)) {
dev_info(&instance->pdev->dev, "Failed from %s %d\n",
__func__, __LINE__); goto kill_hba;
}
megasas_refire_mgmt_cmd(instance,
(i == (MEGASAS_FUSION_MAX_RESET_TRIES - 1)
? 1 : 0));
/* Reset load balance info */ if (fusion->load_balance_info)
memset(fusion->load_balance_info, 0,
(sizeof(struct LD_LOAD_BALANCE_INFO) *
MAX_LOGICAL_DRIVES_EXT));
/* * Allocate host crash buffers to copy data from 1 MB DMA crash buffer * to host crash buffers
*/ if (instance->drv_buf_index == 0) { /* Buffer is already allocated for old Crash dump. * Do OCR and do not wait for crash dump collection
*/ if (instance->drv_buf_alloc) {
dev_info(&instance->pdev->dev, "earlier crash dump is " "not yet copied by application, ignoring this " "crash dump and initiating OCR\n");
status_reg |= MFI_STATE_CRASH_DUMP_DONE;
writel(status_reg,
&instance->reg_set->outbound_scratch_pad_0);
readl(&instance->reg_set->outbound_scratch_pad_0); return;
}
megasas_alloc_host_crash_buffer(instance);
dev_info(&instance->pdev->dev, "Number of host crash buffers " "allocated: %d\n", instance->drv_buf_alloc);
}
while (!(status_reg & MFI_STATE_CRASH_DUMP_DONE) &&
(wait < MEGASAS_WATCHDOG_WAIT_COUNT)) { if (!(status_reg & MFI_STATE_DMADONE)) { /* * Next crash dump buffer is not yet DMA'd by FW * Check after 10ms. Wait for 1 second for FW to * post the next buffer. If not bail out.
*/
wait++;
msleep(MEGASAS_WAIT_FOR_NEXT_DMA_MSECS);
status_reg = instance->instancet->read_fw_status_reg(
instance); continue;
}
if (fusion) { if (fusion->load_balance_info) { if (is_vmalloc_addr(fusion->load_balance_info))
vfree(fusion->load_balance_info); else
free_pages((ulong)fusion->load_balance_info,
fusion->load_balance_info_pages);
}
if (fusion->log_to_span) { if (is_vmalloc_addr(fusion->log_to_span))
vfree(fusion->log_to_span); else
free_pages((ulong)fusion->log_to_span,
fusion->log_to_span_pages);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.