/* * This is the Fusion MPT base driver providing common API layer interface * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c * Copyright (C) 2012-2014 LSI Corporation * Copyright (C) 2013-2014 Avago Technologies * (mailto: MPT-FusionLinux.pdl@avagotech.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * NO WARRANTY * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is * solely responsible for determining the appropriateness of using and * distributing the Program and assumes all risks associated with its * exercise of rights under this Agreement, including but not limited to * the risks and costs of program errors, damage to or loss of data, * programs or equipment, and unavailability or interruption of operations.
* DISCLAIMER OF LIABILITY * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
* You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA.
*/
staticint irqpoll_weight = -1;
module_param(irqpoll_weight, int, 0444);
MODULE_PARM_DESC(irqpoll_weight, "irq poll weight (default= one fourth of HBA queue depth)");
staticint mpt3sas_fwfault_debug;
MODULE_PARM_DESC(mpt3sas_fwfault_debug, " enable detection of firmware fault and halt firmware - (default=0)");
staticint perf_mode = -1;
module_param(perf_mode, int, 0444);
MODULE_PARM_DESC(perf_mode, "Performance mode (only for Aero/Sea Generation), options:\n\t\t" "0 - balanced: high iops mode is enabled &\n\t\t" "interrupt coalescing is enabled only on high iops queues,\n\t\t" "1 - iops: high iops mode is disabled &\n\t\t" "interrupt coalescing is enabled on all queues,\n\t\t" "2 - latency: high iops mode is disabled &\n\t\t" "interrupt coalescing is enabled on all queues with timeout value 0xA,\n" "\t\tdefault - default perf_mode is 'balanced'"
);
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" "when poll_queues are enabled then &\n\t\t" "perf_mode is set to latency mode. &\n\t\t"
);
/** * mpt3sas_base_check_cmd_timeout - Function * to check timeout and command termination due * to Host reset. * * @ioc: per adapter object. * @status: Status of issued command. * @mpi_request:mf request pointer. * @sz: size of buffer. * * Return: 1/0 Reset to be done or Not
*/
u8
mpt3sas_base_check_cmd_timeout(struct MPT3SAS_ADAPTER *ioc,
u8 status, void *mpi_request, int sz)
{
u8 issue_reset = 0;
if (!(status & MPT3_CMD_RESET))
issue_reset = 1;
ioc_err(ioc, "Command %s\n",
issue_reset == 0 ? "terminated due to Host Reset" : "Timeout");
_debug_dump_mf(mpi_request, sz);
return issue_reset;
}
/** * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. * @val: ? * @kp: ? * * Return: ?
*/ staticint
_scsih_set_fwfault_debug(constchar *val, conststruct kernel_param *kp)
{ int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc;
if (ret) return ret;
/* global ioc spinlock to protect controller list on list operations */
pr_info("setting fwfault_debug(%d)\n", mpt3sas_fwfault_debug);
spin_lock(&gioc_lock);
list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
ioc->fwfault_debug = mpt3sas_fwfault_debug;
spin_unlock(&gioc_lock); return 0;
}
module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug,
param_get_int, &mpt3sas_fwfault_debug, 0644);
/** * _base_readl_aero - retry readl for max three times. * @addr: MPT Fusion system interface register address * * Retry the readl() for max three times if it gets zero value * while reading the system interface register.
*/ staticinline u32
_base_readl_aero(constvoid __iomem *addr)
{
u32 i = 0, ret_val;
do {
ret_val = readl(addr);
i++;
} while (ret_val == 0 && i < 3);
return ret_val;
}
static u32
_base_readl_ext_retry(constvoid __iomem *addr)
{
u32 i, ret_val;
for (i = 0 ; i < 30 ; i++) {
ret_val = readl(addr); if (ret_val != 0) break;
}
/** * _base_clone_reply_to_sys_mem - copies reply to reply free iomem * in BAR0 space. * * @ioc: per adapter object * @reply: reply message frame(lower 32bit addr) * @index: System request message index.
*/ staticvoid
_base_clone_reply_to_sys_mem(struct MPT3SAS_ADAPTER *ioc, u32 reply,
u32 index)
{ /* * 256 is offset within sys register. * 256 offset MPI frame starts. Max MPI frame supported is 32. * 32 * 128 = 4K. From here, Clone of reply free for mcpu starts
*/
u16 cmd_credit = ioc->facts.RequestCredit + 1; void __iomem *reply_free_iomem = (void __iomem *)ioc->chip +
MPI_FRAME_START_OFFSET +
(cmd_credit * ioc->request_sz) + (index * sizeof(u32));
writel(reply, reply_free_iomem);
}
/** * _base_clone_mpi_to_sys_mem - Writes/copies MPI frames * to system/BAR0 region. * * @dst_iomem: Pointer to the destination location in BAR0 space. * @src: Pointer to the Source data. * @size: Size of data to be copied.
*/ staticvoid
_base_clone_mpi_to_sys_mem(void *dst_iomem, void *src, u32 size)
{ int i;
u32 *src_virt_mem = (u32 *)src;
for (i = 0; i < size/4; i++)
writel((u32)src_virt_mem[i],
(void __iomem *)dst_iomem + (i * 4));
}
/** * _base_clone_to_sys_mem - Writes/copies data to system/BAR0 region * * @dst_iomem: Pointer to the destination location in BAR0 space. * @src: Pointer to the Source data. * @size: Size of data to be copied.
*/ staticvoid
_base_clone_to_sys_mem(void __iomem *dst_iomem, void *src, u32 size)
{ int i;
u32 *src_virt_mem = (u32 *)(src);
for (i = 0; i < size/4; i++)
writel((u32)src_virt_mem[i],
(void __iomem *)dst_iomem + (i * 4));
}
/** * _base_get_chain - Calculates and Returns virtual chain address * for the provided smid in BAR0 space. * * @ioc: per adapter object * @smid: system request message index * @sge_chain_count: Scatter gather chain count. * * Return: the chain address.
*/ staticinlinevoid __iomem*
_base_get_chain(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u8 sge_chain_count)
{ void __iomem *base_chain, *chain_virt;
u16 cmd_credit = ioc->facts.RequestCredit + 1;
/** * _base_get_buffer_bar0 - Calculates and Returns BAR0 mapped Host * buffer address for the provided smid. * (Each smid can have 64K starts from 17024) * * @ioc: per adapter object * @smid: system request message index * * Return: Pointer to buffer location in BAR0.
*/
/* From smid we can get scsi_cmd, once we have sg_scmd, * we just need to get sg_virt and sg_next to get virtual * address associated with sgel->Address.
*/
if (is_scsiio_req) { /* Get scsi_cmd using smid */
scmd = mpt3sas_scsih_scsi_lookup_get(ioc, smid); if (scmd == NULL) {
ioc_err(ioc, "scmd is NULL\n"); return;
}
/* Get sg_scmd from scmd provided */
sg_scmd = scsi_sglist(scmd);
}
/* * 0 - 255 System register * 256 - 4352 MPI Frame. (This is based on maxCredit 32) * 4352 - 4864 Reply_free pool (512 byte is reserved * considering maxCredit 32. Reply need extra * room, for mCPU case kept four times of * maxCredit). * 4864 - 17152 SGE chain element. (32cmd * 3 chain of * 128 byte size = 12288) * 17152 - x Host buffer mapped with smid. * (Each smid can have 64K Max IO.) * BAR0+Last 1K MSIX Addr and Data * Total size in use 2113664 bytes of 4MB BAR0
*/
switch (sgl_flags & MPI2_SGE_FLAGS_ELEMENT_MASK) { case MPI2_SGE_FLAGS_CHAIN_ELEMENT: /* * Helper function which on passing * chain_buffer_dma returns chain_buffer. Get * the virtual address for sgel->Address
*/
sgel_next =
_base_get_chain_buffer_dma_to_chain_buffer(ioc,
le32_to_cpu(sgel->Address)); if (sgel_next == NULL) return; /* * This is coping 128 byte chain * frame (not a host buffer)
*/
dst_chain_addr[sge_chain_count] =
_base_get_chain(ioc,
smid, sge_chain_count);
src_chain_addr[sge_chain_count] =
(void *) sgel_next;
dst_addr_phys = _base_get_chain_phys(ioc,
smid, sge_chain_count);
WARN_ON(dst_addr_phys > U32_MAX);
sgel->Address =
cpu_to_le32(lower_32_bits(dst_addr_phys));
sgel = sgel_next;
sge_chain_count++; break; case MPI2_SGE_FLAGS_SIMPLE_ELEMENT: if (is_write) { if (is_scsiio_req) {
_base_clone_to_sys_mem(buff_ptr,
sg_virt(sg_scmd),
(le32_to_cpu(sgel->FlagsLength) &
0x00ffffff)); /* * FIXME: this relies on a a zero * PCI mem_offset.
*/
sgel->Address =
cpu_to_le32((u32)buff_ptr_phys);
} else {
_base_clone_to_sys_mem(buff_ptr,
ioc->config_vaddr,
(le32_to_cpu(sgel->FlagsLength) &
0x00ffffff));
sgel->Address =
cpu_to_le32((u32)buff_ptr_phys);
}
}
buff_ptr += (le32_to_cpu(sgel->FlagsLength) &
0x00ffffff);
buff_ptr_phys += (le32_to_cpu(sgel->FlagsLength) &
0x00ffffff); if ((le32_to_cpu(sgel->FlagsLength) &
(MPI2_SGE_FLAGS_END_OF_BUFFER
<< MPI2_SGE_FLAGS_SHIFT))) goto eob_clone_chain; else { /* * Every single element in MPT will have * associated sg_next. Better to sanity that * sg_next is not NULL, but it will be a bug * if it is null.
*/ if (is_scsiio_req) {
sg_scmd = sg_next(sg_scmd); if (sg_scmd)
sgel++; else goto eob_clone_chain;
}
} break;
}
}
eob_clone_chain: for (i = 0; i < sge_chain_count; i++) { if (is_scsiio_req)
_base_clone_to_sys_mem(dst_chain_addr[i],
src_chain_addr[i], ioc->request_sz);
}
}
/** * mpt3sas_remove_dead_ioc_func - kthread context to remove dead ioc * @arg: input argument, used to derive ioc * * Return: * 0 if controller is removed from pci subsystem. * -1 for other case.
*/ staticint mpt3sas_remove_dead_ioc_func(void *arg)
{ struct MPT3SAS_ADAPTER *ioc = (struct MPT3SAS_ADAPTER *)arg; struct pci_dev *pdev;
doorbell = mpt3sas_base_get_iocstate(ioc, 0); if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) {
ioc_err(ioc, "SAS host is non-operational !!!!\n");
/* It may be possible that EEH recovery can resolve some of * pci bus failure issues rather removing the dead ioc function * by considering controller is in a non-operational state. So * here priority is given to the EEH recovery. If it doesn't * not resolve this issue, mpt3sas driver will consider this * controller to non-operational state and remove the dead ioc * function.
*/ if (ioc->non_operational_loop++ < 5) {
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock,
flags); goto rearm_timer;
}
/* * Call _scsih_flush_pending_cmds callback so that we flush all * pending commands back to OS. This call is required to avoid * deadlock at block layer. Dead IOC will fail to do diag reset, * and this call is safe since dead ioc will never return any * command back from HW.
*/
mpt3sas_base_pause_mq_polling(ioc);
ioc->schedule_dead_ioc_flush_running_cmds(ioc); /* * Set remove_host flag early since kernel thread will * take some time to execute.
*/
ioc->remove_host = 1; /*Remove the Dead Host */
p = kthread_run(mpt3sas_remove_dead_ioc_func, ioc, "%s_dead_ioc_%d", ioc->driver_name, ioc->id); if (IS_ERR(p))
ioc_err(ioc, "%s: Running mpt3sas_dead_ioc thread failed !!!!\n",
__func__); else
ioc_err(ioc, "%s: Running mpt3sas_dead_ioc thread success !!!!\n",
__func__); return; /* don't rearm timer */
}
/* Wait until CoreDump completes or times out */ if (ioc->ioc_coredump_loop++ < timeout) {
spin_lock_irqsave(
&ioc->ioc_reset_in_progress_lock, flags); goto rearm_timer;
}
}
/** * mpt3sas_base_wait_for_coredump_completion - Wait until coredump * completes or times out * @ioc: per adapter object * @caller: caller function name * * Return: 0 for success, non-zero for failure.
*/ int
mpt3sas_base_wait_for_coredump_completion(struct MPT3SAS_ADAPTER *ioc, constchar *caller)
{
u8 timeout = (ioc->manu_pg11.CoreDumpTOSec) ?
ioc->manu_pg11.CoreDumpTOSec :
MPT3SAS_DEFAULT_COREDUMP_TIMEOUT_SECONDS;
int ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_FAULT,
timeout);
/** * mpt3sas_halt_firmware - halt's mpt controller firmware * @ioc: per adapter object * * For debugging timeout related issues. Writing 0xCOFFEE00 * to the doorbell register will halt controller firmware. With * the purpose to stop both driver and firmware, the enduser can * obtain a ring buffer from controller UART.
*/ void
mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc)
{
u32 doorbell;
if (!ioc->fwfault_debug) return;
dump_stack();
doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
mpt3sas_print_fault_code(ioc, doorbell &
MPI2_DOORBELL_DATA_MASK);
} elseif ((doorbell & MPI2_IOC_STATE_MASK) ==
MPI2_IOC_STATE_COREDUMP) {
mpt3sas_print_coredump_info(ioc, doorbell &
MPI2_DOORBELL_DATA_MASK);
} else {
writel(0xC0FFEE00, &ioc->chip->Doorbell);
ioc_err(ioc, "Firmware is halted due to command timeout\n");
}
if (ioc->fwfault_debug == 2) for (;;)
; else
panic("panic in %s\n", __func__);
}
/** * _base_sas_ioc_info - verbose translation of the ioc status * @ioc: per adapter object * @mpi_reply: reply mf payload returned from firmware * @request_hdr: request mf
*/ staticvoid
_base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
MPI2RequestHeader_t *request_hdr)
{
u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
MPI2_IOCSTATUS_MASK; char *desc = NULL;
u16 frame_sz; char *func_str = NULL;
/* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) return;
if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) return; /* * Older Firmware version doesn't support driver trigger pages. * So, skip displaying 'config invalid type' type * of error message.
*/ if (request_hdr->Function == MPI2_FUNCTION_CONFIG) {
Mpi2ConfigRequest_t *rqst = (Mpi2ConfigRequest_t *)request_hdr;
/**************************************************************************** * Common IOCStatus values for all replies
****************************************************************************/
case MPI2_IOCSTATUS_INVALID_FUNCTION:
desc = "invalid function"; break; case MPI2_IOCSTATUS_BUSY:
desc = "busy"; break; case MPI2_IOCSTATUS_INVALID_SGL:
desc = "invalid sgl"; break; case MPI2_IOCSTATUS_INTERNAL_ERROR:
desc = "internal error"; break; case MPI2_IOCSTATUS_INVALID_VPID:
desc = "invalid vpid"; break; case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
desc = "insufficient resources"; break; case MPI2_IOCSTATUS_INSUFFICIENT_POWER:
desc = "insufficient power"; break; case MPI2_IOCSTATUS_INVALID_FIELD:
desc = "invalid field"; break; case MPI2_IOCSTATUS_INVALID_STATE:
desc = "invalid state"; break; case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
desc = "op state not supported"; break;
case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: break;
/**************************************************************************** * For use by SCSI Initiator and SCSI Target end-to-end data protection
****************************************************************************/
case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
desc = "eedp guard error"; break; case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
desc = "eedp ref tag error"; break; case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
desc = "eedp app tag error"; break;
/** * mpt3sas_base_done - base internal command completion routine * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return: * 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/
u8
mpt3sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
u32 reply)
{
MPI2DefaultReply_t *mpi_reply;
/** * _base_async_event - main callback handler for firmware asyn events * @ioc: per adapter object * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return: * 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/ static u8
_base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply)
{
Mpi2EventNotificationReply_t *mpi_reply;
Mpi2EventAckRequest_t *ack_request;
u16 smid; struct _event_ack_list *delayed_event_ack;
mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1; if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) return 1;
/** * mpt3sas_base_pause_mq_polling - pause polling on the mq poll queues * when driver is flushing out the IOs. * @ioc: per adapter object * * Pause polling on the mq poll (io uring) queues when driver is flushing * out the IOs. Otherwise we may see the race condition of completing the same * IO from two paths. * * Returns nothing.
*/ void
mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc)
{ int iopoll_q_count =
ioc->reply_queue_count - ioc->iopoll_q_start_index; int qid;
for (qid = 0; qid < iopoll_q_count; qid++)
atomic_set(&ioc->io_uring_poll_queues[qid].pause, 1);
/* * wait for current poll to complete.
*/ for (qid = 0; qid < iopoll_q_count; qid++) { while (atomic_read(&ioc->io_uring_poll_queues[qid].busy)) {
cpu_relax();
udelay(500);
}
}
}
/** * mpt3sas_base_resume_mq_polling - Resume polling on mq poll queues. * @ioc: per adapter object * * Returns nothing.
*/ void
mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc)
{ int iopoll_q_count =
ioc->reply_queue_count - ioc->iopoll_q_start_index; int qid;
rpf->Words = cpu_to_le64(ULLONG_MAX);
reply_q->reply_post_host_index =
(reply_q->reply_post_host_index ==
(ioc->reply_post_queue_depth - 1)) ? 0 :
reply_q->reply_post_host_index + 1;
request_descript_type =
reply_q->reply_post_free[reply_q->reply_post_host_index]. Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
completed_cmds++; /* Update the reply post host index after continuously * processing the threshold number of Reply Descriptors. * So that FW can find enough entries to post the Reply * Descriptors in the reply descriptor post queue.
*/ if (completed_cmds >= ioc->thresh_hold) { if (ioc->combined_reply_queue) {
writel(reply_q->reply_post_host_index |
((msix_index & 7) <<
MPI2_RPHI_MSIX_INDEX_SHIFT),
ioc->replyPostRegisterIndex[msix_index/8]);
} else {
writel(reply_q->reply_post_host_index |
(msix_index <<
MPI2_RPHI_MSIX_INDEX_SHIFT),
&ioc->chip->ReplyPostHostIndex);
} if (!reply_q->is_iouring_poll_q &&
!reply_q->irq_poll_scheduled) {
reply_q->irq_poll_scheduled = true;
irq_poll_sched(&reply_q->irqpoll);
}
atomic_dec(&reply_q->busy); return completed_cmds;
} if (request_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) goto out; if (!reply_q->reply_post_host_index)
rpf = reply_q->reply_post_free; else
rpf++;
} while (1);
out:
if (!completed_cmds) {
atomic_dec(&reply_q->busy); return completed_cmds;
}
if (ioc->is_warpdrive) {
writel(reply_q->reply_post_host_index,
ioc->reply_post_host_index[msix_index]);
atomic_dec(&reply_q->busy); return completed_cmds;
}
/* Update Reply Post Host Index. * For those HBA's which support combined reply queue feature * 1. Get the correct Supplemental Reply Post Host Index Register. * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host * Index Register address bank i.e replyPostRegisterIndex[], * 2. Then update this register with new reply host index value * in ReplyPostIndex field and the MSIxIndex field with * msix_index value reduced to a value between 0 and 7, * using a modulo 8 operation. Since each Supplemental Reply Post * Host Index Register supports 8 MSI-X vectors. * * For other HBA's just update the Reply Post Host Index register with * new reply host index value in ReplyPostIndex Field and msix_index * value in MSIxIndex field.
*/ if (ioc->combined_reply_queue)
writel(reply_q->reply_post_host_index | ((msix_index & 7) <<
MPI2_RPHI_MSIX_INDEX_SHIFT),
ioc->replyPostRegisterIndex[msix_index/8]); else
writel(reply_q->reply_post_host_index | (msix_index <<
MPI2_RPHI_MSIX_INDEX_SHIFT),
&ioc->chip->ReplyPostHostIndex);
atomic_dec(&reply_q->busy); return completed_cmds;
}
/** * mpt3sas_blk_mq_poll - poll the blk mq poll queue * @shost: Scsi_Host object * @queue_num: hw ctx queue number * * Return number of entries that has been processed from poll queue.
*/ int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsignedint queue_num)
{ struct MPT3SAS_ADAPTER *ioc =
(struct MPT3SAS_ADAPTER *)shost->hostdata; struct adapter_reply_queue *reply_q; int num_entries = 0; int qid = queue_num - ioc->iopoll_q_start_index;
if (atomic_read(&ioc->io_uring_poll_queues[qid].pause) ||
!atomic_add_unless(&ioc->io_uring_poll_queues[qid].busy, 1, 1)) return 0;
if (ioc->mask_interrupts) return IRQ_NONE; if (reply_q->irq_poll_scheduled) return IRQ_HANDLED; return ((_base_process_reply_queue(reply_q) > 0) ?
IRQ_HANDLED : IRQ_NONE);
}
/** * _base_irqpoll - IRQ poll callback handler * @irqpoll: irq_poll object * @budget: irq poll weight * * Return: number of reply descriptors processed
*/ staticint
_base_irqpoll(struct irq_poll *irqpoll, int budget)
{ struct adapter_reply_queue *reply_q; int num_entries = 0;
reply_q = container_of(irqpoll, struct adapter_reply_queue,
irqpoll); if (reply_q->irq_line_enable) {
disable_irq_nosync(reply_q->os_irq);
reply_q->irq_line_enable = false;
}
num_entries = _base_process_reply_queue(reply_q); if (num_entries < budget) {
irq_poll_complete(irqpoll);
reply_q->irq_poll_scheduled = false;
reply_q->irq_line_enable = true;
enable_irq(reply_q->os_irq); /* * Go for one more round of processing the * reply descriptor post queue in case the HBA * Firmware has posted some reply descriptors * while reenabling the IRQ.
*/
_base_process_reply_queue(reply_q);
}
/** * _base_is_controller_msix_enabled - is controller support muli-reply queues * @ioc: per adapter object * * Return: Whether or not MSI/X is enabled.
*/ staticinlineint
_base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc)
{ return (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable;
}
/** * mpt3sas_base_sync_reply_irqs - flush pending MSIX interrupts * @ioc: per adapter object * @poll: poll over reply descriptor pools incase interrupt for * timed-out SCSI command got delayed * Context: non-ISR context * * Called when a Task Management request has completed.
*/ void
mpt3sas_base_sync_reply_irqs(struct MPT3SAS_ADAPTER *ioc, u8 poll)
{ struct adapter_reply_queue *reply_q;
/* If MSIX capability is turned off * then multi-queues are not enabled
*/ if (!_base_is_controller_msix_enabled(ioc)) return;
list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { if (ioc->shost_recovery || ioc->remove_host ||
ioc->pci_error_recovery) return; /* TMs are on msix_index == 0 */ if (reply_q->msix_index == 0) continue;
if (reply_q->is_iouring_poll_q) {
_base_process_reply_queue(reply_q); continue;
}
synchronize_irq(pci_irq_vector(ioc->pdev, reply_q->msix_index)); if (reply_q->irq_poll_scheduled) { /* Calling irq_poll_disable will wait for any pending * callbacks to have completed.
*/
irq_poll_disable(&reply_q->irqpoll);
irq_poll_enable(&reply_q->irqpoll); /* check how the scheduled poll has ended, * clean up only if necessary
*/ if (reply_q->irq_poll_scheduled) {
reply_q->irq_poll_scheduled = false;
reply_q->irq_line_enable = true;
enable_irq(reply_q->os_irq);
}
}
/** * mpt3sas_base_register_callback_handler - obtain index for the interrupt callback handler * @cb_func: callback function * * Return: Index of @cb_func.
*/
u8
mpt3sas_base_register_callback_handler(MPT_CALLBACK cb_func)
{
u8 cb_idx;
for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--) if (mpt_callbacks[cb_idx] == NULL) break;
for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++)
mpt3sas_base_release_callback_handler(cb_idx);
}
/** * _base_build_zero_len_sge - build zero length sg entry * @ioc: per adapter object * @paddr: virtual address for SGE * * Create a zero length scatter gather entry to insure the IOCs hardware has * something to use if the target device goes brain dead and tries * to send data even when none is asked for.
*/ staticvoid
_base_build_zero_len_sge(struct MPT3SAS_ADAPTER *ioc, void *paddr)
{
u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT |
MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST |
MPI2_SGE_FLAGS_SIMPLE_ELEMENT) <<
MPI2_SGE_FLAGS_SHIFT);
ioc->base_add_sg_single(paddr, flags_length, -1);
}
/** * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. * @paddr: virtual address for SGE * @flags_length: SGE flags and data transfer length * @dma_addr: Physical address
*/ staticvoid
_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr)
{
Mpi2SGESimple32_t *sgel = paddr;
/** * _base_build_nvme_prp - This function is called for NVMe end devices to build * a native SGL (NVMe PRP). * @ioc: per adapter object * @smid: system request message index for getting asscociated SGL * @nvme_encap_request: the NVMe request msg frame pointer * @data_out_dma: physical address for WRITES * @data_out_sz: data xfer size for WRITES * @data_in_dma: physical address for READS * @data_in_sz: data xfer size for READS * * The native SGL is built starting in the first PRP * entry of the NVMe message (PRP1). If the data buffer is small enough to be * described entirely using PRP1, then PRP2 is not used. If needed, PRP2 is * used to describe a larger data buffer. If the data buffer is too large to * describe using the two PRP entriess inside the NVMe message, then PRP1 * describes the first data memory segment, and PRP2 contains a pointer to a PRP * list located elsewhere in memory to describe the remaining data memory * segments. The PRP list will be contiguous. * * The native SGL for NVMe devices is a Physical Region Page (PRP). A PRP * consists of a list of PRP entries to describe a number of noncontigous * physical memory segments as a single memory buffer, just as a SGL does. Note * however, that this function is only used by the IOCTL call, so the memory * given will be guaranteed to be contiguous. There is no need to translate * non-contiguous SGL into a PRP in this case. All PRPs will describe * contiguous space that is one page size each. * * Each NVMe message contains two PRP entries. The first (PRP1) either contains * a PRP list pointer or a PRP element, depending upon the command. PRP2 * contains the second PRP element if the memory being described fits within 2 * PRP entries, or a PRP list pointer if the PRP spans more than two entries. * * A PRP list pointer contains the address of a PRP list, structured as a linear * array of PRP entries. Each PRP entry in this list describes a segment of * physical memory. * * Each 64-bit PRP entry comprises an address and an offset field. The address * always points at the beginning of a 4KB physical memory page, and the offset * describes where within that 4KB page the memory segment begins. Only the * first element in a PRP list may contain a non-zero offset, implying that all * memory segments following the first begin at the start of a 4KB page. * * Each PRP element normally describes 4KB of physical memory, with exceptions * for the first and last elements in the list. If the memory being described * by the list begins at a non-zero offset within the first 4KB page, then the * first PRP element will contain a non-zero offset indicating where the region * begins within the 4KB page. The last memory segment may end before the end * of the 4KB segment, depending upon the overall size of the memory being * described by the PRP list. * * Since PRP entries lack any indication of size, the overall data buffer length * is used to determine where the end of the data memory buffer is located, and * how many PRP entries are required to describe it.
*/ staticvoid
_base_build_nvme_prp(struct MPT3SAS_ADAPTER *ioc, u16 smid,
Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request,
dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
size_t data_in_sz)
{ int prp_size = NVME_PRP_SIZE;
__le64 *prp_entry, *prp1_entry, *prp2_entry;
__le64 *prp_page;
dma_addr_t prp_entry_dma, prp_page_dma, dma_addr;
u32 offset, entry_len;
u32 page_mask_result, page_mask;
size_t length; struct mpt3sas_nvme_cmd *nvme_cmd =
(void *)nvme_encap_request->NVMe_Command;
/* * Not all commands require a data transfer. If no data, just return * without constructing any PRP.
*/ if (!data_in_sz && !data_out_sz) return;
prp1_entry = &nvme_cmd->prp1;
prp2_entry = &nvme_cmd->prp2;
prp_entry = prp1_entry; /* * For the PRP entries, use the specially allocated buffer of * contiguous memory.
*/
prp_page = (__le64 *)mpt3sas_base_get_pcie_sgl(ioc, smid);
prp_page_dma = mpt3sas_base_get_pcie_sgl_dma(ioc, smid);
/* * Check if we are within 1 entry of a page boundary we don't * want our first entry to be a PRP List entry.
*/
page_mask = ioc->page_size - 1;
page_mask_result = (uintptr_t)((u8 *)prp_page + prp_size) & page_mask; if (!page_mask_result) { /* Bump up to next page boundary. */
prp_page = (__le64 *)((u8 *)prp_page + prp_size);
prp_page_dma = prp_page_dma + prp_size;
}
/* * Set PRP physical pointer, which initially points to the current PRP * DMA memory page.
*/
prp_entry_dma = prp_page_dma;
/* Get physical address and length of the data buffer. */ if (data_in_sz) {
dma_addr = data_in_dma;
length = data_in_sz;
} else {
dma_addr = data_out_dma;
length = data_out_sz;
}
/* Loop while the length is not zero. */ while (length) { /* * Check if we need to put a list pointer here if we are at * page boundary - prp_size (8 bytes).
*/
page_mask_result = (prp_entry_dma + prp_size) & page_mask; if (!page_mask_result) { /* * This is the last entry in a PRP List, so we need to * put a PRP list pointer here. What this does is: * - bump the current memory pointer to the next * address, which will be the next full page. * - set the PRP Entry to point to that page. This * is now the PRP List pointer. * - bump the PRP Entry pointer the start of the * next page. Since all of this PRP memory is * contiguous, no need to get a new page - it's * just the next address.
*/
prp_entry_dma++;
*prp_entry = cpu_to_le64(prp_entry_dma);
prp_entry++;
}
/* Need to handle if entry will be part of a page. */
offset = dma_addr & page_mask;
entry_len = ioc->page_size - offset;
if (prp_entry == prp1_entry) { /* * Must fill in the first PRP pointer (PRP1) before * moving on.
*/
*prp1_entry = cpu_to_le64(dma_addr);
/* * Now point to the second PRP entry within the * command (PRP2).
*/
prp_entry = prp2_entry;
} elseif (prp_entry == prp2_entry) { /* * Should the PRP2 entry be a PRP List pointer or just * a regular PRP pointer? If there is more than one * more page of data, must use a PRP List pointer.
*/ if (length > ioc->page_size) { /* * PRP2 will contain a PRP List pointer because * more PRP's are needed with this command. The * list will start at the beginning of the * contiguous buffer.
*/
*prp2_entry = cpu_to_le64(prp_entry_dma);
/* * The next PRP Entry will be the start of the * first PRP List.
*/
prp_entry = prp_page;
} else { /* * After this, the PRP Entries are complete. * This command uses 2 PRP's and no PRP list.
*/
*prp2_entry = cpu_to_le64(dma_addr);
}
} else { /* * Put entry in list and bump the addresses. * * After PRP1 and PRP2 are filled in, this will fill in * all remaining PRP entries in a PRP List, one per * each time through the loop.
*/
*prp_entry = cpu_to_le64(dma_addr);
prp_entry++;
prp_entry_dma++;
}
/* * Bump the phys address of the command's data buffer by the * entry_len.
*/
dma_addr += entry_len;
/* Decrement length accounting for last partial page. */ if (entry_len > length)
length = 0; else
length -= entry_len;
}
}
/** * base_make_prp_nvme - Prepare PRPs (Physical Region Page) - * SGLs specific to NVMe drives only * * @ioc: per adapter object * @scmd: SCSI command from the mid-layer * @mpi_request: mpi request * @smid: msg Index * @sge_count: scatter gather element count. * * Return: true: PRPs are built * false: IEEE SGLs needs to be built
*/ staticvoid
base_make_prp_nvme(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
Mpi25SCSIIORequest_t *mpi_request,
u16 smid, int sge_count)
{ int sge_len, num_prp_in_chain = 0;
Mpi25IeeeSgeChain64_t *main_chain_element, *ptr_first_sgl;
__le64 *curr_buff;
dma_addr_t msg_dma, sge_addr, offset;
u32 page_mask, page_mask_result; struct scatterlist *sg_scmd;
u32 first_prp_len; int data_len = scsi_bufflen(scmd);
u32 nvme_pg_size;
nvme_pg_size = max_t(u32, ioc->page_size, NVME_PRP_PAGE_SIZE); /* * 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 = nvme_pg_size - 1;
/* * Native SGL is needed. * Put a chain element in main message frame that points to the first * chain buffer. * * NOTE: The ChainOffset field must be 0 when using a chain pointer to * a native SGL.
*/
/* Set main message chain element pointer */
main_chain_element = (pMpi25IeeeSgeChain64_t)&mpi_request->SGL; /* * For NVMe the chain element needs to be the 2nd SG entry in the main * message.
*/
main_chain_element = (Mpi25IeeeSgeChain64_t *)
((u8 *)main_chain_element + sizeof(MPI25_IEEE_SGE_CHAIN64));
/* * For the PRP entries, use the specially allocated buffer of * contiguous memory. Normal chain buffers can't be used * because each chain buffer would need to be the size of an OS * page (4k).
*/
curr_buff = mpt3sas_base_get_pcie_sgl(ioc, smid);
msg_dma = mpt3sas_base_get_pcie_sgl_dma(ioc, smid);
/* Build first prp, sge need not to be page aligned*/
ptr_first_sgl = (pMpi25IeeeSgeChain64_t)&mpi_request->SGL;
sg_scmd = scsi_sglist(scmd);
sge_addr = sg_dma_address(sg_scmd);
sge_len = sg_dma_len(sg_scmd);
/* If Datalenth is <= 16K and number of SGE’s entries are <= 2 * we built IEEE SGL
*/ if ((data_length <= NVME_PRP_PAGE_SIZE*4) && (sge_count <= 2))
build_prp = false;
return build_prp;
}
/** * _base_check_pcie_native_sgl - This function is called for PCIe end devices to * determine if the driver needs to build a native SGL. If so, that native * SGL is built in the special contiguous buffers allocated especially for * PCIe SGL creation. If the driver will not build a native SGL, return * TRUE and a normal IEEE SGL will be built. Currently this routine * supports NVMe. * @ioc: per adapter object * @mpi_request: mf request pointer * @smid: system request message index * @scmd: scsi command * @pcie_device: points to the PCIe device's info * * Return: 0 if native SGL was built, 1 if no SGL was built
*/ staticint
_base_check_pcie_native_sgl(struct MPT3SAS_ADAPTER *ioc,
Mpi25SCSIIORequest_t *mpi_request, u16 smid, struct scsi_cmnd *scmd, struct _pcie_device *pcie_device)
{ int sges_left;
/* Get the SG list pointer and info. */
sges_left = scsi_dma_map(scmd); if (sges_left < 0) return 1;
/* Check if we need to build a native SG list. */ if (!base_is_prp_possible(ioc, pcie_device,
scmd, sges_left)) { /* We built a native SG list, just return. */ goto out;
}
/** * _base_build_zero_len_sge_ieee - build zero length sg entry for IEEE format * @ioc: per adapter object * @paddr: virtual address for SGE * * Create a zero length scatter gather entry to insure the IOCs hardware has * something to use if the target device goes brain dead and tries * to send data even when none is asked for.
*/ staticvoid
_base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr)
{
u8 sgl_flags = (MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR |
MPI25_IEEE_SGE_FLAGS_END_OF_LIST);
staticinlineint _base_scsi_dma_map(struct scsi_cmnd *cmd)
{ /* * Some firmware versions byte-swap the REPORT ZONES command reply from * ATA-ZAC devices by directly accessing in the host buffer. This does * not respect the default command DMA direction and causes IOMMU page * faults on some architectures with an IOMMU enforcing write mappings * (e.g. AMD hosts). Avoid such issue by making the report zones buffer * mapping bi-directional.
*/ if (cmd->cmnd[0] == ZBC_IN && cmd->cmnd[1] == ZI_REPORT_ZONES)
cmd->sc_data_direction = DMA_BIDIRECTIONAL;
return scsi_dma_map(cmd);
}
/** * _base_build_sg_scmd - main sg creation routine * pcie_device is unused here! * @ioc: per adapter object * @scmd: scsi command * @smid: system request message index * @unused: unused pcie_device pointer * Context: none. * * The main routine that builds scatter gather table from a given * scsi request sent via the .queuecommand main handler. * * Return: 0 success, anything else error
*/ staticint
_base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, u16 smid, struct _pcie_device *unused)
{
Mpi2SCSIIORequest_t *mpi_request;
dma_addr_t chain_dma; struct scatterlist *sg_scmd; void *sg_local, *chain;
u32 chain_offset;
u32 chain_length;
u32 chain_flags; int sges_left;
u32 sges_in_segment;
u32 sgl_flags;
u32 sgl_flags_last_element;
u32 sgl_flags_end_buffer; struct chain_tracker *chain_req;
/* fill in main message segment when there is a chain following */ while (sges_in_segment) { if (sges_in_segment == 1)
ioc->base_add_sg_single(sg_local,
sgl_flags_last_element | sg_dma_len(sg_scmd),
sg_dma_address(sg_scmd)); else
ioc->base_add_sg_single(sg_local, sgl_flags |
sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
sg_scmd = sg_next(sg_scmd);
sg_local += ioc->sge_size;
sges_left--;
sges_in_segment--;
}
/* Check if we need to build a native SG list. */ if ((pcie_device) && (_base_check_pcie_native_sgl(ioc, mpi_request,
smid, scmd, pcie_device) == 0)) { /* We built a native SG list, just return. */ return 0;
}
/* fill in main message segment when there is a chain following */ while (sges_in_segment > 1) {
_base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
sg_scmd = sg_next(sg_scmd);
sg_local += ioc->sge_size_ieee;
sges_left--;
sges_in_segment--;
}
si_meminfo(&s);
ioc_info(ioc, "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n",
ioc->dma_mask, convert_to_kb(s.totalram));
return 0;
}
/** * _base_check_enable_msix - checks MSIX capabable. * @ioc: per adapter object * * Check to see if card is capable of MSIX, and set number * of available msix vectors
*/ staticint
_base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc)
{ int base;
u16 message_control;
/* Check whether controller SAS2008 B0 controller, * if it is SAS2008 B0 controller use IO-APIC instead of MSIX
*/ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
ioc->pdev->revision == SAS2_PCI_DEVICE_B0_REVISION) { return -EINVAL;
}
base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); if (!base) {
dfailprintk(ioc, ioc_info(ioc, "msix not supported\n")); return -EINVAL;
}
/** * _base_assign_reply_queues - assigning msix index for each cpu * @ioc: per adapter object * * The enduser would need to set the affinity via /proc/irq/#/smp_affinity
*/ staticvoid
_base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)
{ unsignedint cpu, nr_cpus, nr_msix, index = 0, irq; struct adapter_reply_queue *reply_q; int iopoll_q_count = ioc->reply_queue_count -
ioc->iopoll_q_start_index; conststruct cpumask *mask;
if (!_base_is_controller_msix_enabled(ioc)) return;
/* * set irq affinity to local numa node for those irqs * corresponding to high iops queues.
*/ if (ioc->high_iops_queues) {
mask = cpumask_of_node(dev_to_node(&ioc->pdev->dev)); for (index = 0; index < ioc->high_iops_queues;
index++) {
irq = pci_irq_vector(ioc->pdev, index);
irq_set_affinity_and_hint(irq, mask);
}
}
fall_back:
cpu = cpumask_first(cpu_online_mask);
nr_msix -= (ioc->high_iops_queues - iopoll_q_count);
index = 0;
list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { unsignedint i, group = nr_cpus / nr_msix;
if (reply_q->msix_index < ioc->high_iops_queues ||
reply_q->msix_index >= ioc->iopoll_q_start_index) continue;
if (cpu >= nr_cpus) break;
if (index < nr_cpus % nr_msix)
group++;
for (i = 0 ; i < group ; i++) {
ioc->cpu_msix_table[cpu] = reply_q->msix_index;
cpu = cpumask_next(cpu, cpu_online_mask);
}
index++;
}
}
/** * _base_check_and_enable_high_iops_queues - enable high iops mode * @ioc: per adapter object * @hba_msix_vector_count: msix vectors supported by HBA * * Enable high iops queues only if * - HBA is a SEA/AERO controller and * - MSI-Xs vector supported by the HBA is 128 and * - total CPU count in the system >=16 and * - loaded driver with default max_msix_vectors module parameter and * - system booted in non kdump mode * * Return: nothing.
*/ staticvoid
_base_check_and_enable_high_iops_queues(struct MPT3SAS_ADAPTER *ioc, int hba_msix_vector_count)
{
u16 lnksta, speed;
/* * Disable high iops queues if io uring poll queues are enabled.
*/ if (perf_mode == MPT_PERF_MODE_IOPS ||
perf_mode == MPT_PERF_MODE_LATENCY ||
ioc->io_uring_poll_queues) {
ioc->high_iops_queues = 0; return;
}
/* * Enable msix_load_balance only if combined reply queue mode is * disabled on SAS3 & above generation HBA devices.
*/ if (!ioc->combined_reply_queue &&
ioc->hba_mpi_version_belonged != MPI2_VERSION) {
ioc_info(ioc, "combined ReplyQueue is off, Enabling msix load balance\n");
ioc->msix_load_balance = true;
}
/* * smp affinity setting is not need when msix load balance * is enabled.
*/ if (ioc->msix_load_balance)
ioc->smp_affinity_enable = 0;
if (!ioc->smp_affinity_enable || ioc->reply_queue_count <= 1)
ioc->shost->host_tagset = 0;
/* * Enable io uring poll queues only if host_tagset is enabled.
*/ if (ioc->shost->host_tagset)
iopoll_q_count = poll_queues;
if (iopoll_q_count) {
ioc->io_uring_poll_queues = kcalloc(iopoll_q_count, sizeof(struct io_uring_poll_queue), GFP_KERNEL); if (!ioc->io_uring_poll_queues)
iopoll_q_count = 0;
}
if (ioc->is_aero_ioc)
_base_check_and_enable_high_iops_queues(ioc,
ioc->msix_vector_count);
/* * Add high iops queues count to reply queue count if high iops queues * are enabled.
*/
ioc->reply_queue_count = min_t(int,
ioc->reply_queue_count + ioc->high_iops_queues,
ioc->msix_vector_count);
/* * Adjust the reply queue count incase reply queue count * exceeds the user provided MSIx vectors count.
*/ if (local_max_msix_vectors > 0)
ioc->reply_queue_count = min_t(int, local_max_msix_vectors,
ioc->reply_queue_count); /* * Add io uring poll queues count to reply queues count * if io uring is enabled in driver.
*/ if (iopoll_q_count) { if (ioc->reply_queue_count < (iopoll_q_count + MPT3_MIN_IRQS))
iopoll_q_count = 0;
ioc->reply_queue_count = min_t(int,
ioc->reply_queue_count + iopoll_q_count,
ioc->msix_vector_count);
}
/* * Starting index of io uring poll queues in reply queue list.
*/
ioc->iopoll_q_start_index =
ioc->reply_queue_count - iopoll_q_count;
r = _base_alloc_irq_vectors(ioc); if (r < 0) {
ioc_info(ioc, "pci_alloc_irq_vectors failed (r=%d) !!!\n", r); goto try_ioapic;
}
/* * Adjust the reply queue count if the allocated * MSIx vectors is less then the requested number * of MSIx vectors.
*/ if (r < ioc->iopoll_q_start_index) {
ioc->reply_queue_count = r + iopoll_q_count;
ioc->iopoll_q_start_index =
ioc->reply_queue_count - iopoll_q_count;
}
ioc->msix_enable = 1; for (i = 0; i < ioc->reply_queue_count; i++) {
r = _base_request_irq(ioc, i); if (r) {
mpt3sas_base_free_irq(ioc);
mpt3sas_base_disable_msix(ioc); goto try_ioapic;
}
}
/** * mpt3sas_base_check_for_fault_and_issue_reset - check if IOC is in fault state * and if it is in fault state then issue diag reset. * @ioc: per adapter object * * Return: 0 for success, non-zero for failure.
*/ int
mpt3sas_base_check_for_fault_and_issue_reset(struct MPT3SAS_ADAPTER *ioc)
{
u32 ioc_state; int rc = -EFAULT;
if (ioc->chip == NULL) {
ioc_err(ioc, "unable to map adapter memory! or resource not found\n");
r = -EINVAL; goto out_fail;
}
mpt3sas_base_mask_interrupts(ioc);
r = _base_get_ioc_facts(ioc); if (r) {
rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); if (rc || (_base_get_ioc_facts(ioc))) goto out_fail;
}
if (!ioc->rdpq_array_enable_assigned) {
ioc->rdpq_array_enable = ioc->rdpq_array_capable;
ioc->rdpq_array_enable_assigned = 1;
}
r = _base_enable_msix(ioc); if (r) goto out_fail;
iopoll_q_count = ioc->reply_queue_count - ioc->iopoll_q_start_index; for (i = 0; i < iopoll_q_count; i++) {
atomic_set(&ioc->io_uring_poll_queues[i].busy, 0);
atomic_set(&ioc->io_uring_poll_queues[i].pause, 0);
}
if (!ioc->is_driver_loading)
_base_init_irqpolls(ioc); /* Use the Combined reply queue feature only for SAS3 C0 & higher * revision HBAs and also only when reply queue count is greater than 8
*/ if (ioc->combined_reply_queue) { /* Determine the Supplemental Reply Post Host Index Registers * Addresse. Supplemental Reply Post Host Index Registers * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and * each register is at offset bytes of * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one.
*/
ioc->replyPostRegisterIndex = kcalloc(
ioc->combined_reply_index_count, sizeof(resource_size_t *), GFP_KERNEL); if (!ioc->replyPostRegisterIndex) {
ioc_err(ioc, "allocation for replyPostRegisterIndex failed!\n");
r = -ENOMEM; goto out_fail;
}
for (i = 0; i < ioc->combined_reply_index_count; i++) {
ioc->replyPostRegisterIndex[i] =
(resource_size_t __iomem *)
((u8 __force *)&ioc->chip->Doorbell +
MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET +
(i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET));
}
}
if (ioc->is_warpdrive) {
ioc->reply_post_host_index[0] = (resource_size_t __iomem *)
&ioc->chip->ReplyPostHostIndex;
for (i = 1; i < ioc->cpu_msix_table_sz; i++)
ioc->reply_post_host_index[i] =
(resource_size_t __iomem *)
((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1)
* 4)));
}
/** * mpt3sas_base_get_msg_frame - obtain request mf pointer * @ioc: per adapter object * @smid: system request message index(smid zero is invalid) * * Return: virt pointer to message frame.
*/ void *
mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ return (void *)(ioc->request + (smid * ioc->request_sz));
}
/** * mpt3sas_base_get_sense_buffer - obtain a sense buffer virt addr * @ioc: per adapter object * @smid: system request message index * * Return: virt pointer to sense buffer.
*/ void *
mpt3sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
}
/** * mpt3sas_base_get_sense_buffer_dma - obtain a sense buffer dma addr * @ioc: per adapter object * @smid: system request message index * * Return: phys pointer to the low 32bit address of the sense buffer.
*/
__le32
mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ return cpu_to_le32(ioc->sense_dma + ((smid - 1) *
SCSI_SENSE_BUFFERSIZE));
}
/** * mpt3sas_base_get_pcie_sgl - obtain a PCIe SGL virt addr * @ioc: per adapter object * @smid: system request message index * * Return: virt pointer to a PCIe SGL.
*/ void *
mpt3sas_base_get_pcie_sgl(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ return (void *)(ioc->pcie_sg_lookup[smid - 1].pcie_sgl);
}
/** * mpt3sas_base_get_pcie_sgl_dma - obtain a PCIe SGL dma addr * @ioc: per adapter object * @smid: system request message index * * Return: phys pointer to the address of the PCIe buffer.
*/
dma_addr_t
mpt3sas_base_get_pcie_sgl_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ return ioc->pcie_sg_lookup[smid - 1].pcie_sgl_dma;
}
/** * mpt3sas_base_get_reply_virt_addr - obtain reply frames virt address * @ioc: per adapter object * @phys_addr: lower 32 physical addr of the reply * * Converts 32bit lower physical addr into a virt address.
*/ void *
mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr)
{ if (!phys_addr) return NULL; return ioc->reply + (phys_addr - (u32)ioc->reply_dma);
}
/** * _base_get_msix_index - get the msix index * @ioc: per adapter object * @scmd: scsi_cmnd object * * Return: msix index of general reply queues, * i.e. reply queue on which IO request's reply * should be posted by the HBA firmware.
*/ staticinline u8
_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
{ /* Enables reply_queue load balancing */ if (ioc->msix_load_balance) return ioc->reply_queue_count ?
base_mod64(atomic64_add_return(1,
&ioc->total_io_cnt), ioc->reply_queue_count) : 0;
if (scmd && ioc->shost->nr_hw_queues > 1) {
u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
/** * _base_get_high_iops_msix_index - get the msix index of * high iops queues * @ioc: per adapter object * @scmd: scsi_cmnd object * * Return: msix index of high iops reply queues. * i.e. high iops reply queue on which IO request's * reply should be posted by the HBA firmware.
*/ staticinline u8
_base_get_high_iops_msix_index(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
{ /** * Round robin the IO interrupts among the high iops * reply queues in terms of batch count 16 when outstanding * IOs on the target device is >=8.
*/
/** * mpt3sas_base_get_smid_scsiio - obtain a free smid from scsiio queue * @ioc: per adapter object * @cb_idx: callback index * @scmd: pointer to scsi command object * * Return: smid (zero is invalid)
*/
u16
mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx, struct scsi_cmnd *scmd)
{ struct scsiio_tracker *request = scsi_cmd_priv(scmd);
u16 smid;
u32 tag, unique_tag;
unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
tag = blk_mq_unique_tag_to_tag(unique_tag);
/* * Store hw queue number corresponding to the tag. * This hw queue number is used later to determine * the unique_tag using the logic below. This unique_tag * is used to retrieve the scmd pointer corresponding * to tag using scsi_host_find_tag() API. * * tag = smid - 1; * unique_tag = ioc->io_queue_num[tag] << BLK_MQ_UNIQUE_TAG_BITS | tag;
*/
ioc->io_queue_num[tag] = blk_mq_unique_tag_to_hwq(unique_tag);
/** * mpt3sas_base_free_smid - put smid back on free_list * @ioc: per adapter object * @smid: system request message index
*/ void
mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ unsignedlong flags; int i;
if (smid < ioc->hi_priority_smid) { struct scsiio_tracker *st; void *request;
st = _get_st_from_smid(ioc, smid); if (!st) {
_base_recovery_check(ioc); return;
}
/** * _base_mpi_ep_writeq - 32 bit write to MMIO * @b: data payload * @addr: address in MMIO space * @writeq_lock: spin lock * * This special handling for MPI EP to take care of 32 bit * environment where its not quarenteed to send the entire word * in one transfer.
*/ staticinlinevoid
_base_mpi_ep_writeq(__u64 b, volatilevoid __iomem *addr,
spinlock_t *writeq_lock)
{ unsignedlong flags;
/** * _base_writeq - 64 bit write to MMIO * @b: data payload * @addr: address in MMIO space * @writeq_lock: spin lock * * Glue for handling an atomic 64 bit word to MMIO. This special handling takes * care of 32 bit environment where its not quarenteed to send the entire word * in one transfer.
*/ #ifdefined(writeq) && defined(CONFIG_64BIT) staticinlinevoid
_base_writeq(__u64 b, volatilevoid __iomem *addr, spinlock_t *writeq_lock)
{
wmb();
__raw_writeq(b, addr);
barrier();
} #else staticinlinevoid
_base_writeq(__u64 b, volatilevoid __iomem *addr, spinlock_t *writeq_lock)
{
_base_mpi_ep_writeq(b, addr, writeq_lock);
} #endif
/** * _base_set_and_get_msix_index - get the msix index and assign to msix_io * variable of scsi tracker * @ioc: per adapter object * @smid: system request message index * * Return: msix index.
*/ static u8
_base_set_and_get_msix_index(struct MPT3SAS_ADAPTER *ioc, u16 smid)
{ struct scsiio_tracker *st = NULL;
if (smid < ioc->hi_priority_smid)
st = _get_st_from_smid(ioc, smid);
if (st == NULL) return _base_get_msix_index(ioc, NULL);
/** * _base_put_smid_hi_priority - send Task Management request to firmware * @ioc: per adapter object * @smid: system request message index * @msix_task: msix_task will be same as msix of IO in case of task abort else 0
*/ staticvoid
_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u16 msix_task)
{
Mpi2RequestDescriptorUnion_t descriptor; void *mpi_req_iomem;
u64 *request;
if (ioc->is_mcpu_endpoint) {
__le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid);
/** * _base_put_smid_fast_path_atomic - send fast path request to firmware * using Atomic Request Descriptor * @ioc: per adapter object * @smid: system request message index * @handle: device handle, unused in this function, for function type match * Return: nothing
*/ staticvoid
_base_put_smid_fast_path_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u16 handle)
{
Mpi26AtomicRequestDescriptor_t descriptor;
u32 *request = (u32 *)&descriptor;
/** * _base_put_smid_hi_priority_atomic - send Task Management request to * firmware using Atomic Request Descriptor * @ioc: per adapter object * @smid: system request message index * @msix_task: msix_task will be same as msix of IO in case of task abort else 0 * * Return: nothing.
*/ staticvoid
_base_put_smid_hi_priority_atomic(struct MPT3SAS_ADAPTER *ioc, u16 smid,
u16 msix_task)
{
Mpi26AtomicRequestDescriptor_t descriptor;
u32 *request = (u32 *)&descriptor;
if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES) {
pr_info("%sNVMe", i ? "," : "");
i++;
}
ioc_info(ioc, "Protocol=(");
if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
pr_cont("Initiator");
i++;
}
if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) {
pr_cont("%sTarget", i ? "," : "");
i++;
}
i = 0;
pr_cont("), Capabilities=(");
if (!ioc->hide_ir_msg) { if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
pr_cont("Raid");
i++;
}
}
if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
pr_cont("%sTLR", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) {
pr_cont("%sMulticast", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) {
pr_cont("%sBIDI Target", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) {
pr_cont("%sEEDP", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
pr_cont("%sSnapshot Buffer", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
pr_cont("%sDiag Trace Buffer", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
pr_cont("%sDiag Extended Buffer", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
pr_cont("%sTask Set Full", i ? "," : "");
i++;
}
if (ioc->facts.IOCCapabilities &
MPI26_IOCFACTS_CAPABILITY_MCTP_PASSTHRU) {
pr_cont("%sMCTP Passthru", i ? "," : "");
i++;
}
iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) {
pr_cont("%sNCQ", i ? "," : "");
i++;
}
pr_cont(")\n");
}
/** * mpt3sas_base_update_missing_delay - change the missing delay timers * @ioc: per adapter object * @device_missing_delay: amount of time till device is reported missing * @io_missing_delay: interval IO is returned when there is a missing device * * Passed on the command line, this function will modify the device missing * delay, as well as the io missing delay. This should be called at driver * load time.
*/ void
mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc,
u16 device_missing_delay, u8 io_missing_delay)
{
u16 dmd, dmd_new, dmd_orignal;
u8 io_missing_delay_original;
u16 sz;
Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
Mpi2ConfigReply_t mpi_reply;
u8 num_phys = 0;
u16 ioc_status;
mpt3sas_config_get_number_hba_phys(ioc, &num_phys); if (!num_phys) return;
sz = struct_size(sas_iounit_pg1, PhyData, num_phys);
sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); if (!sas_iounit_pg1) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); goto out;
} if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
sas_iounit_pg1, sz))) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); goto out;
}
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
ioc_err(ioc, "failure at %s:%d/%s()!\n",
__FILE__, __LINE__, __func__); goto out;
}
switch (perf_mode) { case MPT_PERF_MODE_DEFAULT: case MPT_PERF_MODE_BALANCED: if (ioc->high_iops_queues) {
ioc_info(ioc, "Enable interrupt coalescing only for first\t" "%d reply queues\n",
MPT3SAS_HIGH_IOPS_REPLY_QUEUES); /* * If 31st bit is zero then interrupt coalescing is * enabled for all reply descriptor post queues. * If 31st bit is set to one then user can * enable/disable interrupt coalescing on per reply * descriptor post queue group(8) basis. So to enable * interrupt coalescing only on first reply descriptor * post queue group 31st bit and zero th bit is enabled.
*/
ioc_pg1.ProductSpecific = cpu_to_le32(0x80000000 |
((1 << MPT3SAS_HIGH_IOPS_REPLY_QUEUES/8) - 1));
rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); if (rc) return rc;
ioc_info(ioc, "performance mode: balanced\n"); return 0;
}
fallthrough; case MPT_PERF_MODE_LATENCY: /* * Enable interrupt coalescing on all reply queues * with timeout value 0xA
*/
ioc_pg1.CoalescingTimeout = cpu_to_le32(0xa);
ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING);
ioc_pg1.ProductSpecific = 0;
rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); if (rc) return rc;
ioc_info(ioc, "performance mode: latency\n"); break; case MPT_PERF_MODE_IOPS: /* * Enable interrupt coalescing on all reply queues.
*/
ioc_info(ioc, "performance mode: iops with coalescing timeout: 0x%x\n",
le32_to_cpu(ioc_pg1.CoalescingTimeout));
ioc_pg1.Flags |= cpu_to_le32(MPI2_IOCPAGE1_REPLY_COALESCING);
ioc_pg1.ProductSpecific = 0;
rc = mpt3sas_config_set_ioc_pg1(ioc, &mpi_reply, &ioc_pg1); if (rc) return rc; break;
} return 0;
}
/** * _base_get_event_diag_triggers - get event diag trigger values from * persistent pages * @ioc : per adapter object * * Return: nothing.
*/ staticint
_base_get_event_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
{
Mpi26DriverTriggerPage2_t trigger_pg2; struct SL_WH_EVENT_TRIGGER_T *event_tg;
MPI26_DRIVER_MPI_EVENT_TRIGGER_ENTRY *mpi_event_tg;
Mpi2ConfigReply_t mpi_reply; int r = 0, i = 0;
u16 count = 0;
u16 ioc_status;
r = mpt3sas_config_get_driver_trigger_pg2(ioc, &mpi_reply,
&trigger_pg2); if (r) return r;
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
dinitprintk(ioc,
ioc_err(ioc, "%s: Failed to get trigger pg2, ioc_status(0x%04x)\n",
__func__, ioc_status)); return 0;
}
for (i = 0; i < count; i++) {
status_tg->IOCStatus = le16_to_cpu(
mpi_status_tg->IOCStatus);
status_tg->IocLogInfo = le32_to_cpu(
mpi_status_tg->LogInfo);
status_tg++;
mpi_status_tg++;
}
} return 0;
}
/** * _base_get_master_diag_triggers - get master diag trigger values from * persistent pages * @ioc : per adapter object * * Return: nothing.
*/ staticint
_base_get_master_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
{
Mpi26DriverTriggerPage1_t trigger_pg1;
Mpi2ConfigReply_t mpi_reply; int r;
u16 ioc_status;
r = mpt3sas_config_get_driver_trigger_pg1(ioc, &mpi_reply,
&trigger_pg1); if (r) return r;
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
dinitprintk(ioc,
ioc_err(ioc, "%s: Failed to get trigger pg1, ioc_status(0x%04x)\n",
__func__, ioc_status)); return 0;
}
if (le16_to_cpu(trigger_pg1.NumMasterTrigger))
ioc->diag_trigger_master.MasterData |=
le32_to_cpu(
trigger_pg1.MasterTriggers[0].MasterTriggerFlags); return 0;
}
/** * _base_check_for_trigger_pages_support - checks whether HBA FW supports * driver trigger pages or not * @ioc : per adapter object * @trigger_flags : address where trigger page0's TriggerFlags value is copied * * Return: trigger flags mask if HBA FW supports driver trigger pages; * otherwise returns %-EFAULT if driver trigger pages are not supported by FW or * return EAGAIN if diag reset occurred due to FW fault and asking the * caller to retry the command. *
*/ staticint
_base_check_for_trigger_pages_support(struct MPT3SAS_ADAPTER *ioc, u32 *trigger_flags)
{
Mpi26DriverTriggerPage0_t trigger_pg0; int r = 0;
Mpi2ConfigReply_t mpi_reply;
u16 ioc_status;
r = mpt3sas_config_get_driver_trigger_pg0(ioc, &mpi_reply,
&trigger_pg0); if (r) return r;
r = _base_check_for_trigger_pages_support(ioc, &trigger_flags); if (r) { if (r == -EAGAIN) return r; /* * Don't go for error handling when FW doesn't support * driver trigger pages.
*/ return 0;
}
ioc->supports_trigger_pages = 1;
/* * Retrieve master diag trigger values from driver trigger pg1 * if master trigger bit enabled in TriggerFlags.
*/ if ((u16)trigger_flags &
MPI26_DRIVER_TRIGGER0_FLAG_MASTER_TRIGGER_VALID) {
r = _base_get_master_diag_triggers(ioc); if (r) return r;
}
/* * Retrieve event diag trigger values from driver trigger pg2 * if event trigger bit enabled in TriggerFlags.
*/ if ((u16)trigger_flags &
MPI26_DRIVER_TRIGGER0_FLAG_MPI_EVENT_TRIGGER_VALID) {
r = _base_get_event_diag_triggers(ioc); if (r) return r;
}
/* * Retrieve scsi diag trigger values from driver trigger pg3 * if scsi trigger bit enabled in TriggerFlags.
*/ if ((u16)trigger_flags &
MPI26_DRIVER_TRIGGER0_FLAG_SCSI_SENSE_TRIGGER_VALID) {
r = _base_get_scsi_diag_triggers(ioc); if (r) return r;
} /* * Retrieve mpi error diag trigger values from driver trigger pg4 * if loginfo trigger bit enabled in TriggerFlags.
*/ if ((u16)trigger_flags &
MPI26_DRIVER_TRIGGER0_FLAG_LOGINFO_TRIGGER_VALID) {
r = _base_get_mpi_diag_triggers(ioc); if (r) return r;
} return 0;
}
/** * _base_update_diag_trigger_pages - Update the driver trigger pages after * online FW update, in case updated FW supports driver * trigger pages. * @ioc : per adapter object * * Return: nothing.
*/ staticvoid
_base_update_diag_trigger_pages(struct MPT3SAS_ADAPTER *ioc)
{
if (ioc->diag_trigger_master.MasterData)
mpt3sas_config_update_driver_trigger_pg1(ioc,
&ioc->diag_trigger_master, 1);
if (ioc->diag_trigger_event.ValidEntries)
mpt3sas_config_update_driver_trigger_pg2(ioc,
&ioc->diag_trigger_event, 1);
if (ioc->diag_trigger_scsi.ValidEntries)
mpt3sas_config_update_driver_trigger_pg3(ioc,
&ioc->diag_trigger_scsi, 1);
if (ioc->diag_trigger_mpi.ValidEntries)
mpt3sas_config_update_driver_trigger_pg4(ioc,
&ioc->diag_trigger_mpi, 1);
}
/** * _base_assign_fw_reported_qd - Get FW reported QD for SAS/SATA devices. * - On failure set default QD values. * @ioc : per adapter object * * Returns 0 for success, non-zero for failure. *
*/ staticint _base_assign_fw_reported_qd(struct MPT3SAS_ADAPTER *ioc)
{
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage1_t sas_iounit_pg1;
Mpi26PCIeIOUnitPage1_t pcie_iounit_pg1;
u16 depth; int rc = 0;
/** * mpt3sas_atto_init - perform initializaion for ATTO branded * adapter. * @ioc : per adapter object *5 * Return: 0 for success, non-zero for failure.
*/ staticint
mpt3sas_atto_init(struct MPT3SAS_ADAPTER *ioc)
{ int sz = 0;
Mpi2BiosPage4_t *bios_pg4 = NULL;
Mpi2ConfigReply_t mpi_reply; int r; int ix; union ATTO_SAS_ADDRESS sas_addr; union ATTO_SAS_ADDRESS temp; union ATTO_SAS_ADDRESS bias;
r = mpt3sas_atto_get_sas_addr(ioc, &sas_addr); if (r) return r;
/* get header first to get size */
r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, NULL, 0); if (r) {
ioc_err(ioc, "Failed to read ATTO bios page 4 header.\n"); return r;
}
sz = mpi_reply.Header.PageLength * sizeof(u32);
bios_pg4 = kzalloc(sz, GFP_KERNEL); if (!bios_pg4) {
ioc_err(ioc, "Failed to allocate memory for ATTO bios page.\n"); return -ENOMEM;
}
/* read bios page 4 */
r = mpt3sas_config_get_bios_pg4(ioc, &mpi_reply, bios_pg4, sz); if (r) {
ioc_err(ioc, "Failed to read ATTO bios page 4\n"); goto out;
}
/* Update bios page 4 with the ATTO WWID */
bias.q = sas_addr.q;
bias.b[7] += ATTO_SAS_ADDR_DEVNAME_BIAS;
for (ix = 0; ix < bios_pg4->NumPhys; ix++) {
temp.q = sas_addr.q;
temp.b[7] += ix;
bios_pg4->Phy[ix].ReassignmentWWID = temp.q;
bios_pg4->Phy[ix].ReassignmentDeviceName = bias.q;
}
r = mpt3sas_config_set_bios_pg4(ioc, &mpi_reply, bios_pg4, sz);
out:
kfree(bios_pg4); return r;
}
/** * _base_static_config_pages - static start of day config pages * @ioc: per adapter object
*/ staticint
_base_static_config_pages(struct MPT3SAS_ADAPTER *ioc)
{
Mpi2IOUnitPage8_t iounit_pg8;
Mpi2ConfigReply_t mpi_reply;
u32 iounit_pg1_flags; int tg_flags = 0; int rc;
ioc->nvme_abort_timeout = 30;
rc = mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply,
&ioc->manu_pg0); if (rc) return rc; if (ioc->ir_firmware) {
rc = mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply,
&ioc->manu_pg10); if (rc) return rc;
}
if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO) {
rc = mpt3sas_atto_init(ioc); if (rc) return rc;
}
/* * Ensure correct T10 PI operation if vendor left EEDPTagMode * flag unset in NVDATA.
*/
rc = mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply,
&ioc->manu_pg11); if (rc) return rc; if (!ioc->is_gen35_ioc && ioc->manu_pg11.EEDPTagMode == 0) {
pr_err("%s: overriding NVDATA EEDPTagMode setting from 0 to 1\n",
ioc->name);
ioc->manu_pg11.EEDPTagMode = 0x1;
mpt3sas_config_set_manufacturing_pg11(ioc, &mpi_reply,
&ioc->manu_pg11);
} if (ioc->manu_pg11.AddlFlags2 & NVME_TASK_MNGT_CUSTOM_MASK)
ioc->tm_custom_handling = 1; else {
ioc->tm_custom_handling = 0; if (ioc->manu_pg11.NVMeAbortTO < NVME_TASK_ABORT_MIN_TIMEOUT)
ioc->nvme_abort_timeout = NVME_TASK_ABORT_MIN_TIMEOUT; elseif (ioc->manu_pg11.NVMeAbortTO >
NVME_TASK_ABORT_MAX_TIMEOUT)
ioc->nvme_abort_timeout = NVME_TASK_ABORT_MAX_TIMEOUT; else
ioc->nvme_abort_timeout = ioc->manu_pg11.NVMeAbortTO;
}
ioc->time_sync_interval =
ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_MASK; if (ioc->time_sync_interval) { if (ioc->manu_pg11.TimeSyncInterval & MPT3SAS_TIMESYNC_UNIT_MASK)
ioc->time_sync_interval =
ioc->time_sync_interval * SECONDS_PER_HOUR; else
ioc->time_sync_interval =
ioc->time_sync_interval * SECONDS_PER_MIN;
dinitprintk(ioc, ioc_info(ioc, "Driver-FW TimeSync interval is %d seconds. ManuPg11 TimeSync Unit is in %s\n",
ioc->time_sync_interval, (ioc->manu_pg11.TimeSyncInterval &
MPT3SAS_TIMESYNC_UNIT_MASK) ? "Hour" : "Minute"));
} else { if (ioc->is_gen35_ioc)
ioc_warn(ioc, "TimeSync Interval in Manuf page-11 is not enabled. Periodic Time-Sync will be disabled\n");
}
rc = _base_assign_fw_reported_qd(ioc); if (rc) return rc;
/* * ATTO doesn't use bios page 2 and 3 for bios settings.
*/ if (ioc->pdev->vendor == MPI2_MFGPAGE_VENDORID_ATTO)
ioc->bios_pg3.BiosVersion = 0; else {
rc = mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); if (rc) return rc;
rc = mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); if (rc) return rc;
}
rc = mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); if (rc) return rc;
rc = mpt3sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); if (rc) return rc;
rc = mpt3sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); if (rc) return rc;
rc = mpt3sas_config_get_iounit_pg8(ioc, &mpi_reply, &iounit_pg8); if (rc) return rc;
_base_display_ioc_capabilities(ioc);
/* * Enable task_set_full handling in iounit_pg1 when the * facts capabilities indicate that its supported.
*/
iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); if ((ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING))
iounit_pg1_flags &=
~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; else
iounit_pg1_flags |=
MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
rc = mpt3sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); if (rc) return rc;
if (iounit_pg8.NumSensors)
ioc->temp_sensors_count = iounit_pg8.NumSensors; if (ioc->is_aero_ioc) {
rc = _base_update_ioc_page1_inlinewith_perf_mode(ioc); if (rc) return rc;
} if (ioc->is_gen35_ioc) { if (ioc->is_driver_loading) {
rc = _base_get_diag_triggers(ioc); if (rc) return rc;
} else { /* * In case of online HBA FW update operation, * check whether updated FW supports the driver trigger * pages or not. * - If previous FW has not supported driver trigger * pages and newer FW supports them then update these * pages with current diag trigger values. * - If previous FW has supported driver trigger pages * and new FW doesn't support them then disable * support_trigger_pages flag.
*/
_base_check_for_trigger_pages_support(ioc, &tg_flags); if (!ioc->supports_trigger_pages && tg_flags != -EFAULT)
_base_update_diag_trigger_pages(ioc); elseif (ioc->supports_trigger_pages &&
tg_flags == -EFAULT)
ioc->supports_trigger_pages = 0;
}
} return 0;
}
/** * mpt3sas_check_same_4gb_region - checks whether all reply queues in a set are * having same upper 32bits in their base memory address. * @start_address: Base address of a reply queue set * @pool_sz: Size of single Reply Descriptor Post Queues pool size * * Return: 1 if reply queues in a set have a same upper 32bits in their base * memory address, else 0.
*/ staticint
mpt3sas_check_same_4gb_region(dma_addr_t start_address, u32 pool_sz)
{
dma_addr_t end_address;
end_address = start_address + pool_sz - 1;
if (upper_32_bits(start_address) == upper_32_bits(end_address)) return 1; else return 0;
}
/** * _base_reduce_hba_queue_depth- Retry with reduced queue depth * @ioc: Adapter object * * Return: 0 for success, non-zero for failure.
**/ staticinlineint
_base_reduce_hba_queue_depth(struct MPT3SAS_ADAPTER *ioc)
{ int reduce_sz = 64;
/** * _base_allocate_reply_post_free_array - Allocating DMA'able memory * for reply post free array. * @ioc: Adapter object * @reply_post_free_array_sz: DMA Pool size * Return: 0 for success, non-zero for failure.
*/
staticint
_base_allocate_reply_post_free_array(struct MPT3SAS_ADAPTER *ioc,
u32 reply_post_free_array_sz)
{
ioc->reply_post_free_array_dma_pool =
dma_pool_create("reply_post_free_array pool",
&ioc->pdev->dev, reply_post_free_array_sz, 16, 0); if (!ioc->reply_post_free_array_dma_pool) return -ENOMEM;
ioc->reply_post_free_array =
dma_pool_alloc(ioc->reply_post_free_array_dma_pool,
GFP_KERNEL, &ioc->reply_post_free_array_dma); if (!ioc->reply_post_free_array) return -EAGAIN; if (!mpt3sas_check_same_4gb_region(ioc->reply_post_free_array_dma,
reply_post_free_array_sz)) {
dinitprintk(ioc, pr_err( "Bad Reply Free Pool! Reply Free (0x%p) Reply Free dma = (0x%llx)\n",
ioc->reply_free,
(unsignedlonglong) ioc->reply_free_dma));
ioc->use_32bit_dma = true; return -EAGAIN;
} return 0;
} /** * base_alloc_rdpq_dma_pool - Allocating DMA'able memory * for reply queues. * @ioc: per adapter object * @sz: DMA Pool size * Return: 0 for success, non-zero for failure.
*/ staticint
base_alloc_rdpq_dma_pool(struct MPT3SAS_ADAPTER *ioc, int sz)
{ int i = 0;
u32 dma_alloc_count = 0; int reply_post_free_sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t); int count = ioc->rdpq_array_enable ? ioc->reply_queue_count : 1;
ioc->reply_post = kcalloc(count, sizeof(struct reply_post_struct),
GFP_KERNEL); if (!ioc->reply_post) 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 i.e 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(count,
RDPQ_MAX_INDEX_IN_ONE_CHUNK);
ioc->reply_post_free_dma_pool =
dma_pool_create("reply_post_free pool",
&ioc->pdev->dev, sz, 16, 0); if (!ioc->reply_post_free_dma_pool) return -ENOMEM; for (i = 0; i < count; i++) { if ((i % RDPQ_MAX_INDEX_IN_ONE_CHUNK == 0) && dma_alloc_count) {
ioc->reply_post[i].reply_post_free =
dma_pool_zalloc(ioc->reply_post_free_dma_pool,
GFP_KERNEL,
&ioc->reply_post[i].reply_post_free_dma); if (!ioc->reply_post[i].reply_post_free) return -ENOMEM; /* * Each set of RDPQ pool must satisfy 4gb boundary * restriction. * 1) Check if allocated resources for RDPQ pool are in * the same 4GB range. * 2) If #1 is true, continue with 64 bit DMA. * 3) If #1 is false, return 1. which means free all the * resources and set DMA mask to 32 and allocate.
*/ if (!mpt3sas_check_same_4gb_region(
ioc->reply_post[i].reply_post_free_dma, sz)) {
dinitprintk(ioc,
ioc_err(ioc, "bad Replypost free pool(0x%p)" "reply_post_free_dma = (0x%llx)\n",
ioc->reply_post[i].reply_post_free,
(unsignedlonglong)
ioc->reply_post[i].reply_post_free_dma)); return -EAGAIN;
}
dma_alloc_count--;
/* Firmware maintains additional facts->HighPriorityCredit number of * credits for HiPriprity Request messages, so hba queue depth will be * sum of max_request_credit and high priority queue depth.
*/
ioc->hba_queue_depth = max_request_credit + ioc->hi_priority_depth;
/* chain segment size */ if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { if (facts->IOCMaxChainSegmentSize)
ioc->chain_segment_sz =
facts->IOCMaxChainSegmentSize *
MAX_CHAIN_ELEMT_SZ; else /* set to 128 bytes size if IOCMaxChainSegmentSize is zero */
ioc->chain_segment_sz = DEFAULT_NUM_FWCHAIN_ELEMTS *
MAX_CHAIN_ELEMT_SZ;
} else
ioc->chain_segment_sz = ioc->request_sz;
/* calculate the max scatter element size */
sge_size = max_t(u16, ioc->sge_size, ioc->sge_size_ieee);
retry_allocation:
total_sz = 0; /* calculate number of sg elements left over in the 1st frame */
max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) - sizeof(Mpi2SGEIOUnion_t)) + sge_size);
ioc->max_sges_in_main_message = max_sge_elements/sge_size;
/* now do the same for a chain buffer */
max_sge_elements = ioc->chain_segment_sz - sge_size;
ioc->max_sges_in_chain_message = max_sge_elements/sge_size;
/* set the scsi host can_queue depth * with some internal commands that could be outstanding
*/
ioc->shost->can_queue = ioc->scsiio_depth - INTERNAL_SCSIIO_CMDS_COUNT;
dinitprintk(ioc,
ioc_info(ioc, "scsi host: can_queue depth (%d)\n",
ioc->shost->can_queue));
/* contiguous pool for request and chains, 16 byte align, one extra " * "frame for smid=0
*/
ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth;
sz = ((ioc->scsiio_depth + 1) * ioc->request_sz);
ioc->io_queue_num = kcalloc(ioc->scsiio_depth, sizeof(u16), GFP_KERNEL); if (!ioc->io_queue_num) goto out; /* * The number of NVMe page sized blocks needed is: * (((sg_tablesize * 8) - 1) / (page_size - 8)) + 1 * ((sg_tablesize * 8) - 1) is the max PRP's minus the first PRP entry * that is placed in the main message frame. 8 is the size of each PRP * entry or PRP list pointer entry. 8 is subtracted from page_size * because of the PRP list pointer entry at the end of a page, so this * is not counted as a PRP entry. The 1 added page is a round up. * * To avoid allocation failures due to the amount of memory that could * be required for NVMe PRP's, only each set of NVMe blocks will be * contiguous, so a new set is allocated for each possible I/O.
*/
try_32bit_dma:
_base_release_memory_pools(ioc); if (ioc->use_32bit_dma && (ioc->dma_mask > 32)) { /* Change dma coherent mask to 32 bit and reallocate */ if (_base_config_dma_addressing(ioc, ioc->pdev) != 0) {
pr_err("Setting 32 bit coherent DMA mask Failed %s\n",
pci_name(ioc->pdev)); return -ENODEV;
}
} elseif (_base_reduce_hba_queue_depth(ioc) != 0) return -ENOMEM; goto retry_allocation;
out: return -ENOMEM;
}
/** * mpt3sas_base_get_iocstate - Get the current state of a MPT adapter. * @ioc: Pointer to MPT_ADAPTER structure * @cooked: Request raw or cooked IOC state * * Return: all IOC Doorbell register bits if cooked==0, else just the * Doorbell bits in MPI_IOC_STATE_MASK.
*/
u32
mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked)
{
u32 s, sc;
s = ioc->base_readl_ext_retry(&ioc->chip->Doorbell);
sc = s & MPI2_IOC_STATE_MASK; return cooked ? sc : s;
}
/** * _base_wait_on_iocstate - waiting on a particular ioc state * @ioc: ? * @ioc_state: controller state { READY, OPERATIONAL, or RESET } * @timeout: timeout in second * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout)
{
u32 count, cntdn;
u32 current_state;
count = 0;
cntdn = 1000 * timeout; do {
current_state = mpt3sas_base_get_iocstate(ioc, 1); if (current_state == ioc_state) return 0; if (count && current_state == MPI2_IOC_STATE_FAULT) break; if (count && current_state == MPI2_IOC_STATE_COREDUMP) break;
usleep_range(1000, 1500);
count++;
} while (--cntdn);
return current_state;
}
/** * _base_dump_reg_set - This function will print hexdump of register set. * @ioc: per adapter object * * Return: nothing.
*/ staticinlinevoid
_base_dump_reg_set(struct MPT3SAS_ADAPTER *ioc)
{ unsignedint i, sz = 256;
u32 __iomem *reg = (u32 __iomem *)ioc->chip;
ioc_info(ioc, "System Register set:\n"); for (i = 0; i < (sz / sizeof(u32)); i++)
pr_info("%08x: %08x\n", (i * 4), readl(®[i]));
}
/** * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by * a write to the doorbell) * @ioc: per adapter object * @timeout: timeout in seconds * * Return: 0 for success, non-zero for failure. * * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
*/
ioc_err(ioc, "%s: failed due to timeout count(%d), int_status(%x)!\n",
__func__, count, int_status); return -EFAULT;
}
/** * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. * @ioc: per adapter object * @timeout: timeout in second * * Return: 0 for success, non-zero for failure. * * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to * doorbell.
*/ staticint
_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout)
{
u32 cntdn, count;
u32 int_status;
u32 doorbell;
usleep_range(1000, 1500);
count++;
} while (--cntdn);
out:
ioc_err(ioc, "%s: failed due to timeout count(%d), int_status(%x)!\n",
__func__, count, int_status); return -EFAULT;
}
/** * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use * @ioc: per adapter object * @timeout: timeout in second * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout)
{
u32 cntdn, count;
u32 doorbell_reg;
usleep_range(1000, 1500);
count++;
} while (--cntdn);
ioc_err(ioc, "%s: failed due to timeout count(%d), doorbell_reg(%x)!\n",
__func__, count, doorbell_reg); return -EFAULT;
}
/** * _base_send_ioc_reset - send doorbell reset * @ioc: per adapter object * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET * @timeout: timeout in second * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout)
{
u32 ioc_state; int r = 0; unsignedlong flags;
if (!(ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY)) return -EFAULT;
ioc_info(ioc, "sending message unit reset !!\n");
writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT,
&ioc->chip->Doorbell); if ((_base_wait_for_doorbell_ack(ioc, 15))) {
r = -EFAULT; goto out;
}
ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, timeout); if (ioc_state) {
ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
__func__, ioc_state);
r = -EFAULT; goto out;
}
out: if (r != 0) {
ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); /* * Wait for IOC state CoreDump to clear only during * HBA initialization & release time.
*/ if ((ioc_state & MPI2_IOC_STATE_MASK) ==
MPI2_IOC_STATE_COREDUMP && (ioc->is_driver_loading == 1 ||
ioc->fault_reset_work_q == NULL)) {
spin_unlock_irqrestore(
&ioc->ioc_reset_in_progress_lock, flags);
mpt3sas_print_coredump_info(ioc, ioc_state);
mpt3sas_base_wait_for_coredump_completion(ioc,
__func__);
spin_lock_irqsave(
&ioc->ioc_reset_in_progress_lock, flags);
}
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
}
ioc_info(ioc, "message unit reset: %s\n",
r == 0 ? "SUCCESS" : "FAILED"); return r;
}
/** * mpt3sas_wait_for_ioc - IOC's operational state is checked here. * @ioc: per adapter object * @timeout: timeout in seconds * * Return: Waits up to timeout seconds for the IOC to * become operational. Returns 0 if IOC is present * and operational; otherwise returns %-EFAULT.
*/
int
mpt3sas_wait_for_ioc(struct MPT3SAS_ADAPTER *ioc, int timeout)
{ int wait_state_count = 0;
u32 ioc_state;
do {
ioc_state = mpt3sas_base_get_iocstate(ioc, 1); if (ioc_state == MPI2_IOC_STATE_OPERATIONAL) break;
/* * Watchdog thread will be started after IOC Initialization, so * no need to wait here for IOC state to become operational * when IOC Initialization is on. Instead the driver will * return ETIME status, so that calling function can issue * diag reset operation and retry the command.
*/ if (ioc->is_driver_loading) return -ETIME;
ssleep(1);
ioc_info(ioc, "%s: waiting for operational state(count=%d)\n",
__func__, ++wait_state_count);
} while (--timeout); if (!timeout) {
ioc_err(ioc, "%s: failed due to ioc not operational\n", __func__); return -EFAULT;
} if (wait_state_count)
ioc_info(ioc, "ioc is operational\n"); return 0;
}
/** * _base_handshake_req_reply_wait - send request thru doorbell interface * @ioc: per adapter object * @request_bytes: request length * @request: pointer having request payload * @reply_bytes: reply length * @reply: pointer to reply payload * @timeout: timeout in second * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes,
u32 *request, int reply_bytes, u16 *reply, int timeout)
{
MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; int i;
u8 failed;
__le32 *mfp; int ret_val;
/* make sure doorbell is not in use */ if ((ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) {
ioc_err(ioc, "doorbell is in use (line=%d)\n", __LINE__); goto doorbell_diag_reset;
}
/* clear pending doorbell interrupts from previous state changes */ if (ioc->base_readl(&ioc->chip->HostInterruptStatus) &
MPI2_HIS_IOC2SYS_DB_STATUS)
writel(0, &ioc->chip->HostInterruptStatus);
/* send message to ioc */
writel(((MPI2_FUNCTION_HANDSHAKE<<MPI2_DOORBELL_FUNCTION_SHIFT) |
((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)),
&ioc->chip->Doorbell);
if ((_base_spin_on_doorbell_int(ioc, 5))) {
ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
__LINE__); return -EFAULT;
}
writel(0, &ioc->chip->HostInterruptStatus);
/* send message 32-bits at a time */ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) {
writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); if ((_base_wait_for_doorbell_ack(ioc, 5)))
failed = 1;
}
/* now wait for the reply */ if ((_base_wait_for_doorbell_int(ioc, timeout))) {
ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
__LINE__); return -EFAULT;
}
/* read the first two 16-bits, it gives the total length of the reply */
reply[0] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
& MPI2_DOORBELL_DATA_MASK);
writel(0, &ioc->chip->HostInterruptStatus); if ((_base_wait_for_doorbell_int(ioc, 5))) {
ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
__LINE__); return -EFAULT;
}
reply[1] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
& MPI2_DOORBELL_DATA_MASK);
writel(0, &ioc->chip->HostInterruptStatus);
for (i = 2; i < default_reply->MsgLength * 2; i++) { if ((_base_wait_for_doorbell_int(ioc, 5))) {
ioc_err(ioc, "doorbell handshake int failed (line=%d)\n",
__LINE__); return -EFAULT;
} if (i >= reply_bytes/2) /* overflow case */
ioc->base_readl_ext_retry(&ioc->chip->Doorbell); else
reply[i] = le16_to_cpu(
ioc->base_readl_ext_retry(&ioc->chip->Doorbell)
& MPI2_DOORBELL_DATA_MASK);
writel(0, &ioc->chip->HostInterruptStatus);
}
_base_wait_for_doorbell_int(ioc, 5); if (_base_wait_for_doorbell_not_used(ioc, 5) != 0) {
dhsprintk(ioc,
ioc_info(ioc, "doorbell is in use (line=%d)\n",
__LINE__));
}
writel(0, &ioc->chip->HostInterruptStatus);
if (ioc->logging_level & MPT_DEBUG_INIT) {
mfp = (__le32 *)reply;
pr_info("\toffset:data\n"); for (i = 0; i < reply_bytes/4; i++)
ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4,
le32_to_cpu(mfp[i]));
} return 0;
/** * mpt3sas_base_sas_iounit_control - send sas iounit control to FW * @ioc: per adapter object * @mpi_reply: the reply payload from FW * @mpi_request: the request payload sent to FW * * The SAS IO Unit Control Request message allows the host to perform low-level * operations, such as resets on the PHYs of the IO Unit, also allows the host * to obtain the IOC assigned device handles for a device if it has other * identifying information about the device, in addition allows the host to * remove IOC resources associated with the device. * * Return: 0 for success, non-zero for failure.
*/ int
mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
Mpi2SasIoUnitControlReply_t *mpi_reply,
Mpi2SasIoUnitControlRequest_t *mpi_request)
{
u16 smid;
u8 issue_reset = 0; int rc; void *request;
/** * mpt3sas_base_scsi_enclosure_processor - sending request to sep device * @ioc: per adapter object * @mpi_reply: the reply payload from FW * @mpi_request: the request payload sent to FW * * The SCSI Enclosure Processor request message causes the IOC to * communicate with SES devices to control LED status signals. * * Return: 0 for success, non-zero for failure.
*/ int
mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request)
{
u16 smid;
u8 issue_reset = 0; int rc; void *request;
/** * _base_get_port_facts - obtain port facts reply and save in ioc * @ioc: per adapter object * @port: ? * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port)
{
Mpi2PortFactsRequest_t mpi_request;
Mpi2PortFactsReply_t mpi_reply; struct mpt3sas_port_facts *pfacts; int mpi_reply_sz, mpi_request_sz, r;
/** * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL * @ioc: per adapter object * @timeout: * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout)
{
u32 ioc_state; int rc;
/** * _base_get_ioc_facts - obtain ioc facts reply and save in ioc * @ioc: per adapter object * * Return: 0 for success, non-zero for failure.
*/ staticint
_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc)
{
Mpi2IOCFactsRequest_t mpi_request;
Mpi2IOCFactsReply_t mpi_reply; struct mpt3sas_facts *facts; int mpi_reply_sz, mpi_request_sz, r;
if (ioc->rdpq_array_enable) {
reply_post_free_array_sz = ioc->reply_queue_count * sizeof(Mpi2IOCInitRDPQArrayEntry);
memset(ioc->reply_post_free_array, 0, reply_post_free_array_sz); for (i = 0; i < ioc->reply_queue_count; i++)
ioc->reply_post_free_array[i].RDPQBaseAddress =
cpu_to_le64(
(u64)ioc->reply_post[i].reply_post_free_dma);
mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE;
mpi_request.ReplyDescriptorPostQueueAddress =
cpu_to_le64((u64)ioc->reply_post_free_array_dma);
} else {
mpi_request.ReplyDescriptorPostQueueAddress =
cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma);
}
/* * Set the flag to enable CoreDump state feature in IOC firmware.
*/
mpi_request.ConfigurationFlags |=
cpu_to_le16(MPI26_IOCINIT_CFGFLAGS_COREDUMP_ENABLE);
/* This time stamp specifies number of milliseconds * since epoch ~ midnight January 1, 1970.
*/
current_time = ktime_get_real();
mpi_request.TimeStamp = cpu_to_le64(ktime_to_ms(current_time));
if (ioc->logging_level & MPT_DEBUG_INIT) {
__le32 *mfp; int i;
mfp = (__le32 *)&mpi_request;
ioc_info(ioc, "\toffset:data\n"); for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++)
ioc_info(ioc, "\t[0x%02x]:%08x\n", i*4,
le32_to_cpu(mfp[i]));
}
r = _base_handshake_req_reply_wait(ioc, sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 30);
/** * mpt3sas_port_enable_done - command completion routine for port enable * @ioc: per adapter object * @smid: system request message index * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return: 1 meaning mf should be freed from _base_interrupt * 0 means the mf is freed from this function.
*/
u8
mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
u32 reply)
{
MPI2DefaultReply_t *mpi_reply;
u16 ioc_status;
if (ioc->port_enable_cmds.status == MPT3_CMD_NOT_USED) return 1;
mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) return 1;
if (mpi_reply->Function != MPI2_FUNCTION_PORT_ENABLE) return 1;
/** * _base_determine_wait_on_discovery - desposition * @ioc: per adapter object * * Decide whether to wait on discovery to complete. Used to either * locate boot device, or report volumes ahead of physical devices. * * Return: 1 for wait, 0 for don't wait.
*/ staticint
_base_determine_wait_on_discovery(struct MPT3SAS_ADAPTER *ioc)
{ /* We wait for discovery to complete if IR firmware is loaded. * The sas topology events arrive before PD events, so we need time to * turn on the bit in ioc->pd_handles to indicate PD * Also, it maybe required to report Volumes ahead of physical * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set.
*/ if (ioc->ir_firmware) return 1;
/* if no Bios, then we don't need to wait */ if (!ioc->bios_pg3.BiosVersion) return 0;
/* Bios is present, then we drop down here. * * If there any entries in the Bios Page 2, then we wait * for discovery to complete.
*/
if (issue_diag_reset) { if (ioc->drv_internal_flags & MPT_DRV_INTERNAL_FIRST_PE_ISSUED) return -EFAULT; if (mpt3sas_base_check_for_fault_and_issue_reset(ioc)) return -EFAULT;
r = -EAGAIN;
} return r;
}
/** * mpt3sas_base_validate_event_type - validating event types * @ioc: per adapter object * @event_type: firmware event * * This will turn on firmware event notification when application * ask for that event. We don't mask events that are already enabled.
*/ void
mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type)
{ int i, j;
u32 event_mask, desired_event;
u8 send_update_to_fw;
for (i = 0, send_update_to_fw = 0; i <
MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) {
event_mask = ~event_type[i];
desired_event = 1; for (j = 0; j < 32; j++) { if (!(event_mask & desired_event) &&
(ioc->event_masks[i] & desired_event)) {
ioc->event_masks[i] &= ~desired_event;
send_update_to_fw = 1;
}
desired_event = (desired_event << 1);
}
}
drsprintk(ioc, ioc_info(ioc, "Wait for FW to go to the READY state\n"));
ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20); if (ioc_state) {
ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
__func__, ioc_state);
_base_dump_reg_set(ioc); goto fail;
}
/** * mpt3sas_base_make_ioc_ready - put controller in READY state * @ioc: per adapter object * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Return: 0 for success, non-zero for failure.
*/ int
mpt3sas_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, enum reset_type type)
{
u32 ioc_state; int rc; int count;
/* if in RESET state, it should move to READY state shortly */
count = 0; if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_RESET) { while ((ioc_state & MPI2_IOC_STATE_MASK) !=
MPI2_IOC_STATE_READY) { if (count++ == 10) {
ioc_err(ioc, "%s: failed going to ready state (ioc_state=0x%x)\n",
__func__, ioc_state); return -EFAULT;
}
ssleep(1);
ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
}
}
if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) return 0;
if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_COREDUMP) { /* * if host reset is invoked while watch dog thread is waiting * for IOC state to be changed to Fault state then driver has * to wait here for CoreDump state to clear otherwise reset * will be issued to the FW and FW move the IOC state to * reset state without copying the FW logs to coredump region.
*/ if (ioc->ioc_coredump_loop != MPT3SAS_COREDUMP_LOOP_DONE) {
mpt3sas_print_coredump_info(ioc, ioc_state &
MPI2_DOORBELL_DATA_MASK);
mpt3sas_base_wait_for_coredump_completion(ioc,
__func__);
} goto issue_diag_reset;
}
if (type == FORCE_BIG_HAMMER) goto issue_diag_reset;
if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) if (!(_base_send_ioc_reset(ioc,
MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15))) { return 0;
}
/* initialize Reply Free Queue */ for (i = 0, reply_address = (u32)ioc->reply_dma ;
i < ioc->reply_free_queue_depth ; i++, reply_address +=
ioc->reply_sz) {
ioc->reply_free[i] = cpu_to_le32(reply_address); if (ioc->is_mcpu_endpoint)
_base_clone_reply_to_sys_mem(ioc,
reply_address, i);
}
/* initialize reply queues */ if (ioc->is_driver_loading)
_base_assign_reply_queues(ioc);
/* initialize Reply Post Free Queue */
index = 0;
reply_post_free_contig = ioc->reply_post[0].reply_post_free;
list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { /* * If RDPQ is enabled, switch to the next allocation. * Otherwise advance within the contiguous region.
*/ if (ioc->rdpq_array_enable) {
reply_q->reply_post_free =
ioc->reply_post[index++].reply_post_free;
} else {
reply_q->reply_post_free = reply_post_free_contig;
reply_post_free_contig += ioc->reply_post_queue_depth;
}
reply_q->reply_post_host_index = 0; for (i = 0; i < ioc->reply_post_queue_depth; i++)
reply_q->reply_post_free[i].Words =
cpu_to_le64(ULLONG_MAX); if (!_base_is_controller_msix_enabled(ioc)) goto skip_init_reply_post_free_queue;
}
skip_init_reply_post_free_queue:
r = _base_send_ioc_init(ioc); if (r) { /* * No need to check IOC state for fault state & issue * diag reset during host reset. This check is need * only during driver load time.
*/ if (!ioc->is_driver_loading) return r;
rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); if (rc || (_base_send_ioc_init(ioc))) return r;
}
pci_set_drvdata(ioc->pdev, ioc->shost);
r = _base_get_ioc_facts(ioc); if (r) {
rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); if (rc || (_base_get_ioc_facts(ioc))) goto out_free_resources;
}
switch (ioc->hba_mpi_version_belonged) { case MPI2_VERSION:
ioc->build_sg_scmd = &_base_build_sg_scmd;
ioc->build_sg = &_base_build_sg;
ioc->build_zero_len_sge = &_base_build_zero_len_sge;
ioc->get_msix_index_for_smlio = &_base_get_msix_index; break; case MPI25_VERSION: case MPI26_VERSION: /* * In SAS3.0, * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and * Target Status - all require the IEEE formatted scatter gather * elements.
*/
ioc->build_sg_scmd = &_base_build_sg_scmd_ieee;
ioc->build_sg = &_base_build_sg_ieee;
ioc->build_nvme_prp = &_base_build_nvme_prp;
ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee;
ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); if (ioc->high_iops_queues)
ioc->get_msix_index_for_smlio =
&_base_get_high_iops_msix_index; else
ioc->get_msix_index_for_smlio = &_base_get_msix_index; break;
} if (ioc->atomic_desc_capable) {
ioc->put_smid_default = &_base_put_smid_default_atomic;
ioc->put_smid_scsi_io = &_base_put_smid_scsi_io_atomic;
ioc->put_smid_fast_path =
&_base_put_smid_fast_path_atomic;
ioc->put_smid_hi_priority =
&_base_put_smid_hi_priority_atomic;
} else {
ioc->put_smid_default = &_base_put_smid_default;
ioc->put_smid_fast_path = &_base_put_smid_fast_path;
ioc->put_smid_hi_priority = &_base_put_smid_hi_priority; if (ioc->is_mcpu_endpoint)
ioc->put_smid_scsi_io =
&_base_put_smid_mpi_ep_scsi_io; else
ioc->put_smid_scsi_io = &_base_put_smid_scsi_io;
} /* * These function pointers for other requests that don't * the require IEEE scatter gather elements. * * For example Configuration Pages and SAS IOUNIT Control don't.
*/
ioc->build_sg_mpi = &_base_build_sg;
ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge;
r = mpt3sas_base_make_ioc_ready(ioc, SOFT_RESET); if (r) goto out_free_resources;
ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, sizeof(struct mpt3sas_port_facts), GFP_KERNEL); if (!ioc->pfacts) {
r = -ENOMEM; goto out_free_resources;
}
for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) {
r = _base_get_port_facts(ioc, i); if (r) {
rc = mpt3sas_base_check_for_fault_and_issue_reset(ioc); if (rc || (_base_get_port_facts(ioc, i))) goto out_free_resources;
}
}
r = _base_allocate_memory_pools(ioc); if (r) goto out_free_resources;
/* allocate memory pd handle bitmask list */
ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); if (ioc->facts.MaxDevHandle % 8)
ioc->pd_handles_sz++; /* * pd_handles_sz should have, at least, the minimal room for * set_bit()/test_bit(), otherwise out-of-memory touch may occur.
*/
ioc->pd_handles_sz = ALIGN(ioc->pd_handles_sz, sizeof(unsignedlong));
ioc->pd_handles = kzalloc(ioc->pd_handles_sz,
GFP_KERNEL); if (!ioc->pd_handles) {
r = -ENOMEM; goto out_free_resources;
}
ioc->blocking_handles = kzalloc(ioc->pd_handles_sz,
GFP_KERNEL); if (!ioc->blocking_handles) {
r = -ENOMEM; goto out_free_resources;
}
/* allocate memory for pending OS device add list */
ioc->pend_os_device_add_sz = (ioc->facts.MaxDevHandle / 8); if (ioc->facts.MaxDevHandle % 8)
ioc->pend_os_device_add_sz++;
/* * pend_os_device_add_sz should have, at least, the minimal room for * set_bit()/test_bit(), otherwise out-of-memory may occur.
*/
ioc->pend_os_device_add_sz = ALIGN(ioc->pend_os_device_add_sz, sizeof(unsignedlong));
ioc->pend_os_device_add = kzalloc(ioc->pend_os_device_add_sz,
GFP_KERNEL); if (!ioc->pend_os_device_add) {
r = -ENOMEM; goto out_free_resources;
}
ioc->device_remove_in_progress_sz = ioc->pend_os_device_add_sz;
ioc->device_remove_in_progress =
kzalloc(ioc->device_remove_in_progress_sz, GFP_KERNEL); if (!ioc->device_remove_in_progress) {
r = -ENOMEM; goto out_free_resources;
}
for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
ioc->event_masks[i] = -1;
/* here we enable the events we care about */
_base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY);
_base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
_base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
_base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
_base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
_base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
_base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME);
_base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK);
_base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS);
_base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
_base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD);
_base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION);
_base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR); if (ioc->hba_mpi_version_belonged == MPI26_VERSION) { if (ioc->is_gen35_ioc) {
_base_unmask_events(ioc,
MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE);
_base_unmask_events(ioc, MPI2_EVENT_PCIE_ENUMERATION);
_base_unmask_events(ioc,
MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
}
}
r = _base_make_ioc_operational(ioc); if (r == -EAGAIN) {
r = _base_make_ioc_operational(ioc); if (r) goto out_free_resources;
}
/* * Copy current copy of IOCFacts in prev_fw_facts * and it will be used during online firmware upgrade.
*/
memcpy(&ioc->prev_fw_facts, &ioc->facts, sizeof(struct mpt3sas_facts));
/** * mpt3sas_wait_for_commands_to_complete - reset controller * @ioc: Pointer to MPT_ADAPTER structure * * This function is waiting 10s for all pending commands to complete * prior to putting controller in reset.
*/ void
mpt3sas_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc)
{
u32 ioc_state;
/* If this hard reset is called while port enable is active, then * there is no reason to call make_ioc_operational
*/ if (ioc->is_driver_loading && ioc->port_enable_failed) {
ioc->remove_host = 1;
r = -EFAULT; goto out;
}
r = _base_get_ioc_facts(ioc); if (r) goto out;
r = _base_check_ioc_facts_changes(ioc); if (r) {
ioc_info(ioc, "Some of the parameters got changed in this new firmware" " image and it requires system reboot\n"); goto out;
} if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable)
panic("%s: Issue occurred with flashing controller firmware." "Please reboot the system and ensure that the correct" " firmware version is running\n", ioc->name);
r = _base_make_ioc_operational(ioc); if (!r)
_base_reset_done_handler(ioc);
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.