staticunsignedint throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH;
module_param(throttlequeuedepth, int, 0444);
MODULE_PARM_DESC(throttlequeuedepth, "Adapter queue depth when throttled due to I/O timeout. Default: 16");
unsignedint resetwaittime = MEGASAS_RESET_WAIT_TIME;
module_param(resetwaittime, int, 0444);
MODULE_PARM_DESC(resetwaittime, "Wait time in (1-180s) after I/O timeout before resetting adapter. Default: 180s");
staticint perf_mode = -1;
module_param(perf_mode, int, 0444);
MODULE_PARM_DESC(perf_mode, "Performance mode (only for Aero adapters), options:\n\t\t" "0 - balanced: High iops and low latency queues are allocated &\n\t\t" "interrupt coalescing is enabled only on high iops queues\n\t\t" "1 - iops: High iops queues are not allocated &\n\t\t" "interrupt coalescing is enabled on all queues\n\t\t" "2 - latency: High iops queues are not allocated &\n\t\t" "interrupt coalescing is disabled on all queues\n\t\t" "default mode is 'balanced'"
);
staticint event_log_level = MFI_EVT_CLASS_CRITICAL;
module_param(event_log_level, int, 0644);
MODULE_PARM_DESC(event_log_level, "Asynchronous event logging level- range is: -2(CLASS_DEBUG) to 4(CLASS_DEAD), Default: 2(CLASS_CRITICAL)");
staticunsignedint enable_sdev_max_qd;
module_param(enable_sdev_max_qd, int, 0444);
MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0");
staticint poll_queues;
module_param(poll_queues, int, 0444);
MODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t" "This parameter is effective only if host_tagset_enable=1 &\n\t\t" "It is not applicable for MFI_SERIES. &\n\t\t" "Driver will work in latency mode. &\n\t\t" "High iops queues are not allocated &\n\t\t"
);
switch (class) { case MFI_EVT_CLASS_DEBUG: return"debug"; case MFI_EVT_CLASS_PROGRESS: return"progress"; case MFI_EVT_CLASS_INFO: return"info"; case MFI_EVT_CLASS_WARNING: return"WARN"; case MFI_EVT_CLASS_CRITICAL: return"CRIT"; case MFI_EVT_CLASS_FATAL: return"FATAL"; case MFI_EVT_CLASS_DEAD: return"DEAD"; default:
snprintf(buffer, sizeof(buffer), "%d", class); return buffer;
}
}
if ((event_log_level < MFI_EVT_CLASS_DEBUG) ||
(event_log_level > MFI_EVT_CLASS_DEAD)) {
printk(KERN_WARNING "megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n");
event_log_level = MFI_EVT_CLASS_CRITICAL;
}
switch (cmd->cmnd[0]) { case READ_10: case WRITE_10: case READ_12: case WRITE_12: case READ_6: case WRITE_6: case READ_16: case WRITE_16:
ret = (MEGASAS_IS_LOGICAL(cmd->device)) ?
READ_WRITE_LDIO : READ_WRITE_SYSPDIO; break; default:
ret = (MEGASAS_IS_LOGICAL(cmd->device)) ?
NON_READ_WRITE_LDIO : NON_READ_WRITE_SYSPDIO;
} return ret;
}
dev_err(&instance->pdev->dev, "[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no);
dev_err(&instance->pdev->dev, "[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding)); if (IS_DMA64)
dev_err(&instance->pdev->dev, "[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no); else
dev_err(&instance->pdev->dev, "[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no);
dev_err(&instance->pdev->dev, "[%d]: Pending OS cmds in FW : \n",instance->host->host_no); for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i]; if (!cmd->scmd) continue;
dev_err(&instance->pdev->dev, "[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsignedlong)cmd->frame_phys_addr); if (megasas_cmd_type(cmd->scmd) == READ_WRITE_LDIO) {
ldio = (struct megasas_io_frame *)cmd->frame;
mfi_sgl = &ldio->sgl;
sgcount = ldio->sge_count;
dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x," " lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",
instance->host->host_no, cmd->frame_count, ldio->cmd, ldio->target_id,
le32_to_cpu(ldio->start_lba_lo), le32_to_cpu(ldio->start_lba_hi),
le32_to_cpu(ldio->sense_buf_phys_addr_lo), sgcount);
} else {
pthru = (struct megasas_pthru_frame *) cmd->frame;
mfi_sgl = &pthru->sgl;
sgcount = pthru->sge_count;
dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, " "lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",
instance->host->host_no, cmd->frame_count, pthru->cmd, pthru->target_id,
pthru->lun, pthru->cdb_len, le32_to_cpu(pthru->data_xfer_len),
le32_to_cpu(pthru->sense_buf_phys_addr_lo), sgcount);
} if (megasas_dbg_lvl & MEGASAS_DBG_LVL) { for (n = 0; n < sgcount; n++) { if (IS_DMA64)
dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%llx\n",
le32_to_cpu(mfi_sgl->sge64[n].length),
le64_to_cpu(mfi_sgl->sge64[n].phys_addr)); else
dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%x\n",
le32_to_cpu(mfi_sgl->sge32[n].length),
le32_to_cpu(mfi_sgl->sge32[n].phys_addr));
}
}
} /*for max_cmd*/
dev_err(&instance->pdev->dev, "[%d]: Pending Internal cmds in FW : \n",instance->host->host_no); for (i = 0; i < max_cmd; i++) {
/* max_io_size_kb will be set to non zero for *nvmebasedvdandsyspd.
*/ if (is_target_prop)
max_io_size_kb = le32_to_cpu(instance->tgt_prop->max_io_size_kb);
if (instance->nvme_page_size && max_io_size_kb)
megasas_set_nvme_device_properties(sdev, lim,
max_io_size_kb << 10);
instance = megasas_lookup_instance(sdev->host->host_no); if (instance->pd_list_not_supported) { if (!MEGASAS_IS_LOGICAL(sdev) && sdev->type == TYPE_DISK) {
pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
sdev->id; if (instance->pd_list[pd_index].driveState !=
MR_PD_STATE_SYSTEM) return -ENXIO;
}
}
mutex_lock(&instance->reset_mutex); /* Send DCMD to Firmware and cache the information */ if ((instance->pd_info) && !MEGASAS_IS_LOGICAL(sdev))
megasas_get_pd_info(instance, sdev);
/* Some ventura firmware may not have instance->nvme_page_size set. *DonotsendMR_DCMD_DRV_GET_TARGET_PROP
*/ if ((instance->tgt_prop) && (instance->nvme_page_size))
ret_target_prop = megasas_get_target_prop(instance, sdev);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation_111:" "Failed to get cmd for scsi%d\n",
instance->host->host_no); return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
if (!instance->vf_affiliation_111) {
dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " "affiliation for scsi%d\n", instance->host->host_no);
megasas_return_cmd(instance, cmd); return -ENOMEM;
}
if (initial)
memset(instance->vf_affiliation_111, 0, sizeof(struct MR_LD_VF_AFFILIATION_111)); else {
new_affiliation_111 =
dma_alloc_coherent(&instance->pdev->dev, sizeof(struct MR_LD_VF_AFFILIATION_111),
&new_affiliation_111_h, GFP_KERNEL); if (!new_affiliation_111) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n",
instance->host->host_no);
megasas_return_cmd(instance, cmd); return -ENOMEM;
}
}
dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " "scsi%d\n", instance->host->host_no);
if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) {
dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" " failed with status 0x%x for scsi%d\n",
dcmd->cmd_status, instance->host->host_no);
retval = 1; /* Do a scan if we couldn't get affiliation */ goto out;
}
if (!initial) {
thisVf = new_affiliation_111->thisVf; for (ld = 0 ; ld < new_affiliation_111->vdCount; ld++) if (instance->vf_affiliation_111->map[ld].policy[thisVf] !=
new_affiliation_111->map[ld].policy[thisVf]) {
dev_warn(&instance->pdev->dev, "SR-IOV: " "Got new LD/VF affiliation for scsi%d\n",
instance->host->host_no);
memcpy(instance->vf_affiliation_111,
new_affiliation_111, sizeof(struct MR_LD_VF_AFFILIATION_111));
retval = 1; goto out;
}
}
out: if (new_affiliation_111) {
dma_free_coherent(&instance->pdev->dev, sizeof(struct MR_LD_VF_AFFILIATION_111),
new_affiliation_111,
new_affiliation_111_h);
}
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation12: " "Failed to get cmd for scsi%d\n",
instance->host->host_no); return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
if (!instance->vf_affiliation) {
dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " "affiliation for scsi%d\n", instance->host->host_no);
megasas_return_cmd(instance, cmd); return -ENOMEM;
}
if (initial)
memset(instance->vf_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION)); else {
new_affiliation =
dma_alloc_coherent(&instance->pdev->dev,
(MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION),
&new_affiliation_h, GFP_KERNEL); if (!new_affiliation) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n",
instance->host->host_no);
megasas_return_cmd(instance, cmd); return -ENOMEM;
}
}
dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " "scsi%d\n", instance->host->host_no);
if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) {
dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" " failed with status 0x%x for scsi%d\n",
dcmd->cmd_status, instance->host->host_no);
retval = 1; /* Do a scan if we couldn't get affiliation */ goto out;
}
if (!initial) { if (!new_affiliation->ldCount) {
dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF " "affiliation for passive path for scsi%d\n",
instance->host->host_no);
retval = 1; goto out;
}
newmap = new_affiliation->map;
savedmap = instance->vf_affiliation->map;
thisVf = new_affiliation->thisVf; for (i = 0 ; i < new_affiliation->ldCount; i++) {
found = 0; for (j = 0; j < instance->vf_affiliation->ldCount;
j++) { if (newmap->ref.targetId ==
savedmap->ref.targetId) {
found = 1; if (newmap->policy[thisVf] !=
savedmap->policy[thisVf]) {
doscan = 1; goto out;
}
}
savedmap = (struct MR_LD_VF_MAP *)
((unsignedchar *)savedmap +
savedmap->size);
} if (!found && newmap->policy[thisVf] !=
MR_LD_ACCESS_HIDDEN) {
doscan = 1; goto out;
}
newmap = (struct MR_LD_VF_MAP *)
((unsignedchar *)newmap + newmap->size);
}
/* This function will get the current SR-IOV LD/VF affiliation */ staticint megasas_get_ld_vf_affiliation(struct megasas_instance *instance, int initial)
{ int retval;
/* This function will tell FW to start the SR-IOV heartbeat */ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, int initial)
{ struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; int retval = 0;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_sriov_start_heartbeat: " "Failed to get cmd for scsi%d\n",
instance->host->host_no); return -ENOMEM;
}
dcmd = &cmd->frame->dcmd;
if (initial) {
instance->hb_host_mem =
dma_alloc_coherent(&instance->pdev->dev, sizeof(struct MR_CTRL_HB_HOST_MEM),
&instance->hb_host_mem_h,
GFP_KERNEL); if (!instance->hb_host_mem) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate" " memory for heartbeat host memory for scsi%d\n",
instance->host->host_no);
retval = -ENOMEM; goto out;
}
}
if (instance->adapter_type != MFI_SERIES)
ret = megasas_task_abort_fusion(scmd); else {
sdev_printk(KERN_NOTICE, scmd->device, "TASK ABORT not supported\n");
ret = FAILED;
}
if (instance->adapter_type != MFI_SERIES)
ret = megasas_reset_target_fusion(scmd); else {
sdev_printk(KERN_NOTICE, scmd->device, "TARGET RESET not supported\n");
ret = FAILED;
}
return ret;
}
/** *megasas_bios_param-Returnsdiskgeometryforadisk *@sdev:devicehandle *@bdev:blockdevice *@capacity:drivecapacity *@geom:geometryparameters
*/ staticint
megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
sector_t capacity, int geom[])
{ int heads; int sectors;
sector_t cylinders; unsignedlong tmp;
mutex_lock(&instance->crashdump_lock);
buff_offset = instance->fw_crash_buffer_offset;
if (!instance->crash_dump_buf ||
!((instance->fw_crash_state == AVAILABLE) ||
(instance->fw_crash_state == COPYING))) {
dev_err(&instance->pdev->dev,
"Firmware crash dump is not available\n");
mutex_unlock(&instance->crashdump_lock);
return -EINVAL;
}
if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) {
dev_err(&instance->pdev->dev,
"Firmware crash dump offset is out of range\n");
mutex_unlock(&instance->crashdump_lock);
return 0;
}
/**
* megasas_complete_int_cmd - Completes an internal command
* @instance: Adapter soft state
* @cmd: Command to be completed
*
* The megasas_issue_blocked_cmd() function waits for a command to complete
* after it issues a command. This function wakes up that waiting routine by
* calling wake_up() on the wait queue.
*/
static void
megasas_complete_int_cmd(struct megasas_instance *instance,
struct megasas_cmd *cmd)
{
if (cmd->cmd_status_drv == DCMD_INIT)
cmd->cmd_status_drv =
(cmd->frame->io.cmd_status == MFI_STAT_OK) ?
DCMD_SUCCESS : DCMD_FAILED;
wake_up(&instance->int_cmd_wait_q);
}
/**
* megasas_complete_abort - Completes aborting a command
* @instance: Adapter soft state
* @cmd: Cmd that was issued to abort another cmd
*
* The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q
* after it issues an abort on a previously issued command. This function
* wakes up all functions waiting on the same wait queue.
*/
static void
megasas_complete_abort(struct megasas_instance *instance,
struct megasas_cmd *cmd)
{
if (cmd->sync_cmd) {
cmd->sync_cmd = 0;
cmd->cmd_status_drv = DCMD_SUCCESS;
wake_up(&instance->abort_cmd_wait_q);
}
}
for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) {
if (instance->ld_ids_prev[i] != 0xff &&
instance->ld_ids_from_raidmap[i] == 0xff) {
if (megasas_dbg_lvl & LD_PD_DEBUG)
dev_info(&instance->pdev->dev,
"LD target ID %d removed from RAID map\n", i);
instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED;
}
}
}
/**
* megasas_complete_cmd - Completes a command
* @instance: Adapter soft state
* @cmd: Command to be completed
* @alt_status: If non-zero, use this value as status to
* SCSI mid-layer instead of the value returned
* by the FW. This should be used if caller wants
* an alternate status (as in the case of aborted
* commands)
*/
void
megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
u8 alt_status)
{
int exception = 0;
struct megasas_header *hdr = &cmd->frame->hdr;
unsigned long flags;
struct fusion_context *fusion = instance->ctrl_context;
u32 opcode, status;
/* flag for the retry reset */
cmd->retry_for_fw_reset = 0;
if (cmd->scmd)
megasas_priv(cmd->scmd)->cmd_priv = NULL;
switch (hdr->cmd) {
case MFI_CMD_INVALID:
/* Some older 1068 controller FW may keep a pended
MR_DCMD_CTRL_EVENT_GET_INFO left over from the main kernel
when booting the kdump kernel. Ignore this command to
prevent a kernel panic on shutdown of the kdump kernel. */
dev_warn(&instance->pdev->dev, "MFI_CMD_INVALID command "
"completed\n");
dev_warn(&instance->pdev->dev, "If you have a controller "
"other than PERC5, please upgrade your firmware\n");
break;
case MFI_CMD_PD_SCSI_IO:
case MFI_CMD_LD_SCSI_IO:
/*
* MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been
* issued either through an IO path or an IOCTL path. If it
* was via IOCTL, we will send it to internal completion.
*/
if (cmd->sync_cmd) {
cmd->sync_cmd = 0;
megasas_complete_int_cmd(instance, cmd);
break;
}
fallthrough;
case MFI_CMD_SMP:
case MFI_CMD_STP:
case MFI_CMD_NVME:
case MFI_CMD_TOOLBOX:
megasas_complete_int_cmd(instance, cmd);
break;
case MFI_CMD_DCMD:
opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
/* Check for LD map update */
if ((opcode == MR_DCMD_LD_MAP_GET_INFO)
&& (cmd->frame->dcmd.mbox.b[1] == 1)) {
fusion->fast_path_io = 0;
spin_lock_irqsave(instance->host->host_lock, flags);
status = cmd->frame->hdr.cmd_status;
instance->map_update_cmd = NULL;
if (status != MFI_STAT_OK) {
if (status != MFI_STAT_NOT_FOUND)
dev_warn(&instance->pdev->dev, "map syncfailed, status = 0x%x\n",
cmd->frame->hdr.cmd_status);
else {
megasas_return_cmd(instance, cmd);
spin_unlock_irqrestore(
instance->host->host_lock,
flags);
break;
}
}
megasas_return_cmd(instance, cmd);
/*
* Set fast path IO to ZERO.
* Validate Map will set proper value.
* Meanwhile all IOs will go as LD IO.
*/
if (status == MFI_STAT_OK &&
(MR_ValidateMapInfo(instance, (instance->map_id + 1)))) {
instance->map_id++;
fusion->fast_path_io = 1;
} else {
fusion->fast_path_io = 0;
}
if (instance->adapter_type >= INVADER_SERIES)
megasas_set_ld_removed_by_fw(instance);
/*
* See if got an event notification
*/
if (opcode == MR_DCMD_CTRL_EVENT_WAIT)
megasas_service_aen(instance, cmd);
else
megasas_complete_int_cmd(instance, cmd);
break;
case MFI_CMD_ABORT:
/*
* Cmd issued to abort another cmd returned
*/
megasas_complete_abort(instance, cmd);
break;
/**
* megasas_issue_pending_cmds_again - issue all pending cmds
* in FW again because of the fw reset
* @instance: Adapter soft state
*/
static inline void
megasas_issue_pending_cmds_again(struct megasas_instance *instance)
{
struct megasas_cmd *cmd;
struct list_head clist_local;
union megasas_evt_class_locale class_locale;
unsigned long flags;
u32 seq_num;
while (!list_empty(&clist_local)) {
cmd = list_entry((&clist_local)->next,
struct megasas_cmd, list);
list_del_init(&cmd->list);
if (cmd->sync_cmd || cmd->scmd) {
dev_notice(&instance->pdev->dev, "command %p, %p:%d"
"detected to be pending while HBA reset\n",
cmd, cmd->scmd, cmd->sync_cmd);
cmd->retry_for_fw_reset++;
if (cmd->retry_for_fw_reset == 3) {
dev_notice(&instance->pdev->dev, "cmd %p, %p:%d"
"was tried multiple times during reset."
"Shutting down the HBA\n",
cmd, cmd->scmd, cmd->sync_cmd);
instance->instancet->disable_intr(instance);
atomic_set(&instance->fw_reset_no_pci_access, 1);
megaraid_sas_kill_hba(instance);
return;
}
}
if (cmd->sync_cmd == 1) {
if (cmd->scmd) {
dev_notice(&instance->pdev->dev, "unexpected"
"cmd attached to internal command!\n");
}
dev_notice(&instance->pdev->dev, "%p synchronous cmd"
"on the internal reset queue,"
"issue it again.\n", cmd);
cmd->cmd_status_drv = DCMD_INIT;
instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr, 0, instance->reg_set);
} else if (cmd->scmd) {
dev_notice(&instance->pdev->dev, "%p scsi cmd [%02x]"
"detected on the internal queue, issue again.\n",
cmd, cmd->scmd->cmnd[0]);
atomic_inc(&instance->fw_outstanding);
instance->instancet->fire_cmd(instance,
cmd->frame_phys_addr,
cmd->frame_count-1, instance->reg_set);
} else {
dev_notice(&instance->pdev->dev, "%p unexpected cmd on the"
"internal reset defer list while re-issue!!\n",
cmd);
}
}
if (instance->aen_cmd) {
dev_notice(&instance->pdev->dev, "aen_cmd in def process\n");
megasas_return_cmd(instance, instance->aen_cmd);
/*
* Move the internal reset pending commands to a deferred queue.
*
* We move the commands pending at internal reset time to a
* pending queue. This queue would be flushed after successful
* completion of the internal reset sequence. if the internal reset
* did not complete in time, the kernel reset handler would flush
* these commands.
*/
static void
megasas_internal_reset_defer_cmds(struct megasas_instance *instance)
{
struct megasas_cmd *cmd;
int i;
u16 max_cmd = instance->max_fw_cmds;
u32 defer_index;
unsigned long flags;
defer_index = 0;
spin_lock_irqsave(&instance->mfi_pool_lock, flags);
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
if (cmd->sync_cmd == 1 || cmd->scmd) {
dev_notice(&instance->pdev->dev, "moving cmd[%d]:%p:%d:%p"
"on the defer queue as internal\n",
defer_index, cmd, cmd->sync_cmd, cmd->scmd);
if (!list_empty(&cmd->list)) {
dev_notice(&instance->pdev->dev, "ERROR while"
" moving this cmd:%p, %d %p, it was"
"discovered on some list?\n",
cmd, cmd->sync_cmd, cmd->scmd);
if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) {
dev_notice(&instance->pdev->dev, "error, recovery st %x\n",
atomic_read(&instance->adprecovery));
return ;
}
if (atomic_read(&instance->adprecovery) == MEGASAS_ADPRESET_SM_INFAULT) {
dev_notice(&instance->pdev->dev, "FW detected to be in fault"
"state, restarting it...\n");
/**
* megasas_deplete_reply_queue - Processes all completed commands
* @instance: Adapter soft state
* @alt_status: Alternate status to be returned to
* SCSI mid-layer instead of the status
* returned by the FW
* Note: this must be called with hba lock held
*/
static int
megasas_deplete_reply_queue(struct megasas_instance *instance,
u8 alt_status)
{
u32 mfiStatus;
u32 fw_state;
if (instance->instancet->check_reset(instance, instance->reg_set) == 1)
return IRQ_HANDLED;
mfiStatus = instance->instancet->clear_intr(instance);
if (mfiStatus == 0) {
/* Hardware may not set outbound_intr_status in MSI-X mode */
if (!instance->msix_vectors)
return IRQ_NONE;
}
/**
* megasas_transition_to_ready - Move the FW to READY state
* @instance: Adapter soft state
* @ocr: Adapter reset state
*
* During the initialization, FW passes can potentially be in any one of
* several possible states. If the FW in operational, waiting-for-handshake
* states, driver must take steps to bring it to ready state. Otherwise, it
* has to wait for the ready state.
*/
int
megasas_transition_to_ready(struct megasas_instance *instance, int ocr)
{
int i;
u8 max_wait;
u32 fw_state;
u32 abs_state, curr_abs_state;
/*
* The cur_state should not last for more than max_wait secs
*/
for (i = 0; i < max_wait * 50; i++) {
curr_abs_state = instance->instancet->
read_fw_status_reg(instance);
if (abs_state == curr_abs_state) {
msleep(20);
} else
break;
}
/*
* Return error if fw_state hasn't changed after max_wait
*/
if (curr_abs_state == abs_state) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW state [%d] hasn't changed "
"in %d secs\n", fw_state, max_wait);
dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n");
megasas_dump_reg_set(instance->reg_set);
return -ENODEV;
}
abs_state = curr_abs_state;
fw_state = curr_abs_state & MFI_STATE_MASK;
}
dev_info(&instance->pdev->dev, "FW now in Ready state\n");
return 0;
}
/**
* megasas_teardown_frame_pool - Destroy the cmd frame DMA pool
* @instance: Adapter soft state
*/
static void megasas_teardown_frame_pool(struct megasas_instance *instance)
{
int i;
u16 max_cmd = instance->max_mfi_cmds;
struct megasas_cmd *cmd;
if (!instance->frame_dma_pool)
return;
/*
* Return all frames to pool
*/
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
if (cmd->frame)
dma_pool_free(instance->frame_dma_pool, cmd->frame,
cmd->frame_phys_addr);
if (cmd->sense)
dma_pool_free(instance->sense_dma_pool, cmd->sense,
cmd->sense_phys_addr);
}
/*
* Now destroy the pool itself
*/
dma_pool_destroy(instance->frame_dma_pool);
dma_pool_destroy(instance->sense_dma_pool);
/**
* megasas_create_frame_pool - Creates DMA pool for cmd frames
* @instance: Adapter soft state
*
* Each command packet has an embedded DMA memory buffer that is used for
* filling MFI frame and the SG list that immediately follows the frame. This
* function creates those DMA memory buffers for each command packet by using
* PCI pool facility.
*/
static int megasas_create_frame_pool(struct megasas_instance *instance)
{
int i;
u16 max_cmd;
u32 frame_count;
struct megasas_cmd *cmd;
max_cmd = instance->max_mfi_cmds;
/*
* For MFI controllers.
* max_num_sge = 60
* max_sge_sz = 16 byte (sizeof megasas_sge_skinny)
* Total 960 byte (15 MFI frame of 64 byte)
*
* Fusion adapter require only 3 extra frame.
* max_num_sge = 16 (defined as MAX_IOCTL_SGE)
* max_sge_sz = 12 byte (sizeof megasas_sge64)
* Total 192 byte (3 MFI frame of 64 byte)
*/
frame_count = (instance->adapter_type == MFI_SERIES) ?
(15 + 1) : (3 + 1);
instance->mfi_frame_size = MEGAMFI_FRAME_SIZE * frame_count;
/*
* Use DMA pool facility provided by PCI layer
*/
instance->frame_dma_pool = dma_pool_create("megasas frame pool",
&instance->pdev->dev,
instance->mfi_frame_size, 256, 0);
if (!instance->frame_dma_pool) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup frame pool\n");
return -ENOMEM;
}
instance->sense_dma_pool = dma_pool_create("megasas sense pool",
&instance->pdev->dev, 128, 4, 0);
if (!instance->sense_dma_pool) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool\n");
/*
* Allocate and attach a frame to each of the commands in cmd_list.
* By making cmd->index as the context instead of the &cmd, we can
* always use 32bit context regardless of the architecture
*/
for (i = 0; i < max_cmd; i++) {
/**
* megasas_free_cmds - Free all the cmds in the free cmd pool
* @instance: Adapter soft state
*/
void megasas_free_cmds(struct megasas_instance *instance)
{
int i;
/* First free the MFI frame pool */
megasas_teardown_frame_pool(instance);
/* Free all the commands in the cmd_list */
for (i = 0; i < instance->max_mfi_cmds; i++)
/**
* megasas_alloc_cmds - Allocates the command packets
* @instance: Adapter soft state
*
* Each command that is issued to the FW, whether IO commands from the OS or
* internal commands like IOCTLs, are wrapped in local data structure called
* megasas_cmd. The frame embedded in this megasas_cmd is actually issued to
* the FW.
*
* Each frame has a 32-bit field called context (tag). This context is used
* to get back the megasas_cmd from the frame when a frame gets completed in
* the ISR. Typically the address of the megasas_cmd itself would be used as
* the context. But we wanted to keep the differences between 32 and 64 bit
* systems to the mininum. We always use 32 bit integers for the context. 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 given the context. The
* free commands themselves are maintained in a linked list called cmd_pool.
*/
int megasas_alloc_cmds(struct megasas_instance *instance)
{
int i;
int j;
u16 max_cmd;
struct megasas_cmd *cmd;
max_cmd = instance->max_mfi_cmds;
/*
* instance->cmd_list is an array of struct megasas_cmd pointers.
* Allocate the dynamic array first and then allocate individual
* commands.
*/
instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL);
if (!instance->cmd_list) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "out of memory\n");
return -ENOMEM;
}
for (i = 0; i < max_cmd; i++) {
instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd),
GFP_KERNEL);
if (!instance->cmd_list[i]) {
for (j = 0; j < i; j++)
kfree(instance->cmd_list[j]);
for (i = 0; i < max_cmd; i++) {
cmd = instance->cmd_list[i];
memset(cmd, 0, sizeof(struct megasas_cmd));
cmd->index = i;
cmd->scmd = NULL;
cmd->instance = instance;
list_add_tail(&cmd->list, &instance->cmd_pool);
}
/*
* Create a frame pool and assign one frame to each cmd
*/
if (megasas_create_frame_pool(instance)) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n");
megasas_free_cmds(instance);
return -ENOMEM;
}
return 0;
}
/*
* dcmd_timeout_ocr_possible - Check if OCR is possible based on Driver/FW state.
* @instance: Adapter soft state
*
* Return 0 for only Fusion adapter, if driver load/unload is not in progress
* or FW is not under OCR.
*/
inline int
dcmd_timeout_ocr_possible(struct megasas_instance *instance) {
if (instance->adapter_type == MFI_SERIES)
return KILL_ADAPTER;
else if (instance->unload ||
test_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE,
&instance->reset_flags))
return IGNORE_TIMEOUT;
else
return INITIATE_OCR;
}
switch (dcmd_timeout_ocr_possible(instance)) {
case INITIATE_OCR:
cmd->flags |= DRV_DCMD_SKIP_REFIRE;
mutex_unlock(&instance->reset_mutex);
megasas_reset_fusion(instance->host,
MFI_IO_TIMEOUT_OCR);
mutex_lock(&instance->reset_mutex);
break;
case KILL_ADAPTER:
megaraid_sas_kill_hba(instance);
break;
case IGNORE_TIMEOUT:
dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
__func__, __LINE__);
break;
}
break;
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
return;
}
/*
* megasas_get_pd_list_info - Returns FW's pd_list structure
* @instance: Adapter soft state
* @pd_list: pd_list structure
*
* 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.
*/
static int
megasas_get_pd_list(struct megasas_instance *instance)
{
int ret = 0, pd_index = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct MR_PD_LIST *ci;
struct MR_PD_ADDRESS *pd_addr;
if (instance->pd_list_not_supported) {
dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY "
"not supported by firmware\n");
return ret;
}
ci = instance->pd_list_buf;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "(get_pd_list): Failed to get cmd\n");
return -ENOMEM;
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
return ret;
}
/*
* megasas_get_ld_list_info - Returns FW's ld_list structure
* @instance: Adapter soft state
* @ld_list: ld_list structure
*
* 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.
*/
static int
megasas_get_ld_list(struct megasas_instance *instance)
{
int ret = 0, ld_index = 0, ids = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct MR_LD_LIST *ci;
dma_addr_t ci_h = 0;
u32 ld_count;
ci = instance->ld_list_buf;
ci_h = instance->ld_list_buf_h;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_list: Failed to get cmd\n");
return -ENOMEM;
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
return ret;
}
/**
* megasas_ld_list_query - Returns FW's ld_list structure
* @instance: Adapter soft state
* @query_type: ld_list structure type
*
* 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.
*/
static int
megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
{
int ret = 0, ld_index = 0, ids = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct MR_LD_TARGETID_LIST *ci;
dma_addr_t ci_h = 0;
u32 tgtid_count;
ci = instance->ld_targetid_list_buf;
ci_h = instance->ld_targetid_list_buf_h;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_warn(&instance->pdev->dev,
"megasas_ld_list_query: Failed to get cmd\n");
return -ENOMEM;
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
return ret;
}
/**
* megasas_host_device_list_query
* dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET
* dcmd.mbox - reserved
* dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure
* Desc: This DCMD will return the combined device list
* Status: MFI_STAT_OK - List returned successfully
* MFI_STAT_INVALID_CMD - Firmware support for the feature has been
* disabled
* @instance: Adapter soft state
* @is_probe: Driver probe check
* Return: 0 if DCMD succeeded
* non-zero if failed
*/
static int
megasas_host_device_list_query(struct megasas_instance *instance,
bool is_probe)
{
int ret, i, target_id;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct MR_HOST_DEVICE_LIST *ci;
u32 count;
dma_addr_t ci_h;
ci = instance->host_device_list_buf;
ci_h = instance->host_device_list_buf_h;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_warn(&instance->pdev->dev,
"%s: failed to get cmd\n",
__func__);
return -ENOMEM;
}
case DCMD_TIMEOUT:
switch (dcmd_timeout_ocr_possible(instance)) {
case INITIATE_OCR:
cmd->flags |= DRV_DCMD_SKIP_REFIRE;
mutex_unlock(&instance->reset_mutex);
megasas_reset_fusion(instance->host,
MFI_IO_TIMEOUT_OCR);
mutex_lock(&instance->reset_mutex);
break;
case KILL_ADAPTER:
megaraid_sas_kill_hba(instance);
break;
case IGNORE_TIMEOUT:
dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
__func__, __LINE__);
break;
}
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
}
/**
* megasas_get_ctrl_info - Returns FW's controller structure
* @instance: Adapter soft state
*
* Issues an internal command (DCMD) to get the FW's controller structure.
* This information is mainly used to find out the maximum IO transfer per
* command supported by the FW.
*/
int
megasas_get_ctrl_info(struct megasas_instance *instance)
{
int ret = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct megasas_ctrl_info *ci;
dma_addr_t ci_h = 0;
ci = instance->ctrl_info_buf;
ci_h = instance->ctrl_info_buf_h;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a free cmd\n");
return -ENOMEM;
}
if ((instance->adapter_type != MFI_SERIES) &&
!instance->mask_interrupts) {
ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS);
} else {
ret = megasas_issue_polled(instance, cmd);
cmd->flags |= DRV_DCMD_SKIP_REFIRE;
}
switch (ret) {
case DCMD_SUCCESS:
/* Save required controller information in
* CPU endianness format.
*/
le32_to_cpus((u32 *)&ci->properties.OnOffProperties);
le16_to_cpus((u16 *)&ci->properties.on_off_properties2);
le32_to_cpus((u32 *)&ci->adapterOperations2);
le32_to_cpus((u32 *)&ci->adapterOperations3);
le16_to_cpus((u16 *)&ci->adapter_operations4);
le32_to_cpus((u32 *)&ci->adapter_operations5);
/* Update the latest Ext VD info.
* From Init path, store current firmware details.
* From OCR path, detect any firmware properties changes.
* in case of Firmware upgrade without system reboot.
*/
megasas_update_ext_vd_details(instance);
instance->support_seqnum_jbod_fp =
ci->adapterOperations3.useSeqNumJbodFP;
instance->support_morethan256jbod =
ci->adapter_operations4.support_pd_map_target_id;
instance->support_nvme_passthru =
ci->adapter_operations4.support_nvme_passthru;
instance->support_pci_lane_margining =
ci->adapter_operations5.support_pci_lane_margining;
instance->task_abort_tmo = ci->TaskAbortTO;
instance->max_reset_tmo = ci->MaxResetTO;
/*Check whether controller is iMR or MR */
instance->is_imr = (ci->memory_size ? 0 : 1);
case DCMD_TIMEOUT:
switch (dcmd_timeout_ocr_possible(instance)) {
case INITIATE_OCR:
cmd->flags |= DRV_DCMD_SKIP_REFIRE;
mutex_unlock(&instance->reset_mutex);
megasas_reset_fusion(instance->host,
MFI_IO_TIMEOUT_OCR);
mutex_lock(&instance->reset_mutex);
break;
case KILL_ADAPTER:
megaraid_sas_kill_hba(instance);
break;
case IGNORE_TIMEOUT:
dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n",
__func__, __LINE__);
break;
}
break;
case DCMD_FAILED:
megaraid_sas_kill_hba(instance);
break;
}
if (ret != DCMD_TIMEOUT)
megasas_return_cmd(instance, cmd);
return ret;
}
/*
* megasas_set_crash_dump_params - Sends address of crash dump DMA buffer
* to firmware
*
* @instance: Adapter soft state
* @crash_buf_state - tell FW to turn ON/OFF crash dump feature
MR_CRASH_BUF_TURN_OFF = 0
MR_CRASH_BUF_TURN_ON = 1
* @return 0 on success non-zero on failure.
* Issues an internal command (DCMD) to set parameters for crash dump feature.
* Driver will send address of crash dump DMA buffer and set mbox to tell FW
* that driver supports crash dump feature. This DCMD will be sent only if
* crash dump feature is supported by the FW.
*
*/
int megasas_set_crash_dump_params(struct megasas_instance *instance,
u8 crash_buf_state)
{
int ret = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_err(&instance->pdev->dev, "Failed to get a free cmd\n");
return -ENOMEM;
}
/*
* Prepare a init frame. Note the init frame points to queue info
* structure. Each frame has SGL allocated after first 64 bytes. For
* this frame - since we don't need any SGL - we use SGL's space as
* queue info structure
*
* We will not get a NULL command below. We just created the pool.
*/
cmd = megasas_get_cmd(instance);
/*
* Get various operational parameters from status register
*/
instance->max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF;
/*
* 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;
instance->max_mfi_cmds = instance->max_fw_cmds;
instance->max_num_sge = (instance->instancet->read_fw_status_reg(instance) & 0xFF0000) >> 0x10;
/*
* For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands
* are reserved for IOCTL + driver's internal DCMDs.
*/
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
instance->max_scsi_cmds = (instance->max_fw_cmds -
MEGASAS_SKINNY_INT_CMDS);
sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
} else {
instance->max_scsi_cmds = (instance->max_fw_cmds -
MEGASAS_INT_CMDS);
sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS));
}
instance->cur_can_queue = instance->max_scsi_cmds;
/*
* Create a pool of commands
*/
if (megasas_alloc_cmds(instance))
goto fail_alloc_cmds;
/*
* Allocate memory for reply queue. Length of reply queue should
* be _one_ more than the maximum commands handled by the firmware.
*
* Note: When FW completes commands, it places corresponding contex
* values in this circular reply queue. This circular queue is a fairly
* typical producer-consumer queue. FW is the producer (of completed
* commands) and the driver is the consumer.
*/
context_sz = sizeof(u32);
reply_q_sz = context_sz * (instance->max_fw_cmds + 1);
if (!instance->reply_queue) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Out of DMA mem for reply queue\n");
goto fail_reply_queue;
}
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
if (megasas_get_ctrl_info(instance)) {
dev_err(&instance->pdev->dev, "(%d): Could get controller info "
"Fail from %s %d\n", instance->unique_id,
__func__, __LINE__);
goto fail_fw_init;
}
int i;
int count;
struct megasas_irq_context *irq_ctx;
count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
if (instance->adapter_type != MFI_SERIES) {
for (i = 0; i < count; i++) {
irq_ctx = &instance->irq_context[i];
irq_poll_disable(&irq_ctx->irqpoll);
}
}
if (instance->msix_vectors)
for (i = 0; i < instance->msix_vectors; i++) {
if (i < instance->low_latency_index_start)
irq_update_affinity_hint(
pci_irq_vector(instance->pdev, i), NULL);
free_irq(pci_irq_vector(instance->pdev, i),
&instance->irq_context[i]);
}
else
free_irq(pci_irq_vector(instance->pdev, 0),
&instance->irq_context[0]);
}
/**
* megasas_setup_jbod_map - setup jbod map for FP seq_number.
* @instance: Adapter soft state
*
* Return 0 on success.
*/
void
megasas_setup_jbod_map(struct megasas_instance *instance)
{
int i;
struct fusion_context *fusion = instance->ctrl_context;
size_t pd_seq_map_sz;
/**
* megasas_get_device_list - Get the PD and LD device list from FW.
* @instance: Adapter soft state
* @return: Success or failure
*
* Issue DCMDs to Firmware to get the PD and LD list.
* Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
* of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
*/
static
int megasas_get_device_list(struct megasas_instance *instance)
{
if (instance->enable_fw_dev_list) {
if (megasas_host_device_list_query(instance, true))
return FAILED;
} else {
if (megasas_get_pd_list(instance) < 0) {
dev_err(&instance->pdev->dev, "failed to get PD list\n");
return FAILED;
}
if (megasas_ld_list_query(instance,
MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) {
dev_err(&instance->pdev->dev, "failed to get LD list\n");
return FAILED;
}
}
return SUCCESS;
}
/**
* megasas_set_high_iops_queue_affinity_and_hint - Set affinity and hint
* for high IOPS queues
* @instance: Adapter soft state
* return: void
*/
static inline void
megasas_set_high_iops_queue_affinity_and_hint(struct megasas_instance *instance)
{
int i;
unsigned int irq;
const struct cpumask *mask;
if (instance->perf_mode == MR_BALANCED_PERF_MODE) {
int nid = dev_to_node(&instance->pdev->dev);
if (nid == NUMA_NO_NODE)
nid = 0;
mask = cpumask_of_node(nid);
for (i = 0; i < instance->low_latency_index_start; i++) {
irq = pci_irq_vector(instance->pdev, i);
irq_set_affinity_and_hint(irq, mask);
}
}
}
static int
__megasas_alloc_irq_vectors(struct megasas_instance *instance)
{
int i, irq_flags;
struct irq_affinity desc = { .pre_vectors = instance->low_latency_index_start };
struct irq_affinity *descp = &desc;
/* Do not allocate msix vectors for poll_queues.
* msix_vectors is always within a range of FW supported reply queue.
*/
i = pci_alloc_irq_vectors_affinity(instance->pdev,
instance->low_latency_index_start,
instance->msix_vectors - instance->iopoll_q_count, irq_flags, descp);
return i;
}
/**
* megasas_alloc_irq_vectors - Allocate IRQ vectors/enable MSI-x vectors
* @instance: Adapter soft state
* return: void
*/
static void
megasas_alloc_irq_vectors(struct megasas_instance *instance)
{
int i;
unsigned int num_msix_req;
instance->iopoll_q_count = 0;
if ((instance->adapter_type != MFI_SERIES) &&
poll_queues) {
switch (instance->adapter_type) {
case VENTURA_SERIES:
fusion->pcie_bw_limitation = true;
break;
case AERO_SERIES:
fusion->r56_div_offload = true;
break;
default:
break;
}
/* Check if MSI-X is supported while in ready state */
msix_enable = (instance->instancet->read_fw_status_reg(instance) & 0x4000000) >> 0x1a;
if (msix_enable && !msix_disable) {
/*
* For Aero (if some conditions are met), driver will configure a
* few additional reply queues with interrupt coalescing enabled.
* These queues with interrupt coalescing enabled are called
* High IOPS queues and rest of reply queues (based on number of
* logical CPUs) are termed as Low latency queues.
*
* Total Number of reply queues = High IOPS queues + low latency queues
*
* For rest of fusion adapters, 1 additional reply queue will be
* reserved for management commands, rest of reply queues
* (based on number of logical CPUs) will be used for IOs and
* referenced as IO queues.
* Total Number of reply queues = 1 + IO queues
*
* MFI adapters supports single MSI-x so single reply queue
* will be used for IO and management commands.
*/
/*
* For Aero, if PCIe link speed is <16 GT/s, then driver should operate
* in latency perf mode and enable R1 PCI bandwidth algorithm
*/
if (speed < 0x4) {
instance->perf_mode = MR_LATENCY_PERF_MODE;
fusion->pcie_bw_limitation = true;
}
/*
* Performance mode settings provided through module parameter-perf_mode will
* take affect only for:
* 1. Aero family of adapters.
* 2. When user sets module parameter- perf_mode in range of 0-2.
*/
if ((perf_mode >= MR_BALANCED_PERF_MODE) &&
(perf_mode <= MR_LATENCY_PERF_MODE))
instance->perf_mode = perf_mode;
/*
* If intr coalescing is not supported by controller FW, then IOPS
* and Balanced modes are not feasible.
*/
if (!intr_coalescing)
instance->perf_mode = MR_LATENCY_PERF_MODE;
megasas_alloc_irq_vectors(instance);
if (!instance->msix_vectors)
instance->msix_load_balance = false;
}
/*
* MSI-X host index 0 is common for all adapter.
* It is used for all MPT based Adapters.
*/
if (instance->msix_combined) {
instance->reply_post_host_index_addr[0] =
(u32 *)((u8 *)instance->reg_set +
MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET);
} else {
instance->reply_post_host_index_addr[0] =
(u32 *)((u8 *)instance->reg_set +
MPI2_REPLY_POST_HOST_INDEX_OFFSET);
}
if (!instance->msix_vectors) {
i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_INTX);
if (i < 0)
goto fail_init_adapter;
}
/* stream detection initialization */
if (instance->adapter_type >= VENTURA_SERIES) {
fusion->stream_detect_by_ld =
kcalloc(MAX_LOGICAL_DRIVES_EXT,
sizeof(struct LD_STREAM_DETECT *),
GFP_KERNEL);
if (!fusion->stream_detect_by_ld) {
dev_err(&instance->pdev->dev,
"unable to allocate stream detection for pool of LDs\n");
goto fail_get_ld_pd_list;
}
for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) {
fusion->stream_detect_by_ld[i] =
kzalloc(sizeof(struct LD_STREAM_DETECT),
GFP_KERNEL);
if (!fusion->stream_detect_by_ld[i]) {
dev_err(&instance->pdev->dev,
"unable to allocate stream detect by LD\n");
for (j = 0; j < i; ++j)
kfree(fusion->stream_detect_by_ld[j]);
kfree(fusion->stream_detect_by_ld);
fusion->stream_detect_by_ld = NULL;
goto fail_get_ld_pd_list;
}
fusion->stream_detect_by_ld[i]->mru_bit_map
= MR_STREAM_BITMAP;
}
}
/*
* Compute the max allowed sectors per IO: The controller info has two
* limits on max sectors. Driver should use the minimum of these two.
*
* 1 << stripe_sz_ops.min = max sectors per strip
*
* Note that older firmwares ( < FW ver 30) didn't report information
* to calculate max_sectors_1. So the number ended up as zero always.
*/
tmp_sectors = 0;
ctrl_info = instance->ctrl_info_buf;
/*
* Create and start watchdog thread which will monitor
* controller state every 1 sec and trigger OCR when
* it enters fault state
*/
if (instance->adapter_type != MFI_SERIES)
if (megasas_fusion_start_watchdog(instance) != SUCCESS)
goto fail_start_watchdog;
/**
* megasas_get_seq_num - Gets latest event sequence numbers
* @instance: Adapter soft state
* @eli: FW event log sequence numbers information
*
* FW maintains a log of all events in a non-volatile area. Upper layers would
* usually find out the latest sequence number of the events, the seq number at
* the boot etc. They would "read" all the events below the latest seq number
* by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq
* number), they would subsribe to AEN (asynchronous event notification) and
* wait for the events to happen.
*/
static int
megasas_get_seq_num(struct megasas_instance *instance,
struct megasas_evt_log_info *eli)
{
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct megasas_evt_log_info *el_info;
dma_addr_t el_info_h = 0;
int ret;
/**
* megasas_register_aen - Registers for asynchronous event notification
* @instance: Adapter soft state
* @seq_num: The starting sequence number
* @class_locale_word: Class of the event
*
* This function subscribes for AEN for events beyond the @seq_num. It requests
* to be notified if and only if the event is of type @class_locale
*/
static int
megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
u32 class_locale_word)
{
int ret_val;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
union megasas_evt_class_locale curr_aen;
union megasas_evt_class_locale prev_aen;
/*
* If there an AEN pending already (aen_cmd), check if the
* class_locale of that pending AEN is inclusive of the new
* AEN request we currently have. If it is, then we don't have
* to do anything. In other words, whichever events the current
* AEN request is subscribing to, have already been subscribed
* to.
*
* If the old_cmd is _not_ inclusive, then we have to abort
* that command, form a class_locale that is superset of both
* old and current and re-issue to the FW
*/
if ((curr_aen.members.class < MFI_EVT_CLASS_DEBUG) ||
(curr_aen.members.class > MFI_EVT_CLASS_DEAD)) {
dev_info(&instance->pdev->dev,
"%s %d out of range class %d send by application\n",
__func__, __LINE__, curr_aen.members.class);
return 0;
}
/*
* A class whose enum value is smaller is inclusive of all
* higher values. If a PROGRESS (= -1) was previously
* registered, then a new registration requests for higher
* classes need not be sent to FW. They are automatically
* included.
*
* Locale numbers don't have such hierarchy. They are bitmap
* values
*/
if ((prev_aen.members.class <= curr_aen.members.class) &&
!((prev_aen.members.locale & curr_aen.members.locale) ^
curr_aen.members.locale)) {
/*
* Previously issued event registration includes
* current request. Nothing to do.
*/
return 0;
} else {
curr_aen.members.locale |= prev_aen.members.locale;
if (prev_aen.members.class < curr_aen.members.class)
curr_aen.members.class = prev_aen.members.class;
if (instance->aen_cmd != NULL) {
megasas_return_cmd(instance, cmd);
return 0;
}
/*
* Store reference to the cmd used to register for AEN. When an
* application wants us to register for AEN, we have to abort this
* cmd and re-register with a new EVENT LOCALE supplied by that app
*/
instance->aen_cmd = cmd;
/*
* Issue the aen registration frame
*/
instance->instancet->issue_dcmd(instance, cmd);
return 0;
}
/* megasas_get_target_prop - Send DCMD with below details to firmware.
*
* This DCMD will fetch few properties of LD/system PD defined
* in MR_TARGET_DEV_PROPERTIES. eg. Queue Depth, MDTS value.
*
* DCMD send by drivers whenever new target is added to the OS.
*
* dcmd.opcode - MR_DCMD_DEV_GET_TARGET_PROP
* dcmd.mbox.b[0] - DCMD is to be fired for LD or system PD.
* 0 = system PD, 1 = LD.
* dcmd.mbox.s[1] - TargetID for LD/system PD.
* dcmd.sge IN - Pointer to return MR_TARGET_DEV_PROPERTIES.
*
* @instance: Adapter soft state
* @sdev: OS provided scsi device
*
* Returns 0 on success non-zero on failure.
*/
int
megasas_get_target_prop(struct megasas_instance *instance,
struct scsi_device *sdev)
{
int ret;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
u16 targetId = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) +
sdev->id;
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_err(&instance->pdev->dev,
"Failed to get cmd %s\n", __func__);
return -ENOMEM;
}
if ((instance->adapter_type != MFI_SERIES) &&
!instance->mask_interrupts)
ret = megasas_issue_blocked_cmd(instance,
cmd, MFI_IO_TIMEOUT_SECS);
else
ret = megasas_issue_polled(instance, cmd);
switch (ret) {
case DCMD_TIMEOUT:
switch (dcmd_timeout_ocr_possible(instance)) {
case INITIATE_OCR:
cmd->flags |= DRV_DCMD_SKIP_REFIRE;
mutex_unlock(&instance->reset_mutex);
megasas_reset_fusion(instance->host,
MFI_IO_TIMEOUT_OCR);
mutex_lock(&instance->reset_mutex);
break;
case KILL_ADAPTER:
megaraid_sas_kill_hba(instance);
break;
case IGNORE_TIMEOUT:
dev_info(&instance->pdev->dev,
"Ignore DCMD timeout: %s %d\n",
__func__, __LINE__);
break;
}
break;
default:
megasas_return_cmd(instance, cmd);
}
if (ret != DCMD_SUCCESS)
dev_err(&instance->pdev->dev,
"return from %s %d return value %d\n",
__func__, __LINE__, ret);
return ret;
}
/**
* megasas_start_aen - Subscribes to AEN during driver load time
* @instance: Adapter soft state
*/
static int megasas_start_aen(struct megasas_instance *instance)
{
struct megasas_evt_log_info eli;
union megasas_evt_class_locale class_locale;
/*
* Get the latest sequence number from FW
*/
memset(&eli, 0, sizeof(eli));
if (megasas_get_seq_num(instance, &eli))
return -1;
/*
* Register AEN with FW for latest sequence number plus 1
*/
class_locale.members.reserved = 0;
class_locale.members.locale = MR_EVT_LOCALE_ALL;
class_locale.members.class = MR_EVT_CLASS_DEBUG;
/* Use shared host tagset only for fusion adaptors
* if there are managed interrupts (smp affinity enabled case).
* Single msix_vectors in kdump, so shared host tag is also disabled.
*/
dev_info(&instance->pdev->dev,
"Max firmware commands: %d shared with default "
"hw_queues = %d poll_queues %d\n", instance->max_fw_cmds,
host->nr_hw_queues - instance->iopoll_q_count,
instance->iopoll_q_count);
/*
* Notify the mid-layer about the new controller
*/
if (scsi_add_host(host, &instance->pdev->dev)) {
dev_err(&instance->pdev->dev,
"Failed to add host from %s %d\n",
__func__, __LINE__);
return -ENODEV;
}
return 0;
}
/**
* megasas_set_dma_mask - Set DMA mask for supported controllers
*
* @instance: Adapter soft state
* Description:
*
* For Ventura, driver/FW will operate in 63bit DMA addresses.
*
* For invader-
* By default, driver/FW will operate in 32bit DMA addresses
* for consistent DMA mapping but if 32 bit consistent
* DMA mask fails, driver will try with 63 bit consistent
* mask provided FW is true 63bit DMA capable
*
* For older controllers(Thunderbolt and MFI based adapters)-
* driver/FW will operate in 32 bit consistent DMA addresses.
*/
static int
megasas_set_dma_mask(struct megasas_instance *instance)
{
u64 consistent_mask;
struct pci_dev *pdev;
u32 scratch_pad_1;
if (IS_DMA64) {
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(63)) &&
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
goto fail_set_dma_mask;
if ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) &&
(dma_set_coherent_mask(&pdev->dev, consistent_mask) &&
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) {
/*
* If 32 bit DMA mask fails, then try for 64 bit mask
* for FW capable of handling 64 bit DMA.
*/
scratch_pad_1 = megasas_readl
(instance, &instance->reg_set->outbound_scratch_pad_1);
if (!(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET))
goto fail_set_dma_mask;
else if (dma_set_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(63)))
goto fail_set_dma_mask;
}
} else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
goto fail_set_dma_mask;
/**
* megasas_alloc_ctrl_mem - Allocate per controller memory for core data
* structures which are not common across MFI
* adapters and fusion adapters.
* For MFI based adapters, allocate producer and
* consumer buffers. For fusion adapters, allocate
* memory for fusion context.
* @instance: Adapter soft state
* return: 0 for SUCCESS
*/
static int megasas_alloc_ctrl_mem(struct megasas_instance *instance)
{
instance->reply_map = kcalloc(nr_cpu_ids, sizeof(unsigned int),
GFP_KERNEL);
if (!instance->reply_map)
return -ENOMEM;
switch (instance->adapter_type) {
case MFI_SERIES:
if (megasas_alloc_mfi_ctrl_mem(instance))
return -ENOMEM;
break;
case AERO_SERIES:
case VENTURA_SERIES:
case THUNDERBOLT_SERIES:
case INVADER_SERIES:
if (megasas_alloc_fusion_context(instance))
return -ENOMEM;
break;
}
return 0;
}
/*
* megasas_free_ctrl_mem - Free fusion context for fusion adapters and
* producer, consumer buffers for MFI adapters
*
* @instance - Adapter soft instance
*
*/
static inline void megasas_free_ctrl_mem(struct megasas_instance *instance)
{
kfree(instance->reply_map);
if (instance->adapter_type == MFI_SERIES) {
if (instance->producer)
dma_free_coherent(&instance->pdev->dev, sizeof(u32),
instance->producer,
instance->producer_h);
if (instance->consumer)
dma_free_coherent(&instance->pdev->dev, sizeof(u32),
instance->consumer,
instance->consumer_h);
} else {
megasas_free_fusion_context(instance);
}
}
/**
* megasas_alloc_ctrl_dma_buffers - Allocate consistent DMA buffers during
* driver load time
*
* @instance: Adapter soft instance
*
* @return: O for SUCCESS
*/
static inline
int megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance)
{
struct pci_dev *pdev = instance->pdev;
struct fusion_context *fusion = instance->ctrl_context;
if (instance->adapter_type != MFI_SERIES)
INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
else
INIT_WORK(&instance->work_init, process_fw_state_change_wq);
}
/**
* megasas_probe_one - PCI hotplug entry point
* @pdev: PCI device structure
* @id: PCI ids of supported hotplugged adapter
*/
static int megasas_probe_one(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int rval, pos;
struct Scsi_Host *host;
struct megasas_instance *instance;
u16 control = 0;
switch (pdev->device) {
case PCI_DEVICE_ID_LSI_AERO_10E0:
case PCI_DEVICE_ID_LSI_AERO_10E3:
case PCI_DEVICE_ID_LSI_AERO_10E4:
case PCI_DEVICE_ID_LSI_AERO_10E7:
dev_err(&pdev->dev, "Adapter is in non secure mode\n");
return 1;
case PCI_DEVICE_ID_LSI_AERO_10E1:
case PCI_DEVICE_ID_LSI_AERO_10E5:
dev_info(&pdev->dev, "Adapter is in configurable secure mode\n");
break;
}
/* Reset MSI-X in the kdump kernel */
if (reset_devices) {
pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
if (pos) {
pci_read_config_word(pdev, pos + PCI_MSIX_FLAGS,
&control);
if (control & PCI_MSIX_FLAGS_ENABLE) {
dev_info(&pdev->dev, "resetting MSI-X\n");
pci_write_config_word(pdev,
pos + PCI_MSIX_FLAGS,
control &
~PCI_MSIX_FLAGS_ENABLE);
}
}
}
/*
* PCI prepping: enable device set bus mastering and dma mask
*/
rval = pci_enable_device_mem(pdev);
/*
* Initialize MFI Firmware
*/
if (megasas_init_fw(instance))
goto fail_init_mfi;
if (instance->requestorId) {
if (instance->PlasmaFW111) {
instance->vf_affiliation_111 =
dma_alloc_coherent(&pdev->dev,
sizeof(struct MR_LD_VF_AFFILIATION_111),
&instance->vf_affiliation_111_h,
GFP_KERNEL);
if (!instance->vf_affiliation_111)
dev_warn(&pdev->dev, "Can't allocate "
"memory for VF affiliation buffer\n");
} else {
instance->vf_affiliation =
dma_alloc_coherent(&pdev->dev,
(MAX_LOGICAL_DRIVES + 1) *
sizeof(struct MR_LD_VF_AFFILIATION),
&instance->vf_affiliation_h,
GFP_KERNEL);
if (!instance->vf_affiliation)
dev_warn(&pdev->dev, "Can't allocate "
"memory for VF affiliation buffer\n");
}
}
/*
* Store instance in PCI softstate
*/
pci_set_drvdata(pdev, instance);
/*
* Add this controller to megasas_mgmt_info structure so that it
* can be exported to management applications
*/
megasas_mgmt_info.count++;
megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance;
megasas_mgmt_info.max_index++;
/*
* Register with SCSI mid-layer
*/
if (megasas_io_attach(instance))
goto fail_io_attach;
instance->unload = 0;
/*
* Trigger SCSI to scan our drives
*/
if (!instance->enable_fw_dev_list ||
(instance->host_device_list_buf->count > 0))
scsi_scan_host(host);
if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
cmd = megasas_get_cmd(instance);
if (!cmd)
return;
if (instance->aen_cmd)
megasas_issue_blocked_abort_cmd(instance,
instance->aen_cmd, MFI_IO_TIMEOUT_SECS);
if (instance->map_update_cmd)
megasas_issue_blocked_abort_cmd(instance,
instance->map_update_cmd, MFI_IO_TIMEOUT_SECS);
if (instance->jbod_seq_cmd)
megasas_issue_blocked_abort_cmd(instance,
instance->jbod_seq_cmd, MFI_IO_TIMEOUT_SECS);
/* cancel the delayed work if this work still in queue */
if (instance->ev != NULL) {
struct megasas_aen_event *ev = instance->ev;
cancel_delayed_work_sync(&ev->hotplug_work);
instance->ev = NULL;
}
skip_firing_dcmds:
/* cancel the delayed work if this work still in queue*/
if (instance->ev != NULL) {
struct megasas_aen_event *ev = instance->ev;
cancel_delayed_work_sync(&ev->hotplug_work);
instance->ev = NULL;
}
/* cancel all wait events */
wake_up_all(&instance->int_cmd_wait_q);
tasklet_kill(&instance->isr_tasklet);
/*
* Take the instance off the instance array. Note that we will not
* decrement the max_index. We let this array be sparse array
*/
for (i = 0; i < megasas_mgmt_info.max_index; i++) {
if (megasas_mgmt_info.instance[i] == instance) {
megasas_mgmt_info.count--;
megasas_mgmt_info.instance[i] = NULL;
break;
}
}
instance->instancet->disable_intr(instance);
megasas_destroy_irqs(instance);
if (instance->msix_vectors)
pci_free_irq_vectors(instance->pdev);
if (instance->adapter_type >= VENTURA_SERIES) {
for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i)
kfree(fusion->stream_detect_by_ld[i]);
kfree(fusion->stream_detect_by_ld);
fusion->stream_detect_by_ld = NULL;
}
if (instance->adapter_type != MFI_SERIES) {
megasas_release_fusion(instance);
pd_seq_map_sz =
struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC,
seq, MAX_PHYSICAL_DEVICES);
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]);
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 (instance->msix_vectors)
pci_free_irq_vectors(instance->pdev);
}
/*
* megasas_mgmt_open - char node "open" entry point
* @inode: char node inode
* @filep: char node file
*/
static int megasas_mgmt_open(struct inode *inode, struct file *filep)
{
/*
* Allow only those users with admin rights
*/
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
return 0;
}
/*
* megasas_mgmt_fasync - Async notifier registration from applications
* @fd: char node file descriptor number
* @filep: char node file
* @mode: notifier on/off
*
* This function adds the calling process to a driver global queue. When an
* event occurs, SIGIO will be sent to all processes in this queue.
*/
static int megasas_mgmt_fasync(int fd, struct file *filep, int mode)
{
int rc;
/*
* megasas_set_crash_dump_params_ioctl:
* Send CRASH_DUMP_MODE DCMD to all controllers
* @cmd: MFI command frame
*/
static int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd)
{
struct megasas_instance *local_instance;
int i, error = 0;
int crash_support;
crash_support = cmd->frame->dcmd.mbox.w[0];
for (i = 0; i < megasas_mgmt_info.max_index; i++) {
local_instance = megasas_mgmt_info.instance[i];
if (local_instance && local_instance->crash_dump_drv_support) {
if ((atomic_read(&local_instance->adprecovery) ==
MEGASAS_HBA_OPERATIONAL) &&
!megasas_set_crash_dump_params(local_instance,
crash_support)) {
local_instance->crash_dump_app_support =
crash_support;
dev_info(&local_instance->pdev->dev,
"Application firmware crash "
"dump mode set success\n");
error = 0;
} else {
dev_info(&local_instance->pdev->dev,
"Application firmware crash "
"dump mode set failed\n");
error = -1;
}
}
}
return error;
}
cmd = megasas_get_cmd(instance);
if (!cmd) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a cmd packet\n");
return -ENOMEM;
}
/*
* User's IOCTL packet has 2 frames (maximum). Copy those two
* frames into our cmd's frames. cmd->frame's context will get
* overwritten when we copy from user's frames. So set that value
* alone separately
*/
memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
cmd->frame->hdr.context = cpu_to_le32(cmd->index);
cmd->frame->hdr.pad_0 = 0;
/*
* The management interface between applications and the fw uses
* MFI frames. E.g, RAID configuration changes, LD property changes
* etc are accomplishes through different kinds of MFI frames. The
* driver needs to care only about substituting user buffers with
* kernel buffers in SGLs. The location of SGL is embedded in the
* struct iocpacket itself.
*/
if (instance->consistent_mask_64bit)
kern_sge64 = (struct megasas_sge64 *)
((unsigned long)cmd->frame + ioc->sgl_off);
else
kern_sge32 = (struct megasas_sge32 *)
((unsigned long)cmd->frame + ioc->sgl_off);
/*
* For each user buffer, create a mirror buffer and copy in
*/
for (i = 0; i < ioc->sge_count; i++) {
if (!ioc->sgl[i].iov_len)
continue;
kbuff_arr[i] = dma_alloc_coherent(&instance->pdev->dev,
ioc->sgl[i].iov_len,
&buf_handle, GFP_KERNEL);
if (!kbuff_arr[i]) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc "
"kernel SGL buffer for IOCTL\n");
error = -ENOMEM;
goto out;
}
/*
* We don't change the dma_coherent_mask, so
* dma_alloc_coherent only returns 32bit addresses
*/
if (instance->consistent_mask_64bit) {
kern_sge64[i].phys_addr = cpu_to_le64(buf_handle);
kern_sge64[i].length = cpu_to_le32(ioc->sgl[i].iov_len);
} else {
kern_sge32[i].phys_addr = cpu_to_le32(buf_handle);
kern_sge32[i].length = cpu_to_le32(ioc->sgl[i].iov_len);
}
/*
* We created a kernel buffer corresponding to the
* user buffer. Now copy in from the user buffer
*/
if (copy_from_user(kbuff_arr[i], ioc->sgl[i].iov_base,
(u32) (ioc->sgl[i].iov_len))) {
error = -EFAULT;
goto out;
}
}
if (ioc->sense_len) {
/* make sure the pointer is part of the frame */
if (ioc->sense_off >
(sizeof(union megasas_frame) - sizeof(__le64))) {
error = -EINVAL;
goto out;
}
sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len,
&sense_handle, GFP_KERNEL);
if (!sense) {
error = -ENOMEM;
goto out;
}
/* always store 64 bits regardless of addressing */
sense_ptr = (void *)cmd->frame + ioc->sense_off;
put_unaligned_le64(sense_handle, sense_ptr);
}
/*
* Set the sync_cmd flag so that the ISR knows not to complete this
* cmd to the SCSI mid-layer
*/
cmd->sync_cmd = 1;
ret = megasas_issue_blocked_cmd(instance, cmd, 0);
switch (ret) {
case DCMD_INIT:
case DCMD_BUSY:
cmd->sync_cmd = 0;
dev_err(&instance->pdev->dev,
"return -EBUSY from %s %d cmd 0x%x opcode 0x%x cmd->cmd_status_drv 0x%x\n",
__func__, __LINE__, cmd->frame->hdr.cmd, opcode,
cmd->cmd_status_drv);
error = -EBUSY;
goto out;
}
cmd->sync_cmd = 0;
if (instance->unload == 1) {
dev_info(&instance->pdev->dev, "Driver unload is in progress "
"don't submit data to application\n");
goto out;
}
/*
* copy out the kernel buffers to user buffers
*/
for (i = 0; i < ioc->sge_count; i++) {
if (copy_to_user(ioc->sgl[i].iov_base, kbuff_arr[i],
ioc->sgl[i].iov_len)) {
error = -EFAULT;
goto out;
}
}
/*
* copy out the sense
*/
if (ioc->sense_len) {
void __user *uptr;
/*
* sense_ptr points to the location that has the user
* sense buffer address
*/
sense_ptr = (void *)ioc->frame.raw + ioc->sense_off;
if (in_compat_syscall())
uptr = compat_ptr(get_unaligned((compat_uptr_t *)
sense_ptr));
else
uptr = get_unaligned((void __user **)sense_ptr);
if (copy_to_user(uptr, sense, ioc->sense_len)) {
dev_err(&instance->pdev->dev, "Failed to copy out to user "
"sense data\n");
error = -EFAULT;
goto out;
}
}
/*
* copy the status codes returned by the fw
*/
if (copy_to_user(&user_ioc->frame.hdr.cmd_status,
&cmd->frame->hdr.cmd_status, sizeof(u8))) {
dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error copying out cmd_status\n");
error = -EFAULT;
}
out:
if (sense) {
dma_free_coherent(&instance->pdev->dev, ioc->sense_len,
sense, sense_handle);
}
for (i = 0; i < ioc->sge_count; i++) {
if (kbuff_arr[i]) {
if (instance->consistent_mask_64bit)
dma_free_coherent(&instance->pdev->dev,
le32_to_cpu(kern_sge64[i].length),
kbuff_arr[i],
le64_to_cpu(kern_sge64[i].phys_addr));
else
dma_free_coherent(&instance->pdev->dev,
le32_to_cpu(kern_sge32[i].length),
kbuff_arr[i],
le32_to_cpu(kern_sge32[i].phys_addr));
kbuff_arr[i] = NULL;
}
}
/**
* megasas_update_device_list - Update the PD and LD device list from FW
* after an AEN event notification
* @instance: Adapter soft state
* @event_type: Indicates type of event (PD or LD event)
*
* @return: Success or failure
*
* Issue DCMDs to Firmware to update the internal device list in driver.
* Based on the FW support, driver sends the HOST_DEVICE_LIST or combination
* of PD_LIST/LD_LIST_QUERY DCMDs to get the device list.
*/
static
int megasas_update_device_list(struct megasas_instance *instance,
int event_type)
{
int dcmd_ret;
if (instance->enable_fw_dev_list) {
return megasas_host_device_list_query(instance, false);
} else {
if (event_type & SCAN_PD_CHANNEL) {
dcmd_ret = megasas_get_pd_list(instance);
if (dcmd_ret != DCMD_SUCCESS)
return dcmd_ret;
}
if (event_type & SCAN_VD_CHANNEL) {
if (!instance->requestorId ||
megasas_get_ld_vf_affiliation(instance, 0)) {
return megasas_ld_list_query(instance,
MR_LD_QUERY_TYPE_EXPOSED_TO_HOST);
}
}
}
return DCMD_SUCCESS;
}
/**
* megasas_add_remove_devices - Add/remove devices to SCSI mid-layer
* after an AEN event notification
* @instance: Adapter soft state
* @scan_type: Indicates type of devices (PD/LD) to add
* @return void
*/
static
void megasas_add_remove_devices(struct megasas_instance *instance,
int scan_type)
{
int i, j;
u16 pd_index = 0;
u16 ld_index = 0;
u16 channel = 0, id = 0;
struct Scsi_Host *host;
struct scsi_device *sdev1;
struct MR_HOST_DEVICE_LIST *targetid_list = NULL;
struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL;
host = instance->host;
if (instance->enable_fw_dev_list) {
targetid_list = instance->host_device_list_buf;
for (i = 0; i < targetid_list->count; i++) {
targetid_entry = &targetid_list->host_device_list[i];
if (targetid_entry->flags.u.bits.is_sys_pd) {
channel = le16_to_cpu(targetid_entry->target_id) /
MEGASAS_MAX_DEV_PER_CHANNEL;
id = le16_to_cpu(targetid_entry->target_id) %
MEGASAS_MAX_DEV_PER_CHANNEL;
} else {
channel = MEGASAS_MAX_PD_CHANNELS +
(le16_to_cpu(targetid_entry->target_id) /
MEGASAS_MAX_DEV_PER_CHANNEL);
id = le16_to_cpu(targetid_entry->target_id) %
MEGASAS_MAX_DEV_PER_CHANNEL;
}
sdev1 = scsi_device_lookup(host, channel, id, 0);
if (!sdev1) {
scsi_add_device(host, channel, id, 0);
} else {
scsi_device_put(sdev1);
}
}
}
if (scan_type & SCAN_PD_CHANNEL) {
for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j;
sdev1 = scsi_device_lookup(host, i, j, 0);
if (instance->pd_list[pd_index].driveState ==
MR_PD_STATE_SYSTEM) {
if (!sdev1)
scsi_add_device(host, i, j, 0);
else
scsi_device_put(sdev1);
} else {
if (sdev1)
megasas_remove_scsi_device(sdev1);
}
}
}
}
if (scan_type & SCAN_VD_CHANNEL) {
for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) {
for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j;
sdev1 = scsi_device_lookup(host,
MEGASAS_MAX_PD_CHANNELS + i, j, 0);
if (instance->ld_ids[ld_index] != 0xff) {
if (!sdev1)
scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0);
else
scsi_device_put(sdev1);
} else {
if (sdev1)
megasas_remove_scsi_device(sdev1);
}
}
}
}
case MR_EVT_PD_INSERTED:
case MR_EVT_PD_REMOVED:
event_type = SCAN_PD_CHANNEL;
break;
case MR_EVT_LD_OFFLINE:
case MR_EVT_LD_DELETED:
ld_target_id = instance->evt_detail->args.ld.target_id;
sdev1 = scsi_device_lookup(instance->host,
MEGASAS_MAX_PD_CHANNELS +
(ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL),
(ld_target_id % MEGASAS_MAX_DEV_PER_CHANNEL), 0);
if (sdev1) {
mutex_unlock(&instance->reset_mutex);
megasas_remove_scsi_device(sdev1);
mutex_lock(&instance->reset_mutex);
}
event_type = SCAN_VD_CHANNEL;
break;
case MR_EVT_LD_CREATED:
event_type = SCAN_VD_CHANNEL;
break;
case MR_EVT_CFG_CLEARED:
case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
case MR_EVT_FOREIGN_CFG_IMPORTED:
case MR_EVT_LD_STATE_CHANGE:
event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL;
dev_info(&instance->pdev->dev, "scanning for scsi%d...\n",
instance->host->host_no);
break;
if ((event_log_level < MFI_EVT_CLASS_DEBUG) ||
(event_log_level > MFI_EVT_CLASS_DEAD)) {
pr_warn("megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n");
event_log_level = MFI_EVT_CLASS_CRITICAL;
}
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_version);
if (rval)
goto err_dcf_attr_ver;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
if (rval)
goto err_dcf_rel_date;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
if (rval)
goto err_dcf_support_poll_for_event;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
if (rval)
goto err_dcf_dbg_lvl;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_device_change);
if (rval)
goto err_dcf_support_device_change;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_nvme_encapsulation);
if (rval)
goto err_dcf_support_nvme_encapsulation;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_pci_lane_margining);
if (rval)
goto err_dcf_support_pci_lane_margining;
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.