blob: fc90a0a687b5b51c25421127f0bdf5d3a4c94221 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
* Copyright (c) 2009-2013 LSI Corporation
* Copyright (c) 2013-2016 Avago Technologies
* Copyright (c) 2016-2018 Broadcom Inc.
*
* FILE: megaraid_sas_fusion.c
*
* Authors: Broadcom Inc.
* Sumant Patro
* Adam Radford
* Kashyap Desai <kashyap.desai@broadcom.com>
* Sumit Saxena <sumit.saxena@broadcom.com>
*
* Send feedback to: megaraidlinux.pdl@broadcom.com
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/irq_poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_dbg.h>
#include <linux/dmi.h>
#include "megaraid_sas_fusion.h"
#include "megaraid_sas.h"
extern void
megasas_complete_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd, u8 alt_status);
int
wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
int seconds);
int
megasas_clear_intr_fusion(struct megasas_instance *instance);
int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
extern u32 megasas_dbg_lvl;
int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
int initial);
extern struct megasas_mgmt_info megasas_mgmt_info;
extern unsigned int resetwaittime;
extern unsigned int dual_qdepth_disable;
static void megasas_free_rdpq_fusion(struct megasas_instance *instance);
static void megasas_free_reply_fusion(struct megasas_instance *instance);
static inline
void megasas_configure_queue_sizes(struct megasas_instance *instance);
static void megasas_fusion_crash_dump(struct megasas_instance *instance);
/**
* 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 initates 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
*/
static inline bool 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",
(unsigned long long)start_addr,
(unsigned long long)end_addr);
return false;
}
return true;
}
/**
* megasas_enable_intr_fusion - Enables interrupts
* @instance: adapter's soft instance
*/
static void
megasas_enable_intr_fusion(struct megasas_instance *instance)
{
struct megasas_register_set __iomem *regs;
regs = instance->reg_set;
instance->mask_interrupts = 0;
/* For Thunderbolt/Invader also clear intr on enable */
writel(~0, &regs->outbound_intr_status);
readl(&regs->outbound_intr_status);
writel(~MFI_FUSION_ENABLE_INTERRUPT_MASK, &(regs)->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(&regs->outbound_intr_mask));
}
/**
* megasas_disable_intr_fusion - Disables interrupt
* @instance: adapter's soft instance
*/
static void
megasas_disable_intr_fusion(struct megasas_instance *instance)
{
u32 mask = 0xFFFFFFFF;
struct megasas_register_set __iomem *regs;
regs = instance->reg_set;
instance->mask_interrupts = 1;
writel(mask, &regs->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(&regs->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,
&regs->outbound_intr_status);
if (status & 1) {
writel(status, &regs->outbound_intr_status);
readl(&regs->outbound_intr_status);
return 1;
}
if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK))
return 0;
return 1;
}
static inline void
megasas_sdev_busy_inc(struct megasas_instance *instance,
struct scsi_cmnd *scmd)
{
if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
struct MR_PRIV_DEVICE *mr_device_priv_data =
scmd->device->hostdata;
atomic_inc(&mr_device_priv_data->sdev_priv_busy);
}
}
static inline void
megasas_sdev_busy_dec(struct megasas_instance *instance,
struct scsi_cmnd *scmd)
{
if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
struct MR_PRIV_DEVICE *mr_device_priv_data =
scmd->device->hostdata;
atomic_dec(&mr_device_priv_data->sdev_priv_busy);
}
}
static inline int
megasas_sdev_busy_read(struct megasas_instance *instance,
struct scsi_cmnd *scmd)
{
if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
struct MR_PRIV_DEVICE *mr_device_priv_data =
scmd->device->hostdata;
return atomic_read(&mr_device_priv_data->sdev_priv_busy);
}
return 0;
}
/**
* 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
*/
inline struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
*instance, u32 blk_tag)
{
struct fusion_context *fusion;
fusion = instance->ctrl_context;
return fusion->cmd_list[blk_tag];
}
/**
* megasas_return_cmd_fusion - Return a cmd to free command pool
* @instance: Adapter soft state
* @cmd: Command packet to be returned to free command pool
*/
inline void megasas_return_cmd_fusion(struct megasas_instance *instance,
struct megasas_cmd_fusion *cmd)
{
cmd->scmd = NULL;
memset(cmd->io_request, 0, MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE);
cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
cmd->cmd_completed = false;
}
/**
* megasas_write_64bit_req_desc - PCI writes 64bit request descriptor
* @instance: Adapter soft state
* @req_desc: 64bit Request descriptor
*/
static void
megasas_write_64bit_req_desc(struct megasas_instance *instance,
union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
{
#if defined(writeq) && defined(CONFIG_64BIT)
u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) |
le32_to_cpu(req_desc->u.low));
writeq(req_data, &instance->reg_set->inbound_low_queue_port);
#else
unsigned long flags;
spin_lock_irqsave(&instance->hba_lock, flags);
writel(le32_to_cpu(req_desc->u.low),
&instance->reg_set->inbound_low_queue_port);
writel(le32_to_cpu(req_desc->u.high),
&instance->reg_set->inbound_high_queue_port);
spin_unlock_irqrestore(&instance->hba_lock, flags);
#endif
}
/**
* 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.
*/
static void
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 skiped because underlying firmware has
* more resource than exposed to the OS.
*
*/
static void
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 (dual_qdepth_disable || !cur_max_fw_cmds)
cur_max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF;
else
ldio_threshold =
(instance->instancet->read_fw_status_reg(instance) & 0x00FFFF) - MEGASAS_FUSION_IOCTL_CMDS;
dev_info(&instance->pdev->dev,
"Current firmware supports maximum commands: %d\t LDIO threshold: %d\n",
cur_max_fw_cmds, ldio_threshold);
if (fw_boot_context == OCR_CONTEXT) {
cur_max_fw_cmds = cur_max_fw_cmds - 1;
if (cur_max_fw_cmds < instance->max_fw_cmds) {
instance->cur_can_queue =
cur_max_fw_cmds - (MEGASAS_FUSION_INTERNAL_CMDS +
MEGASAS_FUSION_IOCTL_CMDS);
instance->host->can_queue = instance->cur_can_queue;
instance->ldio_threshold = ldio_threshold;
}
} else {
instance->max_fw_cmds = cur_max_fw_cmds;
instance->ldio_threshold = ldio_threshold;
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;
}
}
static inline void
megasas_get_msix_index(struct megasas_instance *instance,
struct scsi_cmnd *scmd,
struct megasas_cmd_fusion *cmd,
u8 data_arms)
{
if (instance->perf_mode == MR_BALANCED_PERF_MODE &&
(megasas_sdev_busy_read(instance, scmd) >
(data_arms * MR_DEVICE_HIGH_IOPS_DEPTH))) {
cmd->request_desc->SCSIIO.MSIxIndex =
mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) /
MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start);
} else if (instance->msix_load_balance) {
cmd->request_desc->SCSIIO.MSIxIndex =
(mega_mod64(atomic64_add_return(1, &instance->total_io_count),
instance->msix_vectors));
} else if (instance->host->nr_hw_queues > 1) {
u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
cmd->request_desc->SCSIIO.MSIxIndex = blk_mq_unique_tag_to_hwq(tag) +
instance->low_latency_index_start;
} else {
cmd->request_desc->SCSIIO.MSIxIndex =
instance->reply_map[raw_smp_processor_id()];
}
}
/**
* 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
*
*/
static int 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;
fusion = instance->ctrl_context;
max_cmd = instance->max_fw_cmds;
sense_sz = instance->max_mpt_cmds * SCSI_SENSE_BUFFERSIZE;
fusion->sg_dma_pool =
dma_pool_create("mr_sg", &instance->pdev->dev,
instance->max_chain_frame_sz,
MR_DEFAULT_NVME_PAGE_SIZE, 0);
/* SCSI_SENSE_BUFFERSIZE = 96 bytes */
fusion->sense_dma_pool =
dma_pool_create("mr_sense", &instance->pdev->dev,
sense_sz, 64, 0);
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.
*/
if (!megasas_check_same_4gb_region(instance, fusion->sense_phys_addr,
sense_sz)) {
dma_pool_free(fusion->sense_dma_pool, fusion->sense,
fusion->sense_phys_addr);
fusion->sense = NULL;
dma_pool_destroy(fusion->sense_dma_pool);
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);
offset = SCSI_SENSE_BUFFERSIZE * i;
cmd->sense = (u8 *)fusion->sense + offset;
cmd->sense_phys_addr = fusion->sense_phys_addr + offset;
if (!cmd->sg_frame) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
}
/* create sense buffer for the raid 1/10 fp */
for (i = max_cmd; i < instance->max_mpt_cmds; i++) {
cmd = fusion->cmd_list[i];
offset = SCSI_SENSE_BUFFERSIZE * i;
cmd->sense = (u8 *)fusion->sense + offset;
cmd->sense_phys_addr = fusion->sense_phys_addr + offset;
}
return 0;
}
static int
megasas_alloc_cmdlist_fusion(struct megasas_instance *instance)
{
u32 max_mpt_cmd, i, j;
struct fusion_context *fusion;
fusion = instance->ctrl_context;
max_mpt_cmd = instance->max_mpt_cmds;
/*
* 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;
}
}
return 0;
}
static int
megasas_alloc_request_fusion(struct megasas_instance *instance)
{
struct fusion_context *fusion;
fusion = instance->ctrl_context;
retry_alloc:
fusion->io_request_frames_pool =
dma_pool_create("mr_ioreq", &instance->pdev->dev,
fusion->io_frames_alloc_sz, 16, 0);
if (!fusion->io_request_frames_pool) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
fusion->io_request_frames =
dma_pool_alloc(fusion->io_request_frames_pool,
GFP_KERNEL | __GFP_NOWARN,
&fusion->io_request_frames_phys);
if (!fusion->io_request_frames) {
if (instance->max_fw_cmds >= (MEGASAS_REDUCE_QD_COUNT * 2)) {
instance->max_fw_cmds -= MEGASAS_REDUCE_QD_COUNT;
dma_pool_destroy(fusion->io_request_frames_pool);
megasas_configure_queue_sizes(instance);
goto retry_alloc;
} else {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
}
if (!megasas_check_same_4gb_region(instance,
fusion->io_request_frames_phys,
fusion->io_frames_alloc_sz)) {
dma_pool_free(fusion->io_request_frames_pool,
fusion->io_request_frames,
fusion->io_request_frames_phys);
fusion->io_request_frames = NULL;
dma_pool_destroy(fusion->io_request_frames_pool);
fusion->io_request_frames_pool =
dma_pool_create("mr_ioreq_align",
&instance->pdev->dev,
fusion->io_frames_alloc_sz,
roundup_pow_of_two(fusion->io_frames_alloc_sz),
0);
if (!fusion->io_request_frames_pool) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
fusion->io_request_frames =
dma_pool_alloc(fusion->io_request_frames_pool,
GFP_KERNEL | __GFP_NOWARN,
&fusion->io_request_frames_phys);
if (!fusion->io_request_frames) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
}
fusion->req_frames_desc =
dma_alloc_coherent(&instance->pdev->dev,
fusion->request_alloc_sz,
&fusion->req_frames_desc_phys, GFP_KERNEL);
if (!fusion->req_frames_desc) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
return 0;
}
static int
megasas_alloc_reply_fusion(struct megasas_instance *instance)
{
int i, count;
struct fusion_context *fusion;
union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc;
fusion = instance->ctrl_context;
count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
count += instance->iopoll_q_count;
fusion->reply_frames_desc_pool =
dma_pool_create("mr_reply", &instance->pdev->dev,
fusion->reply_alloc_sz * count, 16, 0);
if (!fusion->reply_frames_desc_pool) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
fusion->reply_frames_desc[0] =
dma_pool_alloc(fusion->reply_frames_desc_pool,
GFP_KERNEL, &fusion->reply_frames_desc_phys[0]);
if (!fusion->reply_frames_desc[0]) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
if (!megasas_check_same_4gb_region(instance,
fusion->reply_frames_desc_phys[0],
(fusion->reply_alloc_sz * count))) {
dma_pool_free(fusion->reply_frames_desc_pool,
fusion->reply_frames_desc[0],
fusion->reply_frames_desc_phys[0]);
fusion->reply_frames_desc[0] = NULL;
dma_pool_destroy(fusion->reply_frames_desc_pool);
fusion->reply_frames_desc_pool =
dma_pool_create("mr_reply_align",
&instance->pdev->dev,
fusion->reply_alloc_sz * count,
roundup_pow_of_two(fusion->reply_alloc_sz * count),
0);
if (!fusion->reply_frames_desc_pool) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
fusion->reply_frames_desc[0] =
dma_pool_alloc(fusion->reply_frames_desc_pool,
GFP_KERNEL,
&fusion->reply_frames_desc_phys[0]);
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);
return 0;
}
static int
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;
fusion = instance->ctrl_context;
chunk_size = fusion->reply_alloc_sz * RDPQ_MAX_INDEX_IN_ONE_CHUNK;
array_size = sizeof(struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY) *
MAX_MSIX_QUEUES_FUSION;
fusion->rdpq_virt = dma_alloc_coherent(&instance->pdev->dev,
array_size, &fusion->rdpq_phys,
GFP_KERNEL);
if (!fusion->rdpq_virt) {
dev_err(&instance->pdev->dev,
"Failed from %s %d\n", __func__, __LINE__);
return -ENOMEM;
}
msix_count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
msix_count += instance->iopoll_q_count;
fusion->reply_frames_desc_pool = dma_pool_create("mr_rdpq",
&instance->pdev->dev,
chunk_size, 16, 0);
fusion->reply_frames_desc_pool_align =
dma_pool_create("mr_rdpq_align",
&instance->pdev->dev,
chunk_size,
roundup_pow_of_two(chunk_size),
0);
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]);
rdpq_chunk_virt[i] =
dma_pool_alloc(fusion->reply_frames_desc_pool_align,
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;
}
fusion->rdpq_tracker[i].dma_pool_ptr =
fusion->reply_frames_desc_pool_align;
} else {
fusion->rdpq_tracker[i].dma_pool_ptr =
fusion->reply_frames_desc_pool;
}
fusion->rdpq_tracker[i].pool_entry_phys = rdpq_chunk_phys[i];
fusion->rdpq_tracker[i].pool_entry_virt = rdpq_chunk_virt[i];
}
for (k = 0; k < dma_alloc_count; k++) {
for (i = 0; i < RDPQ_MAX_INDEX_IN_ONE_CHUNK; i++) {
abs_index = (k * RDPQ_MAX_INDEX_IN_ONE_CHUNK) + i;
if (abs_index == msix_count)
break;
offset = fusion->reply_alloc_sz * i;
fusion->rdpq_virt[abs_index].RDPQBaseAddress =
cpu_to_le64(rdpq_chunk_phys[k] + offset);
fusion->reply_frames_desc_phys[abs_index] =
rdpq_chunk_phys[k] + offset;
fusion->reply_frames_desc[abs_index] =
(union MPI2_REPLY_DESCRIPTORS_UNION *)((u8 *)rdpq_chunk_virt[k] + offset);
reply_desc = fusion->reply_frames_desc[abs_index];
for (j = 0; j < fusion->reply_q_depth; j++, reply_desc++)
reply_desc->Words = ULLONG_MAX;
}
}
return 0;
}
static void
megasas_free_rdpq_fusion(struct megasas_instance *instance) {
int i;
struct fusion_context *fusion;
fusion = instance->ctrl_context;
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);
}
dma_pool_destroy(fusion->reply_frames_desc_pool);
dma_pool_destroy(fusion->reply_frames_desc_pool_align);
if (fusion->rdpq_virt)
dma_free_coherent(&instance->pdev->dev,
sizeof(struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY) * MAX_MSIX_QUEUES_FUSION,
fusion->rdpq_virt, fusion->rdpq_phys);
}
static void
megasas_free_reply_fusion(struct megasas_instance *instance) {
struct fusion_context *fusion;
fusion = instance->ctrl_context;
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.
*/
static int
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;
return 0;
fail_exit:
megasas_free_cmds_fusion(instance);
return -ENOMEM;
}
/**
* 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 (frame_hdr->cmd_status == MFI_STAT_INVALID_STATUS)
return DCMD_TIMEOUT;
else if (frame_hdr->cmd_status == MFI_STAT_OK)
return DCMD_SUCCESS;
else
return DCMD_FAILED;
}
/**
* megasas_ioc_init_fusion - Initializes the FW
* @instance: Adapter soft state
*
* Issues the IOC Init cmd
*/
int
megasas_ioc_init_fusion(struct megasas_instance *instance)
{
struct megasas_init_frame *init_frame;
struct MPI2_IOC_INIT_REQUEST *IOCInitMessage = NULL;
dma_addr_t ioc_init_handle;
struct megasas_cmd *cmd;
u8 ret, cur_rdpq_mode;
struct fusion_context *fusion;
union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc;
int i;
struct megasas_header *frame_hdr;
const char *sys_info;
MFI_CAPABILITIES *drv_ops;
u32 scratch_pad_1;
ktime_t time;
bool cur_fw_64bit_dma_capable;
bool cur_intr_coalescing;
fusion = instance->ctrl_context;
ioc_init_handle = fusion->ioc_init_request_phys;
IOCInitMessage = fusion->ioc_init_request;
cmd = fusion->ioc_init_cmd;
scratch_pad_1 = megasas_readl
(instance, &instance->reg_set->outbound_scratch_pad_1);
cur_rdpq_mode = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 1 : 0;
if (instance->adapter_type == INVADER_SERIES) {
cur_fw_64bit_dma_capable =
(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET) ? true : false;
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;
}
cur_intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ?
true : false;
if ((instance->low_latency_index_start ==
MR_HIGH_IOPS_QUEUE_COUNT) && cur_intr_coalescing)
instance->perf_mode = MR_BALANCED_PERF_MODE;
dev_info(&instance->pdev->dev, "Performance mode :%s (latency index = %d)\n",
MEGASAS_PERF_MODE_2STR(instance->perf_mode),
instance->low_latency_index_start);
instance->fw_sync_cache_support = (scratch_pad_1 &
MR_CAN_HANDLE_SYNC_CACHE_OFFSET) ? 1 : 0;
dev_info(&instance->pdev->dev, "FW supports sync cache\t: %s\n",
instance->fw_sync_cache_support ? "Yes" : "No");
memset(IOCInitMessage, 0, sizeof(struct MPI2_IOC_INIT_REQUEST));
IOCInitMessage->Function = MPI2_FUNCTION_IOC_INIT;
IOCInitMessage->WhoInit = MPI2_WHOINIT_HOST_DRIVER;
IOCInitMessage->MsgVersion = cpu_to_le16(MPI2_VERSION);
IOCInitMessage->HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
IOCInitMessage->SystemRequestFrameSize = cpu_to_le16(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE / 4);
IOCInitMessage->ReplyDescriptorPostQueueDepth = cpu_to_le16(fusion->reply_q_depth);
IOCInitMessage->ReplyDescriptorPostQueueAddress = instance->is_rdpq ?
cpu_to_le64(fusion->rdpq_phys) :
cpu_to_le64(fusion->reply_frames_desc_phys[0]);
IOCInitMessage->MsgFlags = instance->is_rdpq ?
MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE : 0;
IOCInitMessage->SystemRequestFrameBaseAddress = cpu_to_le64(fusion->io_request_frames_phys);
IOCInitMessage->SenseBufferAddressHigh = cpu_to_le32(upper_32_bits(fusion->sense_phys_addr));
IOCInitMessage->HostMSIxVectors = instance->msix_vectors + instance->iopoll_q_count;
IOCInitMessage->HostPageSize = MR_DEFAULT_NVME_PAGE_SHIFT;
time = ktime_get_real();
/* Convert to milliseconds as per FW requirement */
IOCInitMessage->TimeStamp = cpu_to_le64(ktime_to_ms(time));
init_frame = (struct megasas_init_frame *)cmd->frame;
memset(init_frame, 0, IOC_INIT_FRAME_SIZE);
frame_hdr = &cmd->frame->hdr;
frame_hdr->cmd_status = 0xFF;
frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
init_frame->cmd = MFI_CMD_INIT;
init_frame->cmd_status = 0xFF;
drv_ops = (MFI_CAPABILITIES *) &(init_frame->driver_operations);
/* driver support Extended MSIX */
if (instance->adapter_type >= INVADER_SERIES)
drv_ops->mfi_capabilities.support_additional_msix = 1;
/* driver supports HA / Remote LUN over Fast Path interface */
drv_ops->mfi_capabilities.support_fp_remote_lun = 1;
drv_ops->mfi_capabilities.support_max_255lds = 1;
drv_ops->mfi_capabilities.support_ndrive_r1_lb = 1;
drv_ops->mfi_capabilities.security_protocol_cmds_fw = 1;
if (instance->max_chain_frame_sz > MEGASAS_CHAIN_FRAME_SZ_MIN)
drv_ops->mfi_capabilities.support_ext_io_size = 1;
drv_ops->mfi_capabilities.support_fp_rlbypass = 1;
if (!dual_qdepth_disable)
drv_ops->mfi_capabilities.support_ext_queue_depth = 1;
drv_ops->mfi_capabilities.support_qd_throttling = 1;
drv_ops->mfi_capabilities.support_pd_map_target_id = 1;
drv_ops->mfi_capabilities.support_nvme_passthru = 1;
drv_ops->mfi_capabilities.support_fw_exposed_dev_list = 1;
if (instance->consistent_mask_64bit)
drv_ops->mfi_capabilities.support_64bit_mode = 1;
/* Convert capability to LE32 */
cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities);
sys_info = dmi_get_system_info(DMI_PRODUCT_UUID);
if (instance->system_info_buf && sys_info) {
memcpy(instance->system_info_buf->systemId, sys_info,
strlen(sys_info) > 64 ? 64 : strlen(sys_info));
instance->system_info_buf->systemIdLength =
strlen(sys_info) > 64 ? 64 : strlen(sys_info);
init_frame->system_info_lo = cpu_to_le32(lower_32_bits(instance->system_info_h));
init_frame->system_info_hi = cpu_to_le32(upper_32_bits(instance->system_info_h));
}
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));
/*
* 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;
}
req_desc.u.low = cpu_to_le32(lower_32_bits(cmd->frame_phys_addr));
req_desc.u.high = cpu_to_le32(upper_32_bits(cmd->frame_phys_addr));
req_desc.MFAIo.RequestFlags =
(MEGASAS_REQ_DESCRIPT_FLAGS_MFA <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
/*
* disable the intr before firing the init frame
*/
instance->instancet->disable_intr(instance);
for (i = 0; i < (10 * 1000); i += 20) {
if (megasas_readl(instance, &instance->reg_set->doorbell) & 1)
msleep(20);
else
break;
}
/* For AERO also, IOC_INIT requires 64 bit descriptor write */
megasas_write_64bit_req_desc(instance, &req_desc);
wait_and_poll(instance, cmd, MFI_IO_TIMEOUT_SECS);
frame_hdr = &cmd->frame->hdr;
if (frame_hdr->cmd_status != 0) {
ret = 1;
goto fail_fw_init;
}
if (instance->adapter_type >= AERO_SERIES) {
scratch_pad_1 = megasas_readl
(instance, &instance->reg_set->outbound_scratch_pad_1);
instance->atomic_desc_support =
(scratch_pad_1 & MR_ATOMIC_DESCRIPTOR_SUPPORT_OFFSET) ? 1 : 0;
dev_info(&instance->pdev->dev, "FW supports atomic descriptor\t: %s\n",
instance->atomic_desc_support ? "Yes" : "No");
}
return 0;
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;
pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id & 1)];
pd_seq_h = fusion->pd_seq_phys[(instance->pd_seq_map_id & 1)];
pd_seq_map_sz = struct_size(pd_sync, seq, MAX_PHYSICAL_DEVICES - 1);
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_err(&instance->pdev->dev,
"Could not get mfi cmd. Fail from %s %d\n",
__func__, __LINE__);
return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
memset(pd_sync, 0, pd_seq_map_sz);
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
if (pend) {
dcmd->mbox.b[0] = MEGASAS_DCMD_MBOX_PEND_FLAG;
dcmd->flags = MFI_FRAME_DIR_WRITE;
instance->jbod_seq_cmd = cmd;
} else {
dcmd->flags = MFI_FRAME_DIR_READ;
}
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0xFF;
dcmd->sge_count = 1;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
dcmd->data_xfer_len = cpu_to_le32(pd_seq_map_sz);
dcmd->opcode = cpu_to_le32(MR_DCMD_SYSTEM_PD_MAP_GET_INFO);
megasas_set_dma_settings(instance, dcmd, pd_seq_h, pd_seq_map_sz);
if (pend) {
instance->instancet->issue_dcmd(instance, cmd);
return 0;
}
/* 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
*/
static int
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;
}
dcmd = &cmd->frame->dcmd;
size_map_info = fusion->current_map_sz;
ci = (void *) fusion->ld_map[(instance->map_id & 1)];
ci_h = fusion->ld_map_phys[(instance->map_id & 1)];
if (!ci) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem for ld_map_info\n");
megasas_return_cmd(instance, cmd);
return -ENOMEM;
}
memset(ci, 0, fusion->max_map_sz);
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_READ;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
dcmd->data_xfer_len = cpu_to_le32(size_map_info);
dcmd->opcode = cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO);
megasas_set_dma_settings(instance, dcmd, ci_h, size_map_info);
if (!instance->mask_interrupts)
ret = megasas_issue_blocked_cmd(instance, cmd,
MFI_IO_TIMEOUT_SECS);
else
ret = megasas_issue_polled(instance, cmd);
if (ret == DCMD_TIMEOUT)
dev_warn(&instance->pdev->dev,
"%s DCMD timed out, RAID map is disabled\n",
__func__);
megasas_return_cmd(instance, cmd);
return ret;
}
u8
megasas_get_map_info(struct megasas_instance *instance)
{
struct fusion_context *fusion = instance->ctrl_context;
fusion->fast_path_io = 0;
if (!megasas_get_ld_map_info(instance)) {
if (MR_ValidateMapInfo(instance, instance->map_id)) {
fusion->fast_path_io = 1;
return 0;
}
}
return 1;
}
/*
* 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);
return 1;
}
map = fusion->ld_drv_map[instance->map_id & 1];
num_lds = le16_to_cpu(map->raidMap.ldCount);
dcmd = &cmd->frame->dcmd;
memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
ci = (struct MR_LD_TARGET_SYNC *)
fusion->ld_map[(instance->map_id - 1) & 1];
memset(ci, 0, fusion->max_map_sz);
ci_h = fusion->ld_map_phys[(instance->map_id - 1) & 1];
ld_sync = (struct MR_LD_TARGET_SYNC *)ci;
for (i = 0; i < num_lds; i++, ld_sync++) {
raid = MR_LdRaidGet(i, map);
ld_sync->targetId = MR_GetLDTgtId(i, map);
ld_sync->seqNum = raid->seqNum;
}
size_map_info = fusion->current_map_sz;
dcmd->cmd = MFI_CMD_DCMD;
dcmd->cmd_status = 0xFF;
dcmd->sge_count = 1;
dcmd->flags = MFI_FRAME_DIR_WRITE;
dcmd->timeout = 0;
dcmd->pad_0 = 0;
dcmd->data_xfer_len = cpu_to_le32(size_map_info);
dcmd->mbox.b[0] = num_lds;
dcmd->mbox.b[1] = MEGASAS_DCMD_MBOX_PEND_FLAG;
dcmd->opcode = cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO);
megasas_set_dma_settings(instance, dcmd, ci_h, size_map_info);
instance->map_update_cmd = cmd;
instance->instancet->issue_dcmd(instance, cmd);
return 0;
}
/*
* meagasas_display_intel_branding - Display branding string
* @instance: per adapter object
*
* Return nothing.
*/
static void
megasas_display_intel_branding(struct megasas_instance *instance)
{
if (instance->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL)
return;
switch (instance->pdev->device) {
case PCI_DEVICE_ID_LSI_INVADER:
switch (instance->pdev->subsystem_device) {
case MEGARAID_INTEL_RS3DC080_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3DC080_BRANDING);
break;
case MEGARAID_INTEL_RS3DC040_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3DC040_BRANDING);
break;
case MEGARAID_INTEL_RS3SC008_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3SC008_BRANDING);
break;
case MEGARAID_INTEL_RS3MC044_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3MC044_BRANDING);
break;
default:
break;
}
break;
case PCI_DEVICE_ID_LSI_FURY:
switch (instance->pdev->subsystem_device) {
case MEGARAID_INTEL_RS3WC080_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3WC080_BRANDING);
break;
case MEGARAID_INTEL_RS3WC040_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RS3WC040_BRANDING);
break;
default:
break;
}
break;
case PCI_DEVICE_ID_LSI_CUTLASS_52:
case PCI_DEVICE_ID_LSI_CUTLASS_53:
switch (instance->pdev->subsystem_device) {
case MEGARAID_INTEL_RMS3BC160_SSDID:
dev_info(&instance->pdev->dev, "scsi host %d: %s\n",
instance->host->host_no,
MEGARAID_INTEL_RMS3BC160_BRANDING);
break;
default:
break;
}
break;
default:
break;
}
}
/**
* megasas_allocate_raid_maps - Allocate memory for RAID maps
* @instance: Adapter soft state
*
* return: if success: return 0
* failed: return -ENOMEM
*/
static inline int megasas_allocate_raid_maps(struct megasas_instance *instance)
{
struct fusion_context *fusion;
int i = 0;
fusion = instance->ctrl_context;
fusion->drv_map_pages = get_order(fusion->drv_map_sz);
for (i = 0; i < 2; i++) {
fusion->ld_map[i] = NULL;
fusion->ld_drv_map[i] = (void *)
__get_free_pages(__GFP_ZERO | GFP_KERNEL,
fusion->drv_map_pages);
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;
}
}
return 0;
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);
}
}
return -ENOMEM;
}
/**
* megasas_configure_queue_sizes - Calculate size of request desc queue,
* reply desc queue,
* IO request frame queue, set can_queue.
* @instance: Adapter soft state
* @return: void
*/
static inline
void megasas_configure_queue_sizes(struct megasas_instance *instance)
{
struct fusion_context *fusion;
u16 max_cmd;
fusion = instance->ctrl_context;
max_cmd = instance->max_fw_cmds;
if (instance->adapter_type >= VENTURA_SERIES)
instance->max_mpt_cmds = instance->max_fw_cmds * RAID_1_PEER_CMDS;
else
instance->max_mpt_cmds = instance->max_fw_cmds;
instance->max_scsi_cmds = instance->max_fw_cmds - instance->max_mfi_cmds;
instance->cur_can_queue = instance->max_scsi_cmds;
instance->host->can_queue = instance->cur_can_queue;
fusion->reply_q_depth = 2 * ((max_cmd + 1 + 15) / 16) * 16;
fusion->request_alloc_sz = sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *
instance->max_mpt_cmds;
fusion->reply_alloc_sz = sizeof(union MPI2_REPLY_DESCRIPTORS_UNION) *
(fusion->reply_q_depth);
fusion->io_frames_alloc_sz = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE +
(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE
* (instance->max_mpt_cmds + 1)); /* Extra 1 for SMID 0 */
}
static int megasas_alloc_ioc_init_frame(struct megasas_instance *instance)
{
struct fusion_context *fusion;
struct megasas_cmd *cmd;
fusion = instance->ctrl_context;
cmd = kzalloc(sizeof(struct megasas_cmd), GFP_KERNEL);
if (!cmd) {
dev_err(&instance->pdev->dev, "Failed from func: %s line: %d\n",
__func__, __LINE__);
return -ENOMEM;
}
cmd->frame = dma_alloc_coherent(&instance->pdev->dev,
IOC_INIT_FRAME_SIZE,
&cmd->frame_phys_addr, GFP_KERNEL);
if (!cmd->frame) {
dev_err(&instance->pdev->dev, "Failed from func: %s line: %d\n",
__func__, __LINE__);
kfree(cmd);
return -ENOMEM;
}
fusion->ioc_init_cmd = cmd;
return 0;
}
/**
* megasas_free_ioc_init_cmd - Free IOC INIT command frame
* @instance: Adapter soft state
*/
static inline void megasas_free_ioc_init_cmd(struct megasas_instance *instance)
{
struct fusion_context *fusion;
fusion = instance->ctrl_context;
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;
fusion = instance->ctrl_context;
megasas_fusion_update_can_queue(instance, PROBE_CONTEXT);
/*
* 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;
}
fusion->max_sge_in_main_msg =
(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE
- offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16;
fusion->max_sge_in_chain =
instance->max_chain_frame_sz
/ sizeof(union MPI2_SGE_IO_UNION);
instance->max_num_sge =
rounddown_pow_of_two(fusion->max_sge_in_main_msg
+ fusion->max_sge_in_chain - 2);
/* Used for pass thru MFI frame (DCMD) */
fusion->chain_offset_mfi_pthru =
offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL)/16;
fusion->chain_offset_io_request =
(MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE -
sizeof(union MPI2_SGE_IO_UNION))/16;
count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
count += instance->iopoll_q_count;
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))
return 1;
/*
* 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;
if (megasas_ioc_init_fusion(instance))
goto fail_ioc_init;
} else {
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;
}
instance->flag_ieee = 1;
instance->r1_ldio_hint_default = MR_R1_LDIO_PIGGYBACK_DEFAULT;
instance->threshold_reply_count = instance->max_fw_cmds / 4;
fusion->fast_path_io = 0;
if (megasas_allocate_raid_maps(instance))
goto fail_ioc_init;
if (!megasas_get_map_info(instance))
megasas_sync_map_info(instance);
return 0;
fail_ioc_init:
megasas_free_cmds_fusion(instance);
fail_alloc_cmds:
megasas_free_cmds(instance);
fail_alloc_mfi_cmds:
megasas_free_ioc_init_cmd(instance);
return 1;
}
/**
* megasas_fault_detect_work - Worker function of
* FW fault handling workqueue.
* @work: FW fault work struct
*/
static void
megasas_fault_detect_work(struct work_struct *work)
{
struct megasas_instance *instance =
container_of(work, struct megasas_instance,
fw_fault_work.work);
u32 fw_state, dma_state, status;
/* 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;
INIT_DELAYED_WORK(&instance->fw_fault_work, megasas_fault_detect_work);
snprintf(instance->fault_handler_work_q_name,
sizeof(instance->fault_handler_work_q_name),
"poll_megasas%d_status", instance->host->host_no);
instance->fw_fault_work_q =
create_singlethread_workqueue(instance->fault_handler_work_q_name);
if (!instance->fw_fault_work_q) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n",
__func__, __LINE__);
return FAILED;
}
queue_delayed_work(instance->fw_fault_work_q,
&instance->fw_fault_work,
msecs_to_jiffies(MEGASAS_WATCHDOG_THREAD_INTERVAL));
return SUCCESS;
}
void
megasas_fusion_stop_watchdog(struct megasas_instance *instance)
{
struct workqueue_struct *wq;
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
*/
static void
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;
cmd_type = megasas_cmd_type(scmd);
switch (status) {
case MFI_STAT_OK:
scmd->result = DID_OK << 16;
break;
case MFI_STAT_SCSI_IO_FAILED:
case MFI_STAT_LD_INIT_IN_PROGRESS:
scmd->result = (DID_ERROR << 16) | ext_status;
break;
case MFI_STAT_SCSI_DONE_WITH_ERROR:
scmd->result = (DID_OK << 16) | ext_status;
if (ext_status == SAM_STAT_CHECK_CONDITION) {
memset(scmd->sense_buffer, 0,
SCSI_SENSE_BUFFERSIZE);
memcpy(scmd->sense_buffer, sense,
SCSI_SENSE_BUFFERSIZE);
}
/*
* 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
*/
static bool
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;
mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
MR_DEFAULT_NVME_PAGE_SIZE);
data_length = scsi_bufflen(scmd);
sg_scmd = scsi_sglist(scmd);
/*
* 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
*/
if (data_length > (mr_nvme_pg_size * 5)) {
build_prp = true;
} else if ((data_length > (mr_nvme_pg_size * 4)) &&
(data_length <= (mr_nvme_pg_size * 5))) {
/* check if 1st SG entry size is < residual beyond 4 pages */
if (sg_dma_len(sg_scmd) < (data_length - (mr_nvme_pg_size * 4)))
build_prp = true;
}
return build_prp;
}
/**
* megasas_make_prp_nvme -
* Prepare PRPs(Physical Region Page)- SGLs specific to NVMe drives only
*
* @instance: Adapter soft state
* @scmd: SCSI command from the mid-layer
* @sgl_ptr: SGL to be filled in
* @cmd: Fusion command frame
* @sge_count: scatter gather element count.
*
* Returns: true: PRPs are built
* false: IEEE SGLs needs to be built
*/
static bool
megasas_make_prp_nvme(struct megasas_instance *instance, struct scsi_cmnd *scmd,
struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr,
struct megasas_cmd_fusion *cmd, int sge_count)
{
int sge_len, offset, num_prp_in_chain = 0;
struct MPI25_IEEE_SGE_CHAIN64 *main_chain_element, *ptr_first_sgl;
u64 *ptr_sgl;
dma_addr_t ptr_sgl_phys;
u64 sge_addr;
u32 page_mask, page_mask_result;
struct scatterlist *sg_scmd;
u32 first_prp_len;
bool build_prp = false;
int data_len = scsi_bufflen(scmd);
u32 mr_nvme_pg_size = max_t(u32, instance->nvme_page_size,
MR_DEFAULT_NVME_PAGE_SIZE);
build_prp = megasas_is_prp_possible(instance, scmd, sge_count);
if (!build_prp)
return false;
/*
* 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));
main_chain_element->Address = cpu_to_le64(ptr_sgl_phys);
main_chain_element->NextChainOffset = 0;
main_chain_element->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT |
IEEE_SGE_FLAGS_SYSTEM_ADDR |
MPI26_IEEE_SGE_FLAGS_NSF_NVME_PRP;
/* 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);
offset = (u32)(sge_addr & page_mask);
first_prp_len = mr_nvme_pg_size - offset;
ptr_first_sgl->Address = cpu_to_le64(sge_addr);
ptr_first_sgl->Length = cpu_to_le32(first_prp_len);
data_len -= first_prp_len;
if (sge_len > first_prp_len) {
sge_addr += first_prp_len;
sge_len -= first_prp_len;
} else if (sge_len == first_prp_len) {
sg_scmd = sg_next(sg_scmd);
sge_addr = sg_dma_address(sg_scmd);
sge_len = sg_dma_len(sg_scmd);
}
for (;;) {
offset = (u32)(sge_addr & page_mask);
/* Put PRP pointer due to page boundary*/
page_mask_result = (uintptr_t)(ptr_sgl + 1) & page_mask;
if (unlikely(!page_mask_result)) {
scmd_printk(KERN_NOTICE,
scmd, "page boundary ptr_sgl: 0x%p\n",
ptr_sgl);
ptr_sgl_phys += 8;
*ptr_sgl = cpu_to_le64(ptr_sgl_phys);
ptr_sgl++;
num_prp_in_chain++;
}
*ptr_sgl = cpu_to_le64(sge_addr);
ptr_sgl++;
ptr_sgl_phys += 8;
num_prp_in_chain++;
sge_addr += mr_nvme_pg_size;
sge_len -= mr_nvme_pg_size;
data_len -= mr_nvme_pg_size;
if (data_len <= 0)
break;
if (sge_len > 0)
continue;
sg_scmd = sg_next(sg_scmd);
sge_addr = sg_dma_address(sg_scmd);
sge_len = sg_dma_len(sg_scmd);
}
main_chain_element->Length =
cpu_to_le32(num_prp_in_chain * sizeof(u64));
return build_prp;
}
/**
* 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
*
*/
static void
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;
fusion = instance->ctrl_context;
if (instance->adapter_type >= INVADER_SERIES) {
struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end = sgl_ptr;
sgl_ptr_end += fusion->max_sge_in_main_msg - 1;
sgl_ptr_end->Flags = 0;
}
scsi_for_each_sg(scp, os_sgl, sge_count, i) {
sgl_ptr->Length = cpu_to_le32(sg_dma_len(os_sgl));
sgl_ptr->Address = cpu_to_le64(sg_dma_address(os_sgl));
sgl_ptr->Flags = 0;
if (instance->adapter_type >= INVADER_SERIES)
if (i == sge_count - 1)
sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST;
sgl_ptr++;
sg_processed = i + 1;
if ((sg_processed == (fusion->max_sge_in_main_msg - 1)) &&
(sge_count > fusion->max_sge_in_main_msg)) {
struct MPI25_IEEE_SGE_CHAIN64 *sg_chain;
if (instance->adapter_type >= INVADER_SERIES) {
if ((le16_to_cpu(cmd->io_request->IoFlags) &
MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) !=
MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH)
cmd->io_request->ChainOffset =
fusion->
chain_offset_io_request;
else
cmd->io_request->ChainOffset = 0;
} else
cmd->io_request->ChainOffset =
fusion->chain_offset_io_request;
sg_chain = sgl_ptr;
/* Prepare chain element */
sg_chain->NextChainOffset = 0;
if (instance->adapter_type >= INVADER_SERIES)
sg_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT;
else
sg_chain->Flags =
(IEEE_SGE_FLAGS_CHAIN_ELEMENT |
MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR);
sg_chain->Length = cpu_to_le32((sizeof(union MPI2_SGE_IO_UNION) * (sge_count - sg_processed)));
sg_chain->Address = cpu_to_le64(cmd->sg_frame_phys_addr);
sgl_ptr =
(struct MPI25_IEEE_SGE_CHAIN64 *)cmd->sg_frame;
memset(sgl_ptr, 0, instance->max_chain_frame_sz);
}
}
}
/**
* 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;
sgl_chain64 = (struct MPI25_IEEE_SGE_CHAIN64 *)&cmd->io_request->SGL;
if ((le16_to_cpu(cmd->io_request->IoFlags) &
MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) &&
(cmd->pd_interface == NVME_PD))
build_prp = megasas_make_prp_nvme(instance, scp, sgl_chain64,
cmd, sge_count);
if (!build_prp)
megasas_make_sgl_fusion(instance, scp, sgl_chain64,
cmd, sge_count);
return sge_count;
}
/**
* megasas_set_pd_lba - Sets PD LBA
* @io_request: IO request
* @cdb_len: cdb length
* @io_info: IO information
* @scp: SCSI command
* @local_map_ptr: Raid map
* @ref_tag: Primary reference tag
*
* Used to set the PD LBA in CDB for FP IOs
*/
static void
megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len,
struct IO_REQUEST_INFO *io_info, struct scsi_cmnd *scp,
struct MR_DRV_RAID_MAP_ALL *local_map_ptr, u32 ref_tag)
{
struct MR_LD_RAID *raid;
u16 ld;
u64 start_blk = io_info->pdBlock;
u8 *cdb = io_request->CDB.CDB32;
u32 num_blocks = io_info->numBlocks;
u8 opcode = 0, flagvals = 0, groupnum = 0, control = 0;
/* Check if T10 PI (DIF) is enabled for this LD */
ld = MR_TargetIdToLdGet(io_info->ldTgtId, local_map_ptr);
raid = MR_LdRaidGet(ld, local_map_ptr);
if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) {
memset(cdb, 0, sizeof(io_request->CDB.CDB32));
cdb[0] = MEGASAS_SCSI_VARIABLE_LENGTH_CMD;
cdb[7] = MEGASAS_SCSI_ADDL_CDB_LEN;
if (scp->sc_data_direction == DMA_FROM_DEVICE)
cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_READ32;
else
cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_WRITE32;
cdb[10] = MEGASAS_RD_WR_PROTECT_CHECK_ALL;
/* LBA */
cdb[12] = (u8)((start_blk >> 56) & 0xff);
cdb[13] = (u8)((start_blk >> 48) & 0xff);
cdb[14] = (u8)((start_blk >> 40) & 0xff);
cdb[15] = (u8)((start_blk >> 32) & 0xff);
cdb[16] = (u8)((start_blk >> 24) & 0xff);
cdb[17] = (u8)((start_blk >> 16) & 0xff);
cdb[18] = (u8)((start_blk >> 8) & 0xff);
cdb[19] = (u8)(start_blk & 0xff);
/* Logical block reference tag */
io_request->CDB.EEDP32.PrimaryReferenceTag =
cpu_to_be32(ref_tag);
io_request->CDB.EEDP32.PrimaryApplicationTagMask = cpu_to_be16(0xffff);
io_request->IoFlags = cpu_to_le16(32); /* Specify 32-byte cdb */
/* Transfer length */
cdb[28] = (u8)((num_blocks >> 24) & 0xff);
cdb[29] = (u8)((num_blocks >> 16) & 0xff);
cdb[30] = (u8)((num_blocks >> 8) & 0xff);
cdb[31] = (u8)(num_blocks & 0xff);
/* set SCSI IO EEDPFlags */
if (scp->sc_data_direction == DMA_FROM_DEVICE) {
io_request->EEDPFlags = cpu_to_le16(
MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP |
MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG |
MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE |
MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD);
} else {
io_request->EEDPFlags = cpu_to_le16(
MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_INSERT_OP);
}
io_request->Control |= cpu_to_le32((0x4 << 26));
io_request->EEDPBlockSize = cpu_to_le32(scp->device->sector_size);
} else {
/* Some drives don't support 16/12 byte CDB's, convert to 10 */
if (((cdb_len == 12) || (cdb_len == 16)) &&
(start_blk <= 0xffffffff)) {
if (cdb_len == 16) {
opcode = cdb[0] == READ_16 ? READ_10 : WRITE_10;
flagvals = cdb[1];
groupnum = cdb[14];
control = cdb[15];
} else {
opcode = cdb[0] == READ_12 ? READ_10 : WRITE_10;
flagvals = cdb[1];
groupnum = cdb[10];
control = cdb[11];
}
memset(cdb, 0, sizeof(io_request->CDB.CDB32));
cdb[0] = opcode;
cdb[1] = flagvals;
cdb[6] = groupnum;
cdb[9] = control;
/* Transfer length */
cdb[8] = (u8)(num_blocks & 0xff);
cdb[7] = (u8)((num_blocks >> 8) & 0xff);
io_request->IoFlags = cpu_to_le16(10); /* Specify 10-byte cdb */
cdb_len = 10;
} else if ((cdb_len < 16) && (start_blk > 0xffffffff)) {
/* Convert to 16 byte CDB for large LBA's */
switch (cdb_len) {
case 6:
opcode = cdb[0] == READ_6 ? READ_16 : WRITE_16;
control = cdb[5];
break;
case 10:
opcode =
cdb[0] == READ_10 ? READ_16 : WRITE_16;
flagvals = cdb[1];
groupnum = cdb[6];
control = cdb[9];
break;
case 12:
opcode =
cdb[0] == READ_12 ? READ_16 : WRITE_16;
flagvals = cdb[1];
groupnum = cdb[10];
control = cdb[11];
break;
}
memset(cdb, 0, sizeof(io_request->CDB.CDB32));
cdb[0] = opcode;
cdb[1] = flagvals;
cdb[14] = groupnum;
cdb[15] = control;
/* Transfer length */
cdb[13] = (u8)(num_blocks & 0xff);
cdb[12] = (u8)((num_blocks >> 8) & 0xff);
cdb[11] = (u8)((num_blocks >> 16) & 0xff);
cdb[10] = (u8)((num_blocks >> 24) & 0xff);
io_request->IoFlags = cpu_to_le16(16); /* Specify 16-byte cdb */
cdb_len = 16;
}
/* Normal case, just load LBA here */
switch (cdb_len) {
case 6:
{
u8 val = cdb[1] & 0xE0;
cdb[3] = (u8)(start_blk & 0xff);
cdb[2] = (u8)((start_blk >> 8) & 0xff);
cdb[1] = val | ((u8)(start_blk >> 16) & 0x1f);
break;
}
case 10:
cdb[5] = (u8)(start_blk & 0xff);
cdb[4] = (u8)((start_blk >> 8) & 0xff);
cdb[3] = (u8)((start_blk >> 16) & 0xff);
cdb[2] = (u8)((start_blk >> 24) & 0xff);
break;
case 12:
cdb[5] = (u8)(start_blk & 0xff);
cdb[4] = (u8)((start_blk >> 8) & 0xff);
cdb[3] = (u8)((start_blk >> 16) & 0xff);
cdb[2] = (u8)((start_blk >> 24) & 0xff);
break;
case 16:
cdb[9] = (u8)(start_blk & 0xff);
cdb[8] = (u8)((start_blk >> 8) & 0xff);
cdb[7] = (u8)((start_blk >> 16) & 0xff);
cdb[6] = (u8)((start_blk >> 24) & 0xff);
cdb[5] = (u8)((start_blk >> 32) & 0xff);
cdb[4] = (u8)((start_blk >> 40) & 0xff);
cdb[3] = (u8)((start_blk >> 48) & 0xff);
cdb[2] = (u8)((start_blk >> 56) & 0xff);
break;
}
}
}
/**
* 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 */
static void 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 = &current_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 = &current_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 availible 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 = &current_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
*
*/
static void
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;
else if (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;
else if (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;
else if (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;
else if (raid->cpuAffinity.ldWrite.cpu1)
cpu_sel = MR_RAID_CTX_CPUSEL_1;
if (is_stream_detected(rctx_g35) &&
((raid->level == 5) || (raid->level == 6)) &&
(raid->writeMode == MR_RL_WRITE_THROUGH_MODE) &&
(cpu_sel == MR_RAID_CTX_CPUSEL_FCFS))
cpu_sel = MR_RAID_CTX_CPUSEL_0;
}
}
rctx_g35->routing_flags |=
(cpu_sel << MR_RAID_CTX_ROUTINGFLAGS_CPUSEL_SHIFT);
/* Always give priority to MR_RAID_FLAGS_IO_SUB_TYPE_LDIO_BW_LIMIT
* vs MR_RAID_FLAGS_IO_SUB_TYPE_CACHE_BYPASS.
* IO Subtype is not bitmap.
*/
if ((fusion->pcie_bw_limitation) && (raid->level == 1) && (!is_read) &&
(scsi_buff_len > MR_LARGE_IO_MIN_SIZE)) {
praid_context->raid_context_g35.raid_flags =
(MR_RAID_FLAGS_IO_SUB_TYPE_LDIO_BW_LIMIT
<< MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT);
}
}
/**
* megasas_build_ldio_fusion - Prepares IOs to devices
* @instance: Adapter soft state
* @scp: SCSI command
* @cmd: Command to be prepared
*
* Prepares the io_request and chain elements (sg_frame) for IO
* The IO can be for PD (Fast Path) or LD
*/
static void
megasas_build_ldio_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scp,
struct megasas_cmd_fusion *cmd)
{
bool fp_possible;
u16 ld;
u32 start_lba_lo, start_lba_hi, device_id, datalength = 0;
u32 scsi_buff_len;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
struct IO_REQUEST_INFO io_info;
struct fusion_context *fusion;
struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
u8 *raidLUN;
unsigned long spinlock_flags;
struct MR_LD_RAID *raid = NULL;
struct MR_PRIV_DEVICE *mrdev_priv;
struct RAID_CONTEXT *rctx;
struct RAID_CONTEXT_G35 *rctx_g35;
device_id = MEGASAS_DEV_INDEX(scp);
fusion = instance->ctrl_context;
io_request = cmd->io_request;
rctx = &io_request->RaidContext.raid_context;
rctx_g35 = &io_request->RaidContext.raid_context_g35;
rctx->virtual_disk_tgt_id = cpu_to_le16(device_id);
rctx->status = 0;
rctx->ex_status = 0;
start_lba_lo = 0;
start_lba_hi = 0;
fp_possible = false;
/*
* 6-byte READ(0x08) or WRITE(0x0A) cdb
*/
if (scp->cmd_len == 6) {
datalength = (u32) scp->cmnd[4];
start_lba_lo = ((u32) scp->cmnd[1] << 16) |
((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3];
start_lba_lo &= 0x1FFFFF;
}
/*
* 10-byte READ(0x28) or WRITE(0x2A) cdb
*/
else if (scp->cmd_len == 10) {
datalength = (u32) scp->cmnd[8] |
((u32) scp->cmnd[7] << 8);
start_lba_lo = ((u32) scp->cmnd[2] << 24) |
((u32) scp->cmnd[3] << 16) |
((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
}
/*
* 12-byte READ(0xA8) or WRITE(0xAA) cdb
*/
else if (scp->cmd_len == 12) {
datalength = ((u32) scp->cmnd[6] << 24) |
((u32) scp->cmnd[7] << 16) |
((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];
start_lba_lo = ((u32) scp->cmnd[2] << 24) |
((u32) scp->cmnd[3] << 16) |
((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
}
/*
* 16-byte READ(0x88) or WRITE(0x8A) cdb
*/
else if (scp->cmd_len == 16) {
datalength = ((u32) scp->cmnd[10] << 24) |
((u32) scp->cmnd[11] << 16) |
((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13];
start_lba_lo = ((u32) scp->cmnd[6] << 24) |
((u32) scp->cmnd[7] << 16) |
((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9];
start_lba_hi = ((u32) scp->cmnd[2] << 24) |
((u32) scp->cmnd[3] << 16) |
((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5];
}
memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO));
io_info.ldStartBlock = ((u64)start_lba_hi << 32) | start_lba_lo;
io_info.numBlocks = datalength;
io_info.ldTgtId = device_id;
io_info.r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
scsi_buff_len = scsi_bufflen(scp);
io_request->DataLength = cpu_to_le32(scsi_buff_len);
io_info.data_arms = 1;
if (scp->sc_data_direction == DMA_FROM_DEVICE)
io_info.isRead = 1;
local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
if (ld < instance->fw_supported_vd_count)
raid = MR_LdRaidGet(ld, local_map_ptr);
if (!raid || (!fusion->fast_path_io)) {
rctx->reg_lock_flags = 0;
fp_possible = false;
} else {
if (MR_BuildRaidContext(instance, &io_info, rctx,
local_map_ptr, &raidLUN))
fp_possible = (io_info.fpOkForIo > 0) ? true : false;
}
megasas_get_msix_index(instance, scp, cmd, io_info.data_arms);
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 (atomic_inc_return(&instance->fw_outstanding) >
(instance->host->can_queue)) {
fp_possible = false;
atomic_dec(&instance->fw_outstanding);
} else if (fusion->pcie_bw_limitation &&
((scsi_buff_len > MR_LARGE_IO_MIN_SIZE) ||
(atomic_dec_if_positive(&mrdev_priv->r1_ldio_hint) > 0))) {
fp_possible = false;
atomic_dec(&instance->fw_outstanding);
if (scsi_buff_len > MR_LARGE_IO_MIN_SIZE)
atomic_set(&mrdev_priv->r1_ldio_hint,
instance->r1_ldio_hint_default);
}
}
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);
}
if (fp_possible) {
megasas_set_pd_lba(io_request, scp->cmd_len, &io_info, scp,
local_map_ptr, start_lba_lo);
io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_FP_IO
<< MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
if (instance->adapter_type == INVADER_SERIES) {
rctx->type = MPI2_TYPE_CUDA;
rctx->nseg = 0x1;
io_request->IoFlags |= cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
rctx->reg_lock_flags |=
(MR_RL_FLAGS_GRANT_DESTINATION_CUDA |
MR_RL_FLAGS_SEQ_NUM_ENABLE);
} else if (instance->adapter_type >= VENTURA_SERIES) {
rctx_g35->nseg_type |= (1 << RAID_CONTEXT_NSEG_SHIFT);
rctx_g35->nseg_type |= (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT);
rctx_g35->routing_flags |= (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT);
io_request->IoFlags |=
cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
}
if (fusion->load_balance_info &&
(fusion->load_balance_info[device_id].loadBalanceFlag) &&
(io_info.isRead)) {
io_info.devHandle =
get_updated_dev_handle(instance,
&fusion->load_balance_info[device_id],
&io_info, local_map_ptr);
scp->SCp.Status |= MEGASAS_LOAD_BALANCE_FLAG;
cmd->pd_r1_lb = io_info.pd_after_lb;
if (instance->adapter_type >= VENTURA_SERIES)
rctx_g35->span_arm = io_info.span_arm;
else
rctx->span_arm = io_info.span_arm;
} else
scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG;
if (instance->adapter_type >= VENTURA_SERIES)
cmd->r1_alt_dev_handle = io_info.r1_alt_dev_handle;
else
cmd->r1_alt_dev_handle = MR_DEVHANDLE_INVALID;
if ((raidLUN[0] == 1) &&
(local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].validHandles > 1)) {
instance->dev_handle = !(instance->dev_handle);
io_info.devHandle =
local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].devHandle[instance->dev_handle];
}
cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle;
io_request->DevHandle = io_info.devHandle;
cmd->pd_interface = io_info.pd_interface;
/* populate the LUN field */
memcpy(io_request->LUN, raidLUN, 8);
} else {
rctx->timeout_value =
cpu_to_le16(local_map_ptr->raidMap.fpPdIoTimeoutSec);
cmd->request_desc->SCSIIO.RequestFlags =
(MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO
<< MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
if (instance->adapter_type == INVADER_SERIES) {
if (io_info.do_fp_rlbypass ||
(rctx->reg_lock_flags == REGION_TYPE_UNUSED))
cmd->request_desc->SCSIIO.RequestFlags =
(MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
rctx->type = MPI2_TYPE_CUDA;
rctx->reg_lock_flags |=
(MR_RL_FLAGS_GRANT_DESTINATION_CPU0 |
MR_RL_FLAGS_SEQ_NUM_ENABLE);
rctx->nseg = 0x1;
} else if (instance->adapter_type >= VENTURA_SERIES) {
rctx_g35->routing_flags |= (1 << MR_RAID_CTX_ROUTINGFLAGS_SQN_SHIFT);
rctx_g35->nseg_type |= (1 << RAID_CONTEXT_NSEG_SHIFT);
rctx_g35->nseg_type |= (MPI2_TYPE_CUDA << RAID_CONTEXT_TYPE_SHIFT);
}
io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
io_request->DevHandle = cpu_to_le16(device_id);
} /* Not FP */
}
/**
* megasas_build_ld_nonrw_fusion - prepares non rw ios for virtual disk
* @instance: Adapter soft state
* @scmd: SCSI command
* @cmd: Command to be prepared
*
* Prepares the io_request frame for non-rw io cmds for vd.
*/
static void megasas_build_ld_nonrw_fusion(struct megasas_instance *instance,
struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd)
{
u32 device_id;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
u16 ld;
struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
struct fusion_context *fusion = instance->ctrl_context;
u8 span, physArm;
__le16 devHandle;
u32 arRef, pd;
struct MR_LD_RAID *raid;
struct RAID_CONTEXT *pRAID_Context;
u8 fp_possible = 1;
io_request = cmd->io_request;
device_id = MEGASAS_DEV_INDEX(scmd);
local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
/* get RAID_Context pointer */
pRAID_Context = &io_request->RaidContext.raid_context;
/* Check with FW team */
pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id);
pRAID_Context->reg_lock_row_lba = 0;
pRAID_Context->reg_lock_length = 0;
if (fusion->fast_path_io && (
device_id < instance->fw_supported_vd_count)) {
ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
if (ld >= instance->fw_supported_vd_count - 1)
fp_possible = 0;
else {
raid = MR_LdRaidGet(ld, local_map_ptr);
if (!(raid->capability.fpNonRWCapable))
fp_possible = 0;
}
} else
fp_possible = 0;
if (!fp_possible) {
io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
io_request->DevHandle = cpu_to_le16(device_id);
io_request->LUN[1] = scmd->device->lun;
pRAID_Context->timeout_value =
cpu_to_le16(scsi_cmd_to_rq(scmd)->timeout / HZ);