diff options
46 files changed, 13608 insertions, 569 deletions
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 8fcaa1136f2c..776e46ee95da 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -506,6 +506,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, iser_conn->iscsi_conn = conn; out: + iscsi_put_endpoint(ep); mutex_unlock(&iser_conn->state_mutex); return error; } @@ -1002,6 +1003,7 @@ static struct iscsi_transport iscsi_iser_transport = { /* connection management */ .create_conn = iscsi_iser_conn_create, .bind_conn = iscsi_iser_conn_bind, + .unbind_conn = iscsi_conn_unbind, .destroy_conn = iscsi_conn_teardown, .attr_is_visible = iser_attr_is_visible, .set_param = iscsi_iser_set_param, diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c index ee9e4028c0f3..90253208a72f 100644 --- a/drivers/scsi/FlashPoint.c +++ b/drivers/scsi/FlashPoint.c @@ -40,7 +40,7 @@ struct sccb_mgr_info { u16 si_per_targ_ultra_nego; u16 si_per_targ_no_disc; u16 si_per_targ_wide_nego; - u16 si_flags; + u16 si_mflags; unsigned char si_card_family; unsigned char si_bustype; unsigned char si_card_model[3]; @@ -1045,22 +1045,22 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo) ScamFlg = (unsigned char)FPT_utilEERead(ioport, SCAM_CONFIG / 2); - pCardInfo->si_flags = 0x0000; + pCardInfo->si_mflags = 0x0000; if (i & 0x01) - pCardInfo->si_flags |= SCSI_PARITY_ENA; + pCardInfo->si_mflags |= SCSI_PARITY_ENA; if (!(i & 0x02)) - pCardInfo->si_flags |= SOFT_RESET; + pCardInfo->si_mflags |= SOFT_RESET; if (i & 0x10) - pCardInfo->si_flags |= EXTENDED_TRANSLATION; + pCardInfo->si_mflags |= EXTENDED_TRANSLATION; if (ScamFlg & SCAM_ENABLED) - pCardInfo->si_flags |= FLAG_SCAM_ENABLED; + pCardInfo->si_mflags |= FLAG_SCAM_ENABLED; if (ScamFlg & SCAM_LEVEL2) - pCardInfo->si_flags |= FLAG_SCAM_LEVEL2; + pCardInfo->si_mflags |= FLAG_SCAM_LEVEL2; j = (RD_HARPOON(ioport + hp_bm_ctrl) & ~SCSI_TERM_ENA_L); if (i & 0x04) { @@ -1076,7 +1076,7 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo) if (!(RD_HARPOON(ioport + hp_page_ctrl) & NARROW_SCSI_CARD)) - pCardInfo->si_flags |= SUPPORT_16TAR_32LUN; + pCardInfo->si_mflags |= SUPPORT_16TAR_32LUN; pCardInfo->si_card_family = HARPOON_FAMILY; pCardInfo->si_bustype = BUSTYPE_PCI; @@ -1112,15 +1112,15 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo) if (pCardInfo->si_card_model[1] == '3') { if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7)) - pCardInfo->si_flags |= LOW_BYTE_TERM; + pCardInfo->si_mflags |= LOW_BYTE_TERM; } else if (pCardInfo->si_card_model[2] == '0') { temp = RD_HARPOON(ioport + hp_xfer_pad); WR_HARPOON(ioport + hp_xfer_pad, (temp & ~BIT(4))); if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7)) - pCardInfo->si_flags |= LOW_BYTE_TERM; + pCardInfo->si_mflags |= LOW_BYTE_TERM; WR_HARPOON(ioport + hp_xfer_pad, (temp | BIT(4))); if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7)) - pCardInfo->si_flags |= HIGH_BYTE_TERM; + pCardInfo->si_mflags |= HIGH_BYTE_TERM; WR_HARPOON(ioport + hp_xfer_pad, temp); } else { temp = RD_HARPOON(ioport + hp_ee_ctrl); @@ -1138,9 +1138,9 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo) WR_HARPOON(ioport + hp_ee_ctrl, temp); WR_HARPOON(ioport + hp_xfer_pad, temp2); if (!(temp3 & BIT(7))) - pCardInfo->si_flags |= LOW_BYTE_TERM; + pCardInfo->si_mflags |= LOW_BYTE_TERM; if (!(temp3 & BIT(6))) - pCardInfo->si_flags |= HIGH_BYTE_TERM; + pCardInfo->si_mflags |= HIGH_BYTE_TERM; } ARAM_ACCESS(ioport); @@ -1247,7 +1247,7 @@ static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info WR_HARPOON(ioport + hp_arb_id, pCardInfo->si_id); CurrCard->ourId = pCardInfo->si_id; - i = (unsigned char)pCardInfo->si_flags; + i = (unsigned char)pCardInfo->si_mflags; if (i & SCSI_PARITY_ENA) WR_HARPOON(ioport + hp_portctrl_1, (HOST_MODE8 | CHK_SCSI_P)); @@ -1261,14 +1261,14 @@ static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info j |= SCSI_TERM_ENA_H; WR_HARPOON(ioport + hp_ee_ctrl, j); - if (!(pCardInfo->si_flags & SOFT_RESET)) { + if (!(pCardInfo->si_mflags & SOFT_RESET)) { FPT_sresb(ioport, thisCard); FPT_scini(thisCard, pCardInfo->si_id, 0); } - if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS) + if (pCardInfo->si_mflags & POST_ALL_UNDERRRUNS) CurrCard->globalFlags |= F_NO_FILTER; if (pCurrNvRam) { diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c5612896cdb9..23e015af0e93 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -482,6 +482,7 @@ config SCSI_ARCMSR source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" +source "drivers/scsi/mpi3mr/Kconfig" source "drivers/scsi/smartpqi/Kconfig" source "drivers/scsi/ufs/Kconfig" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index bc3882f5cc69..06f2d5fab899 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/ +obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr/ obj-$(CONFIG_SCSI_UFSHCD) += ufs/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 0e935c49b57b..8aeaddc93b16 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -182,6 +182,7 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, struct beiscsi_endpoint *beiscsi_ep; struct iscsi_endpoint *ep; uint16_t cri_index; + int rc = 0; ep = iscsi_lookup_endpoint(transport_fd); if (!ep) @@ -189,15 +190,17 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, beiscsi_ep = ep->dd_data; - if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) - return -EINVAL; + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) { + rc = -EINVAL; + goto put_ep; + } if (beiscsi_ep->phba != phba) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : beiscsi_ep->hba=%p not equal to phba=%p\n", beiscsi_ep->phba, phba); - - return -EEXIST; + rc = -EEXIST; + goto put_ep; } cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid); if (phba->conn_table[cri_index]) { @@ -209,7 +212,8 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, beiscsi_ep->ep_cid, beiscsi_conn, phba->conn_table[cri_index]); - return -EINVAL; + rc = -EINVAL; + goto put_ep; } } @@ -226,7 +230,10 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, "BS_%d : cid %d phba->conn_table[%u]=%p\n", beiscsi_ep->ep_cid, cri_index, beiscsi_conn); phba->conn_table[cri_index] = beiscsi_conn; - return 0; + +put_ep: + iscsi_put_endpoint(ep); + return rc; } static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba) @@ -1293,7 +1300,6 @@ static int beiscsi_conn_close(struct beiscsi_endpoint *beiscsi_ep) void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) { struct beiscsi_endpoint *beiscsi_ep; - struct beiscsi_conn *beiscsi_conn; struct beiscsi_hba *phba; uint16_t cri_index; @@ -1312,11 +1318,6 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) return; } - if (beiscsi_ep->conn) { - beiscsi_conn = beiscsi_ep->conn; - iscsi_suspend_queue(beiscsi_conn->conn); - } - if (!beiscsi_hba_is_online(phba)) { beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index c15cc6c164d9..310b801c6c87 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -416,7 +416,7 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) "beiscsi_hba_alloc - iscsi_host_alloc failed\n"); return NULL; } - shost->max_id = BE2_MAX_SESSIONS; + shost->max_id = BE2_MAX_SESSIONS - 1; shost->max_channel = 0; shost->max_cmd_len = BEISCSI_MAX_CMD_LEN; shost->max_lun = BEISCSI_NUM_MAX_LUN; @@ -5316,7 +5316,7 @@ static int beiscsi_enable_port(struct beiscsi_hba *phba) /* Re-enable UER. If different TPE occurs then it is recoverable. */ beiscsi_set_uer_feature(phba); - phba->shost->max_id = phba->params.cxns_per_ctrl; + phba->shost->max_id = phba->params.cxns_per_ctrl - 1; phba->shost->can_queue = phba->params.ios_per_ctrl; ret = beiscsi_init_port(phba); if (ret < 0) { @@ -5807,6 +5807,7 @@ struct iscsi_transport beiscsi_iscsi_transport = { .destroy_session = beiscsi_session_destroy, .create_conn = beiscsi_conn_create, .bind_conn = beiscsi_conn_bind, + .unbind_conn = iscsi_conn_unbind, .destroy_conn = iscsi_conn_teardown, .attr_is_visible = beiscsi_attr_is_visible, .set_iface_param = beiscsi_iface_set_param, diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 1e6d8f62ea3c..1b5f3e143f07 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -791,7 +791,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) return NULL; shost->dma_boundary = cnic->pcidev->dma_mask; shost->transportt = bnx2i_scsi_xport_template; - shost->max_id = ISCSI_MAX_CONNS_PER_HBA; + shost->max_id = ISCSI_MAX_CONNS_PER_HBA - 1; shost->max_channel = 0; shost->max_lun = 512; shost->max_cmd_len = 16; @@ -1420,17 +1420,23 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, * Forcefully terminate all in progress connection recovery at the * earliest, either in bind(), send_pdu(LOGIN), or conn_start() */ - if (bnx2i_adapter_ready(hba)) - return -EIO; + if (bnx2i_adapter_ready(hba)) { + ret_code = -EIO; + goto put_ep; + } bnx2i_ep = ep->dd_data; if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) || - (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD)) + (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD)) { /* Peer disconnect via' FIN or RST */ - return -EINVAL; + ret_code = -EINVAL; + goto put_ep; + } - if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) - return -EINVAL; + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) { + ret_code = -EINVAL; + goto put_ep; + } if (bnx2i_ep->hba != hba) { /* Error - TCP connection does not belong to this device @@ -1441,7 +1447,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data, "belong to hba (%s)\n", hba->netdev->name); - return -EEXIST; + ret_code = -EEXIST; + goto put_ep; } bnx2i_ep->conn = bnx2i_conn; bnx2i_conn->ep = bnx2i_ep; @@ -1458,6 +1465,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, bnx2i_put_rq_buf(bnx2i_conn, 0); bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE); +put_ep: + iscsi_put_endpoint(ep); return ret_code; } @@ -2113,7 +2122,6 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) { struct bnx2i_endpoint *bnx2i_ep; struct bnx2i_conn *bnx2i_conn = NULL; - struct iscsi_conn *conn = NULL; struct bnx2i_hba *hba; bnx2i_ep = ep->dd_data; @@ -2126,11 +2134,8 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) !time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ))) msleep(250); - if (bnx2i_ep->conn) { + if (bnx2i_ep->conn) bnx2i_conn = bnx2i_ep->conn; - conn = bnx2i_conn->cls_conn->dd_data; - iscsi_suspend_queue(conn); - } hba = bnx2i_ep->hba; mutex_lock(&hba->net_dev_lock); @@ -2276,6 +2281,7 @@ struct iscsi_transport bnx2i_iscsi_transport = { .destroy_session = bnx2i_session_destroy, .create_conn = bnx2i_conn_create, .bind_conn = bnx2i_conn_bind, + .unbind_conn = iscsi_conn_unbind, .destroy_conn = bnx2i_conn_destroy, .attr_is_visible = bnx2i_attr_is_visible, .set_param = iscsi_set_param, diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 203f938fca7e..f949a4e00783 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -117,6 +117,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = { /* connection management */ .create_conn = cxgbi_create_conn, .bind_conn = cxgbi_bind_conn, + .unbind_conn = iscsi_conn_unbind, .destroy_conn = iscsi_tcp_conn_teardown, .start_conn = iscsi_conn_start, .stop_conn = iscsi_conn_stop, diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 2c3491528d42..efb3e2b3398e 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -134,6 +134,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = { /* connection management */ .create_conn = cxgbi_create_conn, .bind_conn = cxgbi_bind_conn, + .unbind_conn = iscsi_conn_unbind, .destroy_conn = iscsi_tcp_conn_teardown, .start_conn = iscsi_conn_start, .stop_conn = iscsi_conn_stop, diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index f078b3c4e083..8c7d4dda4cf2 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -337,7 +337,7 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev) EXPORT_SYMBOL_GPL(cxgbi_hbas_remove); int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun, - unsigned int max_id, struct scsi_host_template *sht, + unsigned int max_conns, struct scsi_host_template *sht, struct scsi_transport_template *stt) { struct cxgbi_hba *chba; @@ -357,7 +357,7 @@ int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun, shost->transportt = stt; shost->max_lun = max_lun; - shost->max_id = max_id; + shost->max_id = max_conns - 1; shost->max_channel = 0; shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE; @@ -2690,11 +2690,13 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session, err = csk->cdev->csk_ddp_setup_pgidx(csk, csk->tid, ppm->tformat.pgsz_idx_dflt); if (err < 0) - return err; + goto put_ep; err = iscsi_conn_bind(cls_session, cls_conn, is_leading); - if (err) - return -EINVAL; + if (err) { + err = -EINVAL; + goto put_ep; + } /* calculate the tag idx bits needed for this conn based on cmds_max */ cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1; @@ -2715,7 +2717,9 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session, /* init recv engine */ iscsi_tcp_hdr_recv_prep(tcp_conn); - return 0; +put_ep: + iscsi_put_endpoint(ep); + return err; } EXPORT_SYMBOL_GPL(cxgbi_bind_conn); @@ -2968,7 +2972,6 @@ void cxgbi_ep_disconnect(struct iscsi_endpoint *ep) ep, cep, cconn, csk, csk->state, csk->flags); if (cconn && cconn->iconn) { - iscsi_suspend_tx(cconn->iconn); write_lock_bh(&csk->callback_lock); cep->csk->user_data = NULL; cconn->cep = NULL; diff --git a/drivers/scsi/esas2r/atioctl.h b/drivers/scsi/esas2r/atioctl.h index 4aca3d52c851..ff2ad9b38575 100644 --- a/drivers/scsi/esas2r/atioctl.h +++ b/drivers/scsi/esas2r/atioctl.h @@ -1141,7 +1141,7 @@ struct __packed atto_ioctl_vda_gsv_cmd { u8 rsp_len; u8 reserved[7]; - u8 version_info[1]; + u8 version_info[]; #define ATTO_VDA_VER_UNSUPPORTED 0xFF }; diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 62062ed6cd9a..eeaec26ac324 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -709,8 +709,8 @@ isci_task_request_complete(struct isci_host *ihost, tmf->status = completion_status; if (tmf->proto == SAS_PROTOCOL_SSP) { - memcpy(&tmf->resp.resp_iu, - &ireq->ssp.rsp, + memcpy(tmf->resp.rsp_buf, + ireq->ssp.rsp_buf, SSP_RESP_IU_MAX_SIZE); } else if (tmf->proto == SAS_PROTOCOL_SATA) { memcpy(&tmf->resp.d2h_fis, diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index dd33ce0e3737..1bc37593c88f 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -600,6 +600,12 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) if (!sock) return; + /* + * Make sure we start socket shutdown now in case userspace is up + * but delayed in releasing the socket. + */ + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_hold(sock->sk); iscsi_sw_tcp_conn_restore_callbacks(conn); sock_put(sock->sk); @@ -689,6 +695,7 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ sk->sk_allocation = GFP_ATOMIC; sk_set_memalloc(sk); + sock_no_linger(sk); iscsi_sw_tcp_conn_set_callbacks(conn); tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage; diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 9e69422cb77a..4683c183e9d4 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -230,11 +230,11 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task) */ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) { - struct iscsi_conn *conn = task->conn; - struct iscsi_tm *tmf = &conn->tmhdr; + struct iscsi_session *session = task->conn->session; + struct iscsi_tm *tmf = &session->tmhdr; u64 hdr_lun; - if (conn->tmf_state == TMF_INITIAL) + if (session->tmf_state == TMF_INITIAL) return 0; if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) @@ -254,24 +254,19 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) * Fail all SCSI cmd PDUs */ if (opcode != ISCSI_OP_SCSI_DATA_OUT) { - iscsi_conn_printk(KERN_INFO, conn, - "task [op %x itt " - "0x%x/0x%x] " - "rejected.\n", - opcode, task->itt, - task->hdr_itt); + iscsi_session_printk(KERN_INFO, session, + "task [op %x itt 0x%x/0x%x] rejected.\n", + opcode, task->itt, task->hdr_itt); return -EACCES; } /* * And also all data-out PDUs in response to R2T * if fast_abort is set. */ - if (conn->session->fast_abort) { - iscsi_conn_printk(KERN_INFO, conn, - "task [op %x itt " - "0x%x/0x%x] fast abort.\n", - opcode, task->itt, - task->hdr_itt); + if (session->fast_abort) { + iscsi_session_printk(KERN_INFO, session, + "task [op %x itt 0x%x/0x%x] fast abort.\n", + opcode, task->itt, task->hdr_itt); return -EACCES; } break; @@ -284,7 +279,7 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) */ if (opcode == ISCSI_OP_SCSI_DATA_OUT && task->hdr_itt == tmf->rtt) { - ISCSI_DBG_SESSION(conn->session, + ISCSI_DBG_SESSION(session, "Preventing task %x/%x from sending " "data-out due to abort task in " "progress\n", task->itt, @@ -578,6 +573,11 @@ static bool cleanup_queued_task(struct iscsi_task *task) __iscsi_put_task(task); } + if (conn->session->running_aborted_task == task) { + conn->session->running_aborted_task = NULL; + __iscsi_put_task(task); + } + if (conn->task == task) { conn->task = NULL; __iscsi_put_task(task); @@ -933,20 +933,21 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) { struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; + struct iscsi_session *session = conn->session; conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; conn->tmfrsp_pdus_cnt++; - if (conn->tmf_state != TMF_QUEUED) + if (session->tmf_state != TMF_QUEUED) return; if (tmf->response == ISCSI_TMF_RSP_COMPLETE) - conn->tmf_state = TMF_SUCCESS; + session->tmf_state = TMF_SUCCESS; else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) - conn->tmf_state = TMF_NOT_FOUND; + session->tmf_state = TMF_NOT_FOUND; else - conn->tmf_state = TMF_FAILED; - wake_up(&conn->ehwait); + session->tmf_state = TMF_FAILED; + wake_up(&session->ehwait); } static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) @@ -1358,7 +1359,6 @@ void iscsi_session_failure(struct iscsi_session *session, enum iscsi_err err) { struct iscsi_conn *conn; - struct device *dev; spin_lock_bh(&session->frwd_lock); conn = session->leadconn; @@ -1367,10 +1367,8 @@ void iscsi_session_failure(struct iscsi_session *session, return; } - dev = get_device(&conn->cls_conn->dev); + iscsi_get_conn(conn->cls_conn); spin_unlock_bh(&session->frwd_lock); - if (!dev) - return; /* * if the host is being removed bypass the connection * recovery initialization because we are going to kill @@ -1380,27 +1378,36 @@ void iscsi_session_failure(struct iscsi_session *session, iscsi_conn_error_event(conn->cls_conn, err); else iscsi_conn_failure(conn, err); - put_device(dev); + iscsi_put_conn(conn->cls_conn); } EXPORT_SYMBOL_GPL(iscsi_session_failure); -void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) +static bool iscsi_set_conn_failed(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; - spin_lock_bh(&session->frwd_lock); - if (session->state == ISCSI_STATE_FAILED) { - spin_unlock_bh(&session->frwd_lock); - return; - } + if (session->state == ISCSI_STATE_FAILED) + return false; if (conn->stop_stage == 0) session->state = ISCSI_STATE_FAILED; - spin_unlock_bh(&session->frwd_lock); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); - iscsi_conn_error_event(conn->cls_conn, err); + return true; +} + +void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) +{ + struct iscsi_session *session = conn->session; + bool needs_evt; + + spin_lock_bh(&session->frwd_lock); + needs_evt = iscsi_set_conn_failed(conn); + spin_unlock_bh(&session->frwd_lock); + + if (needs_evt) + iscsi_conn_error_event(conn->cls_conn, err); } EXPORT_SYMBOL_GPL(iscsi_conn_failure); @@ -1817,15 +1824,14 @@ EXPORT_SYMBOL_GPL(iscsi_target_alloc); static void iscsi_tmf_timedout(struct timer_list *t) { - struct iscsi_conn *conn = from_timer(conn, t, tmf_timer); - struct iscsi_session *session = conn->session; + struct iscsi_session *session = from_timer(session, t, tmf_timer); spin_lock(&session->frwd_lock); - if (conn->tmf_state == TMF_QUEUED) { - conn->tmf_state = TMF_TIMEDOUT; + if (session->tmf_state == TMF_QUEUED) { + session->tmf_state = TMF_TIMEDOUT; ISCSI_DBG_EH(session, "tmf timedout\n"); /* unblock eh_abort() */ - wake_up(&conn->ehwait); + wake_up(&session->ehwait); } spin_unlock(&session->frwd_lock); } @@ -1848,8 +1854,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, return -EPERM; } conn->tmfcmd_pdus_cnt++; - conn->tmf_timer.expires = timeout * HZ + jiffies; - add_timer(&conn->tmf_timer); + session->tmf_timer.expires = timeout * HZ + jiffies; + add_timer(&session->tmf_timer); ISCSI_DBG_EH(session, "tmf set timeout\n"); spin_unlock_bh(&session->frwd_lock); @@ -1863,12 +1869,12 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, * 3) session is terminated or restarted or userspace has * given up on recovery */ - wait_event_interruptible(conn->ehwait, age != session->age || + wait_event_interruptible(session->ehwait, age != session->age || session->state != ISCSI_STATE_LOGGED_IN || - conn->tmf_state != TMF_QUEUED); + session->tmf_state != TMF_QUEUED); if (signal_pending(current)) flush_signals(current); - del_timer_sync(&conn->tmf_timer); + del_timer_sync(&session->tmf_timer); mutex_lock(&session->eh_mutex); spin_lock_bh(&session->frwd_lock); @@ -2177,6 +2183,51 @@ done: spin_unlock(&session->frwd_lock); } +/** + * iscsi_conn_unbind - prevent queueing to conn. + * @cls_conn: iscsi conn ep is bound to. + * @is_active: is the conn in use for boot or is this for EH/termination + * + * This must be called by drivers implementing the ep_disconnect callout. + * It disables queueing to the connection from libiscsi in preparation for + * an ep_disconnect call. + */ +void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active) +{ + struct iscsi_session *session; + struct iscsi_conn *conn; + + if (!cls_conn) + return; + + conn = cls_conn->dd_data; + session = conn->session; + /* + * Wait for iscsi_eh calls to exit. We don't wait for the tmf to + * complete or timeout. The caller just wants to know what's running + * is everything that needs to be cleaned up, and no cmds will be + * queued. + */ + mutex_lock(&session->eh_mutex); + + iscsi_suspend_queue(conn); + iscsi_suspend_tx(conn); + + spin_lock_bh(&session->frwd_lock); + if (!is_active) { + /* + * if logout timed out before userspace could even send a PDU + * the state might still be in ISCSI_STATE_LOGGED_IN and + * allowing new cmds and TMFs. + */ + if (session->state == ISCSI_STATE_LOGGED_IN) + iscsi_set_conn_failed(conn); + } + spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); +} +EXPORT_SYMBOL_GPL(iscsi_conn_unbind); + static void iscsi_prep_abort_task_pdu(struct iscsi_task *task, struct iscsi_tm *hdr) { @@ -2231,6 +2282,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) } conn = session->leadconn; + iscsi_get_conn(conn->cls_conn); conn->eh_abort_cnt++; age = session->age; @@ -2241,9 +2293,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) ISCSI_DBG_EH(session, "sc completed while abort in progress\n"); spin_unlock(&session->back_lock); - spin_unlock_bh(&session->frwd_lock); - mutex_unlock(&session->eh_mutex); - return SUCCESS; + goto success; } ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt); __iscsi_get_task(task); @@ -2255,17 +2305,17 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) } /* only have one tmf outstanding at a time */ - if (conn->tmf_state != TMF_INITIAL) + if (session->tmf_state != TMF_INITIAL) goto failed; - conn->tmf_state = TMF_QUEUED; + session->tmf_state = TMF_QUEUED; - hdr = &conn->tmhdr; + hdr = &session->tmhdr; iscsi_prep_abort_task_pdu(task, hdr); if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) goto failed; - switch (conn->tmf_state) { + switch (session->tmf_state) { case TMF_SUCCESS: spin_unlock_bh(&session->frwd_lock); /* @@ -2280,18 +2330,19 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) */ spin_lock_bh(&session->frwd_lock); fail_scsi_task(task, DID_ABORT); - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; memset(hdr, 0, sizeof(*hdr)); spin_unlock_bh(&session->frwd_lock); iscsi_start_tx(conn); goto success_unlocked; case TMF_TIMEDOUT: + session->running_aborted_task = task; spin_unlock_bh(&session->frwd_lock); iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto failed_unlocked; case TMF_NOT_FOUND: - if (!sc->SCp.ptr) { - conn->tmf_state = TMF_INITIAL; + if (iscsi_task_is_completed(task)) { + session->tmf_state = TMF_INITIAL; memset(hdr, 0, sizeof(*hdr)); /* task completed before tmf abort response */ ISCSI_DBG_EH(session, "sc completed while abort in " @@ -2300,7 +2351,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) } fallthrough; default: - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; goto failed; } @@ -2310,6 +2361,7 @@ success_unlocked: ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n", sc, task->itt); iscsi_put_task(task); + iscsi_put_conn(conn->cls_conn); mutex_unlock(&session->eh_mutex); return SUCCESS; @@ -2318,7 +2370,15 @@ failed: failed_unlocked: ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc, task ? task->itt : 0); - iscsi_put_task(task); + /* + * The driver might be accessing the task so hold the ref. The conn + * stop cleanup will drop the ref after ep_disconnect so we know the + * driver's no longer touching the task. + */ + if (!session->running_aborted_task) + iscsi_put_task(task); + + iscsi_put_conn(conn->cls_conn); mutex_unlock(&session->eh_mutex); return FAILED; } @@ -2359,11 +2419,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) conn = session->leadconn; /* only have one tmf outstanding at a time */ - if (conn->tmf_state != TMF_INITIAL) + if (session->tmf_state != TMF_INITIAL) goto unlock; - conn->tmf_state = TMF_QUEUED; + session->tmf_state = TMF_QUEUED; - hdr = &conn->tmhdr; + hdr = &session->tmhdr; iscsi_prep_lun_reset_pdu(sc, hdr); if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, @@ -2372,7 +2432,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) goto unlock; } - switch (conn->tmf_state) { + switch (session->tmf_state) { case TMF_SUCCESS: break; case TMF_TIMEDOUT: @@ -2380,7 +2440,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto done; default: - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; goto unlock; } @@ -2392,7 +2452,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) spin_lock_bh(&session->frwd_lock); memset(hdr, 0, sizeof(*hdr)); fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; spin_unlock_bh(&session->frwd_lock); iscsi_start_tx(conn); @@ -2415,8 +2475,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) spin_lock_bh(&session->frwd_lock); if (session->state != ISCSI_STATE_LOGGED_IN) { session->state = ISCSI_STATE_RECOVERY_FAILED; - if (session->leadconn) - wake_up(&session->leadconn->ehwait); + wake_up(&session->ehwait); } spin_unlock_bh(&session->frwd_lock); } @@ -2437,7 +2496,6 @@ int iscsi_eh_session_reset(struct scsi_cmnd *sc) cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - conn = session->leadconn; mutex_lock(&session->eh_mutex); spin_lock_bh(&session->frwd_lock); @@ -2452,16 +2510,17 @@ failed: return FAILED; } + conn = session->leadconn; + iscsi_get_conn(conn->cls_conn); + spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); - /* - * we drop the lock here but the leadconn cannot be destoyed while - * we are in the scsi eh - */ + iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); + iscsi_put_conn(conn->cls_conn); ISCSI_DBG_EH(session, "wait for relogin\n"); - wait_event_interruptible(conn->ehwait, + wait_event_interruptible(session->ehwait, session->state == ISCSI_STATE_TERMINATE || session->state == ISCSI_STATE_LOGGED_IN || session->state == ISCSI_STATE_RECOVERY_FAILED); @@ -2522,11 +2581,11 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc) conn = session->leadconn; /* only have one tmf outstanding at a time */ - if (conn->tmf_state != TMF_INITIAL) + if (session->tmf_state != TMF_INITIAL) goto unlock; - conn->tmf_state = TMF_QUEUED; + session->tmf_state = TMF_QUEUED; - hdr = &conn->tmhdr; + hdr = &session->tmhdr; iscsi_prep_tgt_reset_pdu(sc, hdr); if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, @@ -2535,7 +2594,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc) goto unlock; } - switch (conn->tmf_state) { + switch (session->tmf_state) { case TMF_SUCCESS: break; case TMF_TIMEDOUT: @@ -2543,7 +2602,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc) iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); goto done; default: - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; goto unlock; } @@ -2555,7 +2614,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc) spin_lock_bh(&session->frwd_lock); memset(hdr, 0, sizeof(*hdr)); fail_scsi_tasks(conn, -1, DID_ERROR); - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; spin_unlock_bh(&session->frwd_lock); iscsi_start_tx(conn); @@ -2885,7 +2944,10 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->tt = iscsit; session->dd_data = cls_session->dd_data + sizeof(*session); + session->tmf_state = TMF_INITIAL; + timer_setup(&session->tmf_timer, iscsi_tmf_timedout, 0); mutex_init(&session->eh_mutex); + spin_lock_init(&session->frwd_lock); spin_lock_init(&session->back_lock); @@ -2936,10 +2998,9 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) struct module *owner = cls_session->transport->owner; struct Scsi_Host *shost = session->host; - iscsi_pool_free(&session->cmdpool); - iscsi_remove_session(cls_session); + iscsi_pool_free(&session->cmdpool); kfree(session->password); kfree(session->password_in); kfree(session->username); @@ -2989,7 +3050,6 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, conn->c_stage = ISCSI_CONN_INITIAL_STAGE; conn->id = conn_idx; conn->exp_statsn = 0; - conn->tmf_state = TMF_INITIAL; timer_setup(&conn->transport_timer, iscsi_check_transport_timeouts, 0); @@ -3014,8 +3074,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, goto login_task_data_alloc_fail; conn->login_task->data = conn->data = data; - timer_setup(&conn->tmf_timer, iscsi_tmf_timedout, 0); - init_waitqueue_head(&conn->ehwait); + init_waitqueue_head(&session->ehwait); return cls_conn; @@ -3050,7 +3109,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) * leading connection? then give up on recovery. */ session->state = ISCSI_STATE_TERMINATE; - wake_up(&conn->ehwait); + wake_up(&session->ehwait); } spin_unlock_bh(&session->frwd_lock); @@ -3125,7 +3184,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) * commands after successful recovery */ conn->stop_stage = 0; - conn->tmf_state = TMF_INITIAL; + session->tmf_state = TMF_INITIAL; session->age++; if (session->age == 16) session->age = 0; @@ -3139,7 +3198,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) spin_unlock_bh(&session->frwd_lock); iscsi_unblock_session(session->cls_session); - wake_up(&conn->ehwait); + wake_up(&session->ehwait); return 0; } EXPORT_SYMBOL_GPL(iscsi_conn_start); @@ -3233,7 +3292,7 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) spin_lock_bh(&session->frwd_lock); fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); fail_mgmt_tasks(session, conn); - memset(&conn->tmhdr, 0, sizeof(conn->tmhdr)); + memset(&session->tmhdr, 0, sizeof(session->tmhdr)); spin_unlock_bh(&session->frwd_lock); mutex_unlock(&session->eh_mutex); } diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index b5a765b73c76..7af2c23652b0 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -21,8 +21,8 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "07.714.04.00-rc1" -#define MEGASAS_RELDATE "Apr 14, 2020" +#define MEGASAS_VERSION "07.717.02.00-rc1" +#define MEGASAS_RELDATE "May 19, 2021" #define MEGASAS_MSIX_NAME_LEN 32 @@ -2262,6 +2262,15 @@ enum MR_PERF_MODE { (mode) == MR_LATENCY_PERF_MODE ? "Latency" : \ "Unknown") +enum MEGASAS_LD_TARGET_ID_STATUS { + LD_TARGET_ID_INITIAL, + LD_TARGET_ID_ACTIVE, + LD_TARGET_ID_DELETED, +}; + +#define MEGASAS_TARGET_ID(sdev) \ + (((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id) + struct megasas_instance { unsigned int *reply_map; @@ -2326,6 +2335,9 @@ struct megasas_instance { struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD]; u8 ld_ids[MEGASAS_MAX_LD_IDS]; + u8 ld_tgtid_status[MEGASAS_MAX_LD_IDS]; + u8 ld_ids_prev[MEGASAS_MAX_LD_IDS]; + u8 ld_ids_from_raidmap[MEGASAS_MAX_LD_IDS]; s8 init_id; u16 max_num_sge; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 181407888e58..ec10b2497310 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -141,6 +141,8 @@ static int megasas_register_aen(struct megasas_instance *instance, u32 seq_num, u32 class_locale_word); static void megasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev); +static void +megasas_set_ld_removed_by_fw(struct megasas_instance *instance); /* * PCI ID table for all supported controllers @@ -436,6 +438,12 @@ megasas_decode_evt(struct megasas_instance *instance) (class_locale.members.locale), format_class(class_locale.members.class), evt_detail->description); + + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "evt_detail.args.ld.target_id/index %d/%d\n", + evt_detail->args.ld.target_id, evt_detail->args.ld.ld_index); + } /* @@ -1779,6 +1787,7 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct megasas_instance *instance; struct MR_PRIV_DEVICE *mr_device_priv_data; + u32 ld_tgt_id; instance = (struct megasas_instance *) scmd->device->host->hostdata; @@ -1805,17 +1814,21 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) } } - if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { + mr_device_priv_data = scmd->device->hostdata; + if (!mr_device_priv_data || + (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } - mr_device_priv_data = scmd->device->hostdata; - if (!mr_device_priv_data) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; + if (MEGASAS_IS_LOGICAL(scmd->device)) { + ld_tgt_id = MEGASAS_TARGET_ID(scmd->device); + if (instance->ld_tgtid_status[ld_tgt_id] == LD_TARGET_ID_DELETED) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } } if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) @@ -2095,7 +2108,7 @@ static int megasas_slave_configure(struct scsi_device *sdev) static int megasas_slave_alloc(struct scsi_device *sdev) { - u16 pd_index = 0; + u16 pd_index = 0, ld_tgt_id; struct megasas_instance *instance ; struct MR_PRIV_DEVICE *mr_device_priv_data; @@ -2120,6 +2133,14 @@ scan_target: GFP_KERNEL); if (!mr_device_priv_data) return -ENOMEM; + + if (MEGASAS_IS_LOGICAL(sdev)) { + ld_tgt_id = MEGASAS_TARGET_ID(sdev); + instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_ACTIVE; + if (megasas_dbg_lvl & LD_PD_DEBUG) + sdev_printk(KERN_INFO, sdev, "LD target ID %d created.\n", ld_tgt_id); + } + sdev->hostdata = mr_device_priv_data; atomic_set(&mr_device_priv_data->r1_ldio_hint, @@ -2129,6 +2150,19 @@ scan_target: static void megasas_slave_destroy(struct scsi_device *sdev) { + u16 ld_tgt_id; + struct megasas_instance *instance; + + instance = megasas_lookup_instance(sdev->host->host_no); + + if (MEGASAS_IS_LOGICAL(sdev)) { + ld_tgt_id = MEGASAS_TARGET_ID(sdev); + instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_DELETED; + if (megasas_dbg_lvl & LD_PD_DEBUG) + sdev_printk(KERN_INFO, sdev, + "LD target ID %d removed from OS stack\n", ld_tgt_id); + } + kfree(sdev->hostdata); sdev->hostdata = NULL; } @@ -3525,6 +3559,22 @@ megasas_complete_abort(struct megasas_instance *instance, } } +static void +megasas_set_ld_removed_by_fw(struct megasas_instance *instance) +{ + uint i; + + for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) { + if (instance->ld_ids_prev[i] != 0xff && + instance->ld_ids_from_raidmap[i] == 0xff) { + if (megasas_dbg_lvl & LD_PD_DEBUG) + dev_info(&instance->pdev->dev, + "LD target ID %d removed from RAID map\n", i); + instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED; + } + } +} + /** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state @@ -3685,9 +3735,13 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, fusion->fast_path_io = 0; } + if (instance->adapter_type >= INVADER_SERIES) + megasas_set_ld_removed_by_fw(instance); + megasas_sync_map_info(instance); spin_unlock_irqrestore(instance->host->host_lock, flags); + break; } if (opcode == MR_DCMD_CTRL_EVENT_GET_INFO || @@ -7543,11 +7597,16 @@ static int megasas_probe_one(struct pci_dev *pdev, return 0; fail_start_aen: + instance->unload = 1; + scsi_remove_host(instance->host); fail_io_attach: megasas_mgmt_info.count--; megasas_mgmt_info.max_index--; megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; + if (instance->requestorId && !instance->skip_heartbeat_timer_del) + del_timer_sync(&instance->sriov_heartbeat_timer); + instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); @@ -7555,8 +7614,16 @@ fail_io_attach: megasas_release_fusion(instance); else megasas_release_mfi(instance); + if (instance->msix_vectors) pci_free_irq_vectors(instance->pdev); + instance->msix_vectors = 0; + + if (instance->fw_crash_state != UNAVAILABLE) + megasas_free_host_crash_buffer(instance); + + if (instance->adapter_type != MFI_SERIES) + megasas_fusion_stop_watchdog(instance); fail_init_mfi: scsi_host_put(host); fail_alloc_instance: @@ -8816,8 +8883,10 @@ megasas_aen_polling(struct work_struct *work) union megasas_evt_class_locale class_locale; int event_type = 0; u32 seq_num; + u16 ld_target_id; int error; u8 dcmd_ret = DCMD_SUCCESS; + struct scsi_device *sdev1; if (!instance) { printk(KERN_ERR "invalid instance!\n"); @@ -8840,12 +8909,23 @@ megasas_aen_polling(struct work_struct *work) break; case MR_EVT_LD_OFFLINE: - case MR_EVT_CFG_CLEARED: case MR_EVT_LD_DELETED: + ld_target_id = instance->evt_detail->args.ld.target_id; + sdev1 = scsi_device_lookup(instance->host, + MEGASAS_MAX_PD_CHANNELS + + (ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL), + (ld_target_id - MEGASAS_MAX_DEV_PER_CHANNEL), + 0); + if (sdev1) + megasas_remove_scsi_device(sdev1); + + event_type = SCAN_VD_CHANNEL; + break; case MR_EVT_LD_CREATED: event_type = SCAN_VD_CHANNEL; break; + case MR_EVT_CFG_CLEARED: case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: case MR_EVT_FOREIGN_CFG_IMPORTED: case MR_EVT_LD_STATE_CHANGE: diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index b6c08d620033..83f69c33b01a 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -349,6 +349,10 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id) num_lds = le16_to_cpu(drv_map->raidMap.ldCount); + memcpy(instance->ld_ids_prev, + instance->ld_ids_from_raidmap, + sizeof(instance->ld_ids_from_raidmap)); + memset(instance->ld_ids_from_raidmap, 0xff, MEGASAS_MAX_LD_IDS); /*Convert Raid capability values to CPU arch */ for (i = 0; (num_lds > 0) && (i < MAX_LOGICAL_DRIVES_EXT); i++) { ld = MR_TargetIdToLdGet(i, drv_map); @@ -359,7 +363,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id) raid = MR_LdRaidGet(ld, drv_map); le32_to_cpus((u32 *)&raid->capability); - + instance->ld_ids_from_raidmap[i] = i; num_lds--; } diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 1a4bd75229cf..06399c026a8d 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3202,6 +3202,8 @@ megasas_build_io_fusion(struct megasas_instance *instance, { int sge_count; u8 cmd_type; + u16 pd_index = 0; + u8 drive_type = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; struct MR_PRIV_DEVICE *mr_device_priv_data; mr_device_priv_data = scp->device->hostdata; @@ -3236,8 +3238,12 @@ megasas_build_io_fusion(struct megasas_instance *instance, megasas_build_syspd_fusion(instance, scp, cmd, true); break; case NON_READ_WRITE_SYSPDIO: - if (instance->secure_jbod_support || - mr_device_priv_data->is_tm_capable) + pd_index = MEGASAS_PD_INDEX(scp); + drive_type = instance->pd_list[pd_index].driveType; + if ((instance->secure_jbod_support || + mr_device_priv_data->is_tm_capable) || + (instance->adapter_type >= VENTURA_SERIES && + drive_type == TYPE_ENCLOSURE)) megasas_build_syspd_fusion(instance, scp, cmd, false); else megasas_build_syspd_fusion(instance, scp, cmd, true); @@ -3738,6 +3744,7 @@ static void megasas_sync_irqs(unsigned long instance_addr) if (irq_ctx->irq_poll_scheduled) { irq_ctx->irq_poll_scheduled = false; enable_irq(irq_ctx->os_irq); + complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx); } } } @@ -3769,6 +3776,7 @@ int megasas_irqpoll(struct irq_poll *irqpoll, int budget) irq_poll_complete(irqpoll); irq_ctx->irq_poll_scheduled = false; enable_irq(irq_ctx->os_irq); + complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx); } return num_entries; @@ -3785,6 +3793,7 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) { struct megasas_instance *instance = (struct megasas_instance *)instance_addr; + struct megasas_irq_context *irq_ctx = NULL; u32 count, MSIxIndex; count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; @@ -3793,8 +3802,10 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) return; - for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) - complete_cmd_fusion(instance, MSIxIndex, NULL); + for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) { + irq_ctx = &instance->irq_context[MSIxIndex]; + complete_cmd_fusion(instance, MSIxIndex, irq_ctx); + } } /** @@ -5265,6 +5276,7 @@ megasas_alloc_fusion_context(struct megasas_instance *instance) if (!fusion->log_to_span) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); + kfree(instance->ctrl_context); return -ENOMEM; } } diff --git a/drivers/scsi/mpi3mr/Kconfig b/drivers/scsi/mpi3mr/Kconfig new file mode 100644 index 000000000000..f7882375e74f --- /dev/null +++ b/drivers/scsi/mpi3mr/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config SCSI_MPI3MR + tristate "Broadcom MPI3 Storage Controller Device Driver" + depends on PCI && SCSI + help + MPI3 based Storage & RAID Controllers Driver. diff --git a/drivers/scsi/mpi3mr/Makefile b/drivers/scsi/mpi3mr/Makefile new file mode 100644 index 000000000000..7c2063e04c81 --- /dev/null +++ b/drivers/scsi/mpi3mr/Makefile @@ -0,0 +1,4 @@ +# mpi3mr makefile +obj-m += mpi3mr.o +mpi3mr-y += mpi3mr_os.o \ + mpi3mr_fw.o \ diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h new file mode 100644 index 000000000000..d43bbecef651 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h @@ -0,0 +1,1880 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2017-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_CNFG_H +#define MPI30_CNFG_H 1 +#define MPI3_CONFIG_PAGETYPE_IO_UNIT (0x00) +#define MPI3_CONFIG_PAGETYPE_MANUFACTURING (0x01) +#define MPI3_CONFIG_PAGETYPE_IOC (0x02) +#define MPI3_CONFIG_PAGETYPE_UEFI_BSD (0x03) +#define MPI3_CONFIG_PAGETYPE_SECURITY (0x04) +#define MPI3_CONFIG_PAGETYPE_ENCLOSURE (0x11) +#define MPI3_CONFIG_PAGETYPE_DEVICE (0x12) +#define MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT (0x20) +#define MPI3_CONFIG_PAGETYPE_SAS_EXPANDER (0x21) +#define MPI3_CONFIG_PAGETYPE_SAS_PHY (0x23) +#define MPI3_CONFIG_PAGETYPE_SAS_PORT (0x24) +#define MPI3_CONFIG_PAGETYPE_PCIE_IO_UNIT (0x30) +#define MPI3_CONFIG_PAGETYPE_PCIE_SWITCH (0x31) +#define MPI3_CONFIG_PAGETYPE_PCIE_LINK (0x33) +#define MPI3_CONFIG_PAGEATTR_MASK (0xf0) +#define MPI3_CONFIG_PAGEATTR_READ_ONLY (0x00) +#define MPI3_CONFIG_PAGEATTR_CHANGEABLE (0x10) +#define MPI3_CONFIG_PAGEATTR_PERSISTENT (0x20) +#define MPI3_CONFIG_ACTION_PAGE_HEADER (0x00) +#define MPI3_CONFIG_ACTION_READ_DEFAULT (0x01) +#define MPI3_CONFIG_ACTION_READ_CURRENT (0x02) +#define MPI3_CONFIG_ACTION_WRITE_CURRENT (0x03) +#define MPI3_CONFIG_ACTION_READ_PERSISTENT (0x04) +#define MPI3_CONFIG_ACTION_WRITE_PERSISTENT (0x05) +#define MPI3_DEVICE_PGAD_FORM_MASK (0xf0000000) +#define MPI3_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI3_DEVICE_PGAD_FORM_HANDLE (0x20000000) +#define MPI3_DEVICE_PGAD_HANDLE_MASK (0x0000ffff) +#define MPI3_SAS_EXPAND_PGAD_FORM_MASK (0xf0000000) +#define MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM (0x10000000) +#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE (0x20000000) +#define MPI3_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00ff0000) +#define MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16) +#define MPI3_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000ffff) +#define MPI3_SAS_PHY_PGAD_FORM_MASK (0xf0000000) +#define MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000) +#define MPI3_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000ff) +#define MPI3_SASPORT_PGAD_FORM_MASK (0xf0000000) +#define MPI3_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000) +#define MPI3_SASPORT_PGAD_FORM_PORT_NUM (0x10000000) +#define MPI3_SASPORT_PGAD_PORT_NUMBER_MASK (0x000000ff) +#define MPI3_ENCLOS_PGAD_FORM_MASK (0xf0000000) +#define MPI3_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI3_ENCLOS_PGAD_FORM_HANDLE (0x10000000) +#define MPI3_ENCLOS_PGAD_HANDLE_MASK (0x0000ffff) +#define MPI3_PCIE_SWITCH_PGAD_FORM_MASK (0xf0000000) +#define MPI3_PCIE_SWITCH_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE_PORT_NUM (0x10000000) +#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE (0x20000000) +#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_MASK (0x00ff0000) +#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_SHIFT (16) +#define MPI3_PCIE_SWITCH_PGAD_HANDLE_MASK (0x0000ffff) +#define MPI3_PCIE_LINK_PGAD_FORM_MASK (0xf0000000) +#define MPI3_PCIE_LINK_PGAD_FORM_GET_NEXT_LINK (0x00000000) +#define MPI3_PCIE_LINK_PGAD_FORM_LINK_NUM (0x10000000) +#define MPI3_PCIE_LINK_PGAD_LINKNUM_MASK (0x000000ff) +#define MPI3_SECURITY_PGAD_FORM_MASK (0xf0000000) +#define MPI3_SECURITY_PGAD_FORM_GET_NEXT_SLOT (0x00000000) +#define MPI3_SECURITY_PGAD_FORM_SOT_NUM (0x10000000) +#define MPI3_SECURITY_PGAD_SLOT_GROUP_MASK (0x0000ff00) +#define MPI3_SECURITY_PGAD_SLOT_MASK (0x000000ff) +struct mpi3_config_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + u8 page_version; + u8 page_number; + u8 page_type; + u8 action; + __le32 page_address; + __le16 page_length; + __le16 reserved16; + __le32 reserved18[2]; + union mpi3_sge_union sgl; +}; + +struct mpi3_config_page_header { + u8 page_version; + u8 reserved01; + u8 page_number; + u8 page_attribute; + __le16 page_length; + u8 page_type; + u8 reserved07; +}; + +#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK (0xf0) +#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT (4) +#define MPI3_SAS_NEG_LINK_RATE_PHYSICAL_MASK (0x0f) +#define MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00) +#define MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01) +#define MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02) +#define MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) +#define MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) +#define MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) +#define MPI3_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06) +#define MPI3_SAS_NEG_LINK_RATE_1_5 (0x08) +#define MPI3_SAS_NEG_LINK_RATE_3_0 (0x09) +#define MPI3_SAS_NEG_LINK_RATE_6_0 (0x0a) +#define MPI3_SAS_NEG_LINK_RATE_12_0 (0x0b) +#define MPI3_SAS_NEG_LINK_RATE_22_5 (0x0c) +#define MPI3_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040) +#define MPI3_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020) +#define MPI3_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010) +#define MPI3_SAS_APHYINFO_REASON_MASK (0x0000000f) +#define MPI3_SAS_APHYINFO_REASON_UNKNOWN (0x00000000) +#define MPI3_SAS_APHYINFO_REASON_POWER_ON (0x00000001) +#define MPI3_SAS_APHYINFO_REASON_HARD_RESET (0x00000002) +#define MPI3_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003) +#define MPI3_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004) +#define MPI3_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005) +#define MPI3_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006) +#define MPI3_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007) +#define MPI3_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008) +#define MPI3_SAS_APHYINFO_REASON_EXP_REDUCED_FUNC (0x00000009) +#define MPI3_SAS_PHYINFO_STATUS_MASK (0xc0000000) +#define MPI3_SAS_PHYINFO_STATUS_SHIFT (30) +#define MPI3_SAS_PHYINFO_STATUS_ACCESSIBLE (0x00000000) +#define MPI3_SAS_PHYINFO_STATUS_NOT_EXIST (0x40000000) +#define MPI3_SAS_PHYINFO_STATUS_VACANT (0x80000000) +#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) +#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_ACTIVE (0x00000000) +#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_PARTIAL (0x08000000) +#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_SLUMBER (0x10000000) +#define MPI3_SAS_PHYINFO_REASON_MASK (0x000f0000) +#define MPI3_SAS_PHYINFO_REASON_UNKNOWN (0x00000000) +#define MPI3_SAS_PHYINFO_REASON_POWER_ON (0x00010000) +#define MPI3_SAS_PHYINFO_REASON_HARD_RESET (0x00020000) +#define MPI3_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000) +#define MPI3_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000) +#define MPI3_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000) +#define MPI3_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000) +#define MPI3_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000) +#define MPI3_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000) +#define MPI3_SAS_PHYINFO_REASON_EXP_REDUCED_FUNC (0x00090000) +#define MPI3_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000) +#define MPI3_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000) +#define MPI3_SAS_PHYINFO_VIRTUAL_PHY (0x00001000) +#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_MASK (0x00000f00) +#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_SHIFT (8) +#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_MASK (0x000000f0) +#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_DIRECT (0x00000000) +#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_SUBTRACTIVE (0x00000010) +#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_TABLE (0x00000020) +#define MPI3_SAS_PRATE_MAX_RATE_MASK (0xf0) +#define MPI3_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI3_SAS_PRATE_MAX_RATE_1_5 (0x80) +#define MPI3_SAS_PRATE_MAX_RATE_3_0 (0x90) +#define MPI3_SAS_PRATE_MAX_RATE_6_0 (0xa0) +#define MPI3_SAS_PRATE_MAX_RATE_12_0 (0xb0) +#define MPI3_SAS_PRATE_MAX_RATE_22_5 (0xc0) +#define MPI3_SAS_PRATE_MIN_RATE_MASK (0x0f) +#define MPI3_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI3_SAS_PRATE_MIN_RATE_1_5 (0x08) +#define MPI3_SAS_PRATE_MIN_RATE_3_0 (0x09) +#define MPI3_SAS_PRATE_MIN_RATE_6_0 (0x0a) +#define MPI3_SAS_PRATE_MIN_RATE_12_0 (0x0b) +#define MPI3_SAS_PRATE_MIN_RATE_22_5 (0x0c) +#define MPI3_SAS_HWRATE_MAX_RATE_MASK (0xf0) +#define MPI3_SAS_HWRATE_MAX_RATE_1_5 (0x80) +#define MPI3_SAS_HWRATE_MAX_RATE_3_0 (0x90) +#define MPI3_SAS_HWRATE_MAX_RATE_6_0 (0xa0) +#define MPI3_SAS_HWRATE_MAX_RATE_12_0 (0xb0) +#define MPI3_SAS_HWRATE_MAX_RATE_22_5 (0xc0) +#define MPI3_SAS_HWRATE_MIN_RATE_MASK (0x0f) +#define MPI3_SAS_HWRATE_MIN_RATE_1_5 (0x08) +#define MPI3_SAS_HWRATE_MIN_RATE_3_0 (0x09) +#define MPI3_SAS_HWRATE_MIN_RATE_6_0 (0x0a) +#define MPI3_SAS_HWRATE_MIN_RATE_12_0 (0x0b) +#define MPI3_SAS_HWRATE_MIN_RATE_22_5 (0x0c) +#define MPI3_SLOT_INVALID (0xffff) +#define MPI3_SLOT_INDEX_INVALID (0xffff) +struct mpi3_man_page0 { + struct mpi3_config_page_header header; + u8 chip_revision[8]; + u8 chip_name[32]; + u8 board_name[32]; + u8 board_assembly[32]; + u8 board_tracer_number[32]; + __le32 board_power; + __le32 reserved94; + __le32 reserved98; + u8 oem; + u8 sub_oem; + __le16 reserved9e; + u8 board_mfg_day; + u8 board_mfg_month; + __le16 board_mfg_year; + u8 board_rework_day; + u8 board_rework_month; + __le16 board_rework_year; + __le64 board_revision; + u8 e_pack_fru[16]; + u8 product_name[256]; +}; + +#define MPI3_MAN0_PAGEVERSION (0x00) +#define MPI3_MAN1_VPD_SIZE (512) +struct mpi3_man_page1 { + struct mpi3_config_page_header header; + __le32 reserved08[2]; + u8 vpd[MPI3_MAN1_VPD_SIZE]; +}; + +#define MPI3_MAN1_PAGEVERSION (0x00) +struct mpi3_man5_phy_entry { + __le64 ioc_wwid; + __le64 device_name; + __le64 sata_wwid; +}; + +#ifndef MPI3_MAN5_PHY_MAX +#define MPI3_MAN5_PHY_MAX (1) +#endif +struct mpi3_man_page5 { + struct mpi3_config_page_header header; + u8 num_phys; + u8 reserved09[3]; + __le32 reserved0c; + struct mpi3_man5_phy_entry phy[MPI3_MAN5_PHY_MAX]; +}; + +#define MPI3_MAN5_PAGEVERSION (0x00) +struct mpi3_man6_gpio_entry { + u8 function_code; + u8 reserved01; + __le16 flags; + u8 param1; + u8 param2; + __le16 reserved06; + __le32 param3; +}; + +#define MPI3_MAN6_GPIO_FUNCTION_GENERIC (0x00) +#define MPI3_MAN6_GPIO_FUNCTION_ALTERNATE (0x01) +#define MPI3_MAN6_GPIO_FUNCTION_EXT_INTERRUPT (0x02) +#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_ACTIVITY (0x03) +#define MPI3_MAN6_GPIO_FUNCTION_OVER_TEMPERATURE (0x04) +#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_GREEN (0x05) +#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_YELLOW (0x06) +#define MPI3_MAN6_GPIO_FUNCTION_CABLE_MANAGEMENT (0x07) +#define MPI3_MAN6_GPIO_FUNCTION_BKPLANE_MGMT_TYPE (0x08) +#define MPI3_MAN6_GPIO_FUNCTION_ISTWI_MUX_RESET (0x09) +#define MPI3_MAN6_GPIO_FUNCTION_ISTWI_RESET (0x0a) +#define MPI3_MAN6_GPIO_FUNCTION_BACKEND_PCIE_RESET (0x0b) +#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_FAULT (0x0c) +#define MPI3_MAN6_GPIO_FUNCTION_EPACK_ATTN (0x0d) +#define MPI3_MAN6_GPIO_FUNCTION_EPACK_ONLINE (0x0e) +#define MPI3_MAN6_GPIO_FUNCTION_EPACK_FAULT (0x0f) +#define MPI3_MAN6_GPIO_FUNCTION_CTRL_TYPE (0x10) +#define MPI3_MAN6_GPIO_FUNCTION_LICENSE (0x11) +#define MPI3_MAN6_GPIO_FUNCTION_REFCLK_CONTROL (0x12) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_MASK (0xf0) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_GENERIC (0x00) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_CABLE_MGMT (0x10) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_ACTIVE_CABLE_OVERCURRENT (0x20) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_MASK (0x01) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_EDGE (0x00) +#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_LEVEL (0x01) +#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ALL_UP (0x00) +#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ONE_OR_MORE_UP (0x01) +#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_MODULE_PRESENT (0x00) +#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_ACTIVE_CABLE_ENABLE (0x01) +#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_CABLE_MGMT_ENABLE (0x02) +#define MPI3_MAN6_GPIO_ISTWI_MUX_RESET_PARAM2_SPEC_MUX (0x00) +#define MPI3_MAN6_GPIO_ISTWI_MUX_RESET_PARAM2_ALL_MUXES (0x01) +#define MPI3_MAN6_GPIO_LICENSE_PARAM1_TYPE_IBUTTON (0x00) +#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_MASK (0x0100) +#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_FAST_EDGE (0x0100) +#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_SLOW_EDGE (0x0000) +#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_MASK (0x00c0) +#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_100OHM (0x0000) +#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_66OHM (0x0040) +#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_50OHM (0x0080) +#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_33OHM (0x00c0) +#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_MASK (0x0030) +#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_SHIFT (4) +#define MPI3_MAN6_GPIO_FLAGS_ACTIVE_HIGH (0x0008) +#define MPI3_MAN6_GPIO_FLAGS_BI_DIR_ENABLED (0x0004) +#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_MASK (0x0003) +#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_INPUT (0x0000) +#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_DRAIN_OUTPUT (0x0001) +#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_SOURCE_OUTPUT (0x0002) +#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_PUSH_PULL_OUTPUT (0x0003) +#ifndef MPI3_MAN6_GPIO_MAX +#define MPI3_MAN6_GPIO_MAX (1) +#endif +struct mpi3_man_page6 { + struct mpi3_config_page_header header; + __le16 flags; + __le16 reserved0a; + u8 num_gpio; + u8 reserved0d[3]; + struct mpi3_man6_gpio_entry gpio[MPI3_MAN6_GPIO_MAX]; +}; + +#define MPI3_MAN6_PAGEVERSION (0x00) +#define MPI3_MAN6_FLAGS_HEARTBEAT_LED_DISABLED (0x0001) +struct mpi3_man7_receptacle_info { + __le32 name[4]; + u8 location; + u8 connector_type; + u8 ped_clk; + u8 connector_id; + __le32 reserved14; +}; + +#define MPI3_MAN7_LOCATION_UNKNOWN (0x00) +#define MPI3_MAN7_LOCATION_INTERNAL (0x01) +#define MPI3_MAN7_LOCATION_EXTERNAL (0x02) +#define MPI3_MAN7_LOCATION_VIRTUAL (0x03) +#define MPI3_MAN7_PEDCLK_ROUTING_MASK (0x10) +#define MPI3_MAN7_PEDCLK_ROUTING_DIRECT (0x00) +#define MPI3_MAN7_PEDCLK_ROUTING_CLOCK_BUFFER (0x10) +#define MPI3_MAN7_PEDCLK_ID_MASK (0x0f) +#ifndef MPI3_MAN7_RECEPTACLE_INFO_MAX +#define MPI3_MAN7_RECEPTACLE_INFO_MAX (1) +#endif +struct mpi3_man_page7 { + struct mpi3_config_page_header header; + __le32 flags; + u8 num_receptacles; + u8 reserved0d[3]; + __le32 enclosure_name[4]; + struct mpi3_man7_receptacle_info receptacle_info[MPI3_MAN7_RECEPTACLE_INFO_MAX]; +}; + +#define MPI3_MAN7_PAGEVERSION (0x00) +#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_MASK (0x01) +#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_0 (0x00) +#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_1 (0x01) +struct mpi3_man8_phy_info { + u8 receptacle_id; + u8 connector_lane; + __le16 reserved02; + __le16 slotx1; + __le16 slotx2; + __le16 slotx4; + __le16 reserved0a; + __le32 reserved0c; +}; + +#ifndef MPI3_MAN8_PHY_INFO_MAX +#define MPI3_MAN8_PHY_INFO_MAX (1) +#endif +struct mpi3_man_page8 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_phys; + u8 reserved0d[3]; + struct mpi3_man8_phy_info phy_info[MPI3_MAN8_PHY_INFO_MAX]; +}; + +#define MPI3_MAN8_PAGEVERSION (0x00) +struct mpi3_man9_rsrc_entry { + __le32 maximum; + __le32 decrement; + __le32 minimum; + __le32 actual; +}; + +enum mpi3_man9_resources { + MPI3_MAN9_RSRC_OUTSTANDING_REQS = 0, + MPI3_MAN9_RSRC_TARGET_CMDS = 1, + MPI3_MAN9_RSRC_SAS_TARGETS = 2, + MPI3_MAN9_RSRC_PCIE_TARGETS = 3, + MPI3_MAN9_RSRC_INITIATORS = 4, + MPI3_MAN9_RSRC_VDS = 5, + MPI3_MAN9_RSRC_ENCLOSURES = 6, + MPI3_MAN9_RSRC_ENCLOSURE_PHYS = 7, + MPI3_MAN9_RSRC_EXPANDERS = 8, + MPI3_MAN9_RSRC_PCIE_SWITCHES = 9, + MPI3_MAN9_RSRC_PDS = 10, + MPI3_MAN9_RSRC_HOST_PDS = 11, + MPI3_MAN9_RSRC_ADV_HOST_PDS = 12, + MPI3_MAN9_RSRC_RAID_PDS = 13, + MPI3_MAN9_RSRC_NUM_RESOURCES +}; + +#define MPI3_MAN9_MIN_OUTSTANDING_REQS (1) +#define MPI3_MAN9_MAX_OUTSTANDING_REQS (65000) +#define MPI3_MAN9_MIN_TARGET_CMDS (0) +#define MPI3_MAN9_MAX_TARGET_CMDS (65535) +#define MPI3_MAN9_MIN_SAS_TARGETS (0) +#define MPI3_MAN9_MAX_SAS_TARGETS (65535) +#define MPI3_MAN9_MIN_PCIE_TARGETS (0) +#define MPI3_MAN9_MIN_INITIATORS (0) +#define MPI3_MAN9_MAX_INITIATORS (65535) +#define MPI3_MAN9_MIN_ENCLOSURES (0) +#define MPI3_MAN9_MAX_ENCLOSURES (65535) +#define MPI3_MAN9_MIN_ENCLOSURE_PHYS (0) +#define MPI3_MAN9_MIN_EXPANDERS (0) +#define MPI3_MAN9_MAX_EXPANDERS (65535) +#define MPI3_MAN9_MIN_PCIE_SWITCHES (0) +struct mpi3_man_page9 { + struct mpi3_config_page_header header; + u8 num_resources; + u8 reserved09; + __le16 reserved0a; + __le32 reserved0c; + __le32 reserved10; + __le32 reserved14; + __le32 reserved18; + __le32 reserved1c; + struct mpi3_man9_rsrc_entry resource[MPI3_MAN9_RSRC_NUM_RESOURCES]; +}; + +#define MPI3_MAN9_PAGEVERSION (0x00) +struct mpi3_man10_istwi_ctrlr_entry { + __le16 slave_address; + __le16 flags; + __le32 reserved04; +}; + +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_SLAVE_ENABLED (0x0002) +#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_MASTER_ENABLED (0x0001) +#ifndef MPI3_MAN10_ISTWI_CTRLR_MAX +#define MPI3_MAN10_ISTWI_CTRLR_MAX (1) +#endif +struct mpi3_man_page10 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_istwi_ctrl; + u8 reserved0d[3]; + struct mpi3_man10_istwi_ctrlr_entry istwi_controller[MPI3_MAN10_ISTWI_CTRLR_MAX]; +}; + +#define MPI3_MAN10_PAGEVERSION (0x00) +struct mpi3_man11_mux_device_format { + u8 max_channel; + u8 reserved01[3]; + __le32 reserved04; +}; + +struct mpi3_man11_temp_sensor_device_format { + u8 type; + u8 reserved01[3]; + u8 temp_channel[4]; +}; + +#define MPI3_MAN11_TEMP_SENSOR_TYPE_MAX6654 (0x00) +#define MPI3_MAN11_TEMP_SENSOR_TYPE_EMC1442 (0x01) +#define MPI3_MAN11_TEMP_SENSOR_TYPE_ADT7476 (0x02) +#define MPI3_MAN11_TEMP_SENSOR_CHANNEL_ENABLED (0x01) +struct mpi3_man11_seeprom_device_format { + u8 size; + u8 page_write_size; + __le16 reserved02; + __le32 reserved04; +}; + +#define MPI3_MAN11_SEEPROM_SIZE_1KBITS (0x01) +#define MPI3_MAN11_SEEPROM_SIZE_2KBITS (0x02) +#define MPI3_MAN11_SEEPROM_SIZE_4KBITS (0x03) +#define MPI3_MAN11_SEEPROM_SIZE_8KBITS (0x04) +#define MPI3_MAN11_SEEPROM_SIZE_16KBITS (0x05) +#define MPI3_MAN11_SEEPROM_SIZE_32KBITS (0x06) +#define MPI3_MAN11_SEEPROM_SIZE_64KBITS (0x07) +#define MPI3_MAN11_SEEPROM_SIZE_128KBITS (0x08) +struct mpi3_man11_ddr_spd_device_format { + u8 channel; + u8 reserved01[3]; + __le32 reserved04; +}; + +struct mpi3_man11_cable_mgmt_device_format { + u8 type; + u8 receptacle_id; + __le16 reserved02; + __le32 reserved04; +}; + +#define MPI3_MAN11_CABLE_MGMT_TYPE_SFF_8636 (0x00) +struct mpi3_man11_bkplane_spec_ubm_format { + __le16 flags; + __le16 reserved02; +}; + +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0200) +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_FORCE_POLLING (0x0100) +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_MASK (0x00f0) +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_SHIFT (4) +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_MASK (0x000f) +#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_SHIFT (0) +struct mpi3_man11_bkplane_spec_vpp_format { + __le16 flags; + __le16 reserved02; +}; + +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0040) +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_MASK (0x0030) +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_GPIO (0x0000) +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_REG (0x0010) +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_POLL_INTERVAL_MASK (0x000f) +#define MPI3_MAN11_BKPLANE_VPP_FLAGS_POLL_INTERVAL_SHIFT (0) +union mpi3_man11_bkplane_spec_format { + struct mpi3_man11_bkplane_spec_ubm_format ubm; + struct mpi3_man11_bkplane_spec_vpp_format vpp; +}; + +struct mpi3_man11_bkplane_mgmt_device_format { + u8 type; + u8 receptacle_id; + __le16 reserved02; + union mpi3_man11_bkplane_spec_format backplane_mgmt_specific; +}; + +#define MPI3_MAN11_BKPLANE_MGMT_TYPE_UBM (0x00) +#define MPI3_MAN11_BKPLANE_MGMT_TYPE_VPP (0x01) +struct mpi3_man11_gas_gauge_device_format { + u8 type; + u8 reserved01[3]; + __le32 reserved04; +}; + +#define MPI3_MAN11_GAS_GAUGE_TYPE_STANDARD (0x00) +union mpi3_man11_device_specific_format { + struct mpi3_man11_mux_device_format mux; + struct mpi3_man11_temp_sensor_device_format temp_sensor; + struct mpi3_man11_seeprom_device_format seeprom; + struct mpi3_man11_ddr_spd_device_format ddr_spd; + struct mpi3_man11_cable_mgmt_device_format cable_mgmt; + struct mpi3_man11_bkplane_mgmt_device_format bkplane_mgmt; + struct mpi3_man11_gas_gauge_device_format gas_gauge; + __le32 words[2]; +}; + +struct mpi3_man11_istwi_device_format { + u8 device_type; + u8 controller; + u8 reserved02; + u8 flags; + __le16 device_address; + u8 mux_channel; + u8 mux_index; + union mpi3_man11_device_specific_format device_specific; +}; + +#define MPI3_MAN11_ISTWI_DEVTYPE_MUX (0x00) +#define MPI3_MAN11_ISTWI_DEVTYPE_TEMP_SENSOR (0x01) +#define MPI3_MAN11_ISTWI_DEVTYPE_SEEPROM (0x02) +#define MPI3_MAN11_ISTWI_DEVTYPE_DDR_SPD (0x03) +#define MPI3_MAN11_ISTWI_DEVTYPE_CABLE_MGMT (0x04) +#define MPI3_MAN11_ISTWI_DEVTYPE_BACKPLANE_MGMT (0x05) +#define MPI3_MAN11_ISTWI_DEVTYPE_GAS_GAUGE (0x06) +#define MPI3_MAN11_ISTWI_FLAGS_MUX_PRESENT (0x01) +#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_MASK (0x06) +#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_100KHZ (0x00) +#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_400KHZ (0x02) +#ifndef MPI3_MAN11_ISTWI_DEVICE_MAX +#define MPI3_MAN11_ISTWI_DEVICE_MAX (1) +#endif +struct mpi3_man_page11 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_istwi_dev; + u8 reserved0d[3]; + struct mpi3_man11_istwi_device_format istwi_device[MPI3_MAN11_ISTWI_DEVICE_MAX]; +}; + +#define MPI3_MAN11_PAGEVERSION (0x00) +#ifndef MPI3_MAN12_NUM_SGPIO_MAX +#define MPI3_MAN12_NUM_SGPIO_MAX (1) +#endif +struct mpi3_man12_sgpio_info { + u8 slot_count; + u8 reserved01[3]; + __le32 reserved04; + u8 phy_order[32]; +}; + +struct mpi3_man_page12 { + struct mpi3_config_page_header header; + __le32 flags; + __le32 s_clock_freq; + __le32 activity_modulation; + u8 num_sgpio; + u8 reserved15[3]; + __le32 reserved18; + __le32 reserved1c; + __le32 pattern[8]; + struct mpi3_man12_sgpio_info sgpio_info[MPI3_MAN12_NUM_SGPIO_MAX]; +}; + +#define MPI3_MAN12_PAGEVERSION (0x00) +#define MPI3_MAN12_FLAGS_ERROR_PRESENCE_ENABLED (0x0400) +#define MPI3_MAN12_FLAGS_ACTIVITY_INVERT_ENABLED (0x0200) +#define MPI3_MAN12_FLAGS_GROUP_ID_DISABLED (0x0100) +#define MPI3_MAN12_FLAGS_SIO_CLK_FILTER_ENABLED (0x0004) +#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_MASK (0x0002) +#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_PUSH_PULL (0x0000) +#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_OPEN_DRAIN (0x0002) +#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_MASK (0x0001) +#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_PUSH_PULL (0x0000) +#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_OPEN_DRAIN (0x0001) +#define MPI3_MAN12_SIO_CLK_FREQ_MIN (32) +#define MPI3_MAN12_SIO_CLK_FREQ_MAX (100000) +#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_MASK (0x0000f000) +#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_SHIFT (12) +#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_MASK (0x00000f00) +#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_SHIFT (8) +#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_MASK (0x000000f0) +#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_SHIFT (4) +#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_MASK (0x0000000f) +#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_SHIFT (0) +#define MPI3_MAN12_PATTERN_RATE_MASK (0xe0000000) +#define MPI3_MAN12_PATTERN_RATE_2_HZ (0x00000000) +#define MPI3_MAN12_PATTERN_RATE_4_HZ (0x20000000) +#define MPI3_MAN12_PATTERN_RATE_8_HZ (0x40000000) +#define MPI3_MAN12_PATTERN_RATE_16_HZ (0x60000000) +#define MPI3_MAN12_PATTERN_RATE_10_HZ (0x80000000) +#define MPI3_MAN12_PATTERN_RATE_20_HZ (0xa0000000) +#define MPI3_MAN12_PATTERN_RATE_40_HZ (0xc0000000) +#define MPI3_MAN12_PATTERN_LENGTH_MASK (0x1f000000) +#define MPI3_MAN12_PATTERN_LENGTH_SHIFT (24) +#define MPI3_MAN12_PATTERN_BIT_PATTERN_MASK (0x00ffffff) +#define MPI3_MAN12_PATTERN_BIT_PATTERN_SHIFT (0) +#ifndef MPI3_MAN13_NUM_TRANSLATION_MAX +#define MPI3_MAN13_NUM_TRANSLATION_MAX (1) +#endif +struct mpi3_man13_translation_info { + __le32 slot_status; + __le32 mask; + u8 activity; + u8 locate; + u8 error; + u8 reserved0b; +}; + +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_FAULT (0x20000000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_OFF (0x10000000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_ACTIVITY (0x00800000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DO_NOT_REMOVE (0x00400000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_MISSING (0x00100000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_INSERT (0x00080000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REMOVAL (0x00040000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IDENTIFY (0x00020000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_OK (0x00008000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_RESERVED_DEVICE (0x00004000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_HOT_SPARE (0x00002000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_CONSISTENCY_CHECK (0x00001000) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000800) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_FAILED_ARRAY (0x00000400) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP (0x00000200) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP_ABORT (0x00000100) +#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_PREDICTED_FAILURE (0x00000040) +#define MPI3_MAN13_BLINK_PATTERN_FORCE_OFF (0x00) +#define MPI3_MAN13_BLINK_PATTERN_FORCE_ON (0x01) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_0 (0x02) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_1 (0x03) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_2 (0x04) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_3 (0x05) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_4 (0x06) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_5 (0x07) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_6 (0x08) +#define MPI3_MAN13_BLINK_PATTERN_PATTERN_7 (0x09) +#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY (0x0a) +#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY_TRAIL (0x0b) +struct mpi3_man_page13 { + struct mpi3_config_page_header header; + u8 num_trans; + u8 reserved09[3]; + __le32 reserved0c; + struct mpi3_man13_translation_info translation[MPI3_MAN13_NUM_TRANSLATION_MAX]; +}; + +#define MPI3_MAN13_PAGEVERSION (0x00) +struct mpi3_man_page14 { + struct mpi3_config_page_header header; + __le16 flags; + __le16 reserved0a; + u8 num_slot_groups; + u8 num_slots; + __le16 max_cert_chain_length; + __le32 sealed_slots; +}; + +#define MPI3_MAN14_PAGEVERSION (0x00) +#define MPI3_MAN14_FLAGS_AUTH_SESSION_REQ (0x01) +#define MPI3_MAN14_FLAGS_AUTH_API_MASK (0x0e) +#define MPI3_MAN14_FLAGS_AUTH_API_NONE (0x00) +#define MPI3_MAN14_FLAGS_AUTH_API_CEREBUS (0x02) +#define MPI3_MAN14_FLAGS_AUTH_API_DMTF_PMCI (0x04) +#ifndef MPI3_MAN15_VERSION_RECORD_MAX +#define MPI3_MAN15_VERSION_RECORD_MAX 1 +#endif +struct mpi3_man15_version_record { + __le16 spdm_version; + __le16 reserved02; +}; + +struct mpi3_man_page15 { + struct mpi3_config_page_header header; + u8 num_version_records; + u8 reserved09[3]; + __le32 reserved0c; + struct mpi3_man15_version_record version_record[MPI3_MAN15_VERSION_RECORD_MAX]; +}; + +#define MPI3_MAN15_PAGEVERSION (0x00) +#ifndef MPI3_MAN16_CERT_ALGO_MAX +#define MPI3_MAN16_CERT_ALGO_MAX 1 +#endif +struct mpi3_man16_certificate_algorithm { + u8 slot_group; + u8 reserved01[3]; + __le32 base_asym_algo; + __le32 base_hash_algo; + __le32 reserved0c[3]; +}; + +struct mpi3_man_page16 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_cert_algos; + u8 reserved0d[3]; + struct mpi3_man16_certificate_algorithm certificate_algorithm[MPI3_MAN16_CERT_ALGO_MAX]; +}; + +#define MPI3_MAN16_PAGEVERSION (0x00) +#ifndef MPI3_MAN17_HASH_ALGORITHM_MAX +#define MPI3_MAN17_HASH_ALGORITHM_MAX 1 +#endif +struct mpi3_man17_hash_algorithm { + u8 meas_specification; + u8 reserved01[3]; + __le32 measurement_hash_algo; + __le32 reserved08[2]; +}; + +struct mpi3_man_page17 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_hash_algos; + u8 reserved0d[3]; + struct mpi3_man17_hash_algorithm hash_algorithm[MPI3_MAN17_HASH_ALGORITHM_MAX]; +}; + +#define MPI3_MAN17_PAGEVERSION (0x00) +struct mpi3_man_page20 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le32 nonpremium_features; + u8 allowed_personalities; + u8 reserved11[3]; +}; + +#define MPI3_MAN20_PAGEVERSION (0x00) +#define MPI3_MAN20_ALLOWEDPERSON_RAID_MASK (0x02) +#define MPI3_MAN20_ALLOWEDPERSON_RAID_ALLOWED (0x02) +#define MPI3_MAN20_ALLOWEDPERSON_RAID_NOT_ALLOWED (0x00) +#define MPI3_MAN20_ALLOWEDPERSON_EHBA_MASK (0x01) +#define MPI3_MAN20_ALLOWEDPERSON_EHBA_ALLOWED (0x01) +#define MPI3_MAN20_ALLOWEDPERSON_EHBA_NOT_ALLOWED (0x00) +#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_MASK (0x01) +#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_ENABLED (0x00) +#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_DISABLED (0x01) +struct mpi3_man_page21 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le32 flags; +}; + +#define MPI3_MAN21_PAGEVERSION (0x00) +#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_MASK (0x80) +#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_ENABLED (0x80) +#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_DISABLED (0x00) +#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_MASK (0x60) +#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_BLOCK (0x00) +#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_ALLOW (0x20) +#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_WARN (0x40) +#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_MASK (0x08) +#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_ALLOW (0x00) +#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_PREVENT (0x08) +#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_MASK (0x01) +#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_DEFAULT (0x00) +#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_OEM_SPECIFIC (0x01) +#ifndef MPI3_MAN_PROD_SPECIFIC_MAX +#define MPI3_MAN_PROD_SPECIFIC_MAX (1) +#endif +struct mpi3_man_page_product_specific { + struct mpi3_config_page_header header; + __le32 product_specific_info[MPI3_MAN_PROD_SPECIFIC_MAX]; +}; + +struct mpi3_io_unit_page0 { + struct mpi3_config_page_header header; + __le64 unique_value; + __le32 nvdata_version_default; + __le32 nvdata_version_persistent; +}; + +#define MPI3_IOUNIT0_PAGEVERSION (0x00) +struct mpi3_io_unit_page1 { + struct mpi3_config_page_header header; + __le32 flags; + u8 dmd_io_delay; + u8 dmd_report_pc_ie; + u8 dmd_report_sata; + u8 dmd_report_sas; +}; + +#define MPI3_IOUNIT1_PAGEVERSION (0x00) +#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_MASK (0x00000030) +#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_ENABLE (0x00000000) +#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_DISABLE (0x00000010) +#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_NO_MODIFY (0x00000020) +#define MPI3_IOUNIT1_FLAGS_ATA_SECURITY_FREEZE_LOCK (0x00000008) +#define MPI3_IOUNIT1_FLAGS_WRITE_SAME_BUFFER (0x00000004) +#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_MASK (0x00000003) +#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_ENABLE (0x00000000) +#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_DISABLE (0x00000001) +#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_UNCHANGED (0x00000002) +#define MPI3_IOUNIT1_DMD_REPORT_DELAY_TIME_MASK (0x7f) +#define MPI3_IOUNIT1_DMD_REPORT_UNIT_16_SEC (0x80) +#ifndef MPI3_IO_UNIT2_GPIO_VAL_MAX +#define MPI3_IO_UNIT2_GPIO_VAL_MAX (1) +#endif +struct mpi3_io_unit_page2 { + struct mpi3_config_page_header header; + u8 gpio_count; + u8 reserved09[3]; + __le16 gpio_val[MPI3_IO_UNIT2_GPIO_VAL_MAX]; +}; + +#define MPI3_IOUNIT2_PAGEVERSION (0x00) +#define MPI3_IOUNIT2_GPIO_FUNCTION_MASK (0xfffc) +#define MPI3_IOUNIT2_GPIO_FUNCTION_SHIFT (2) +#define MPI3_IOUNIT2_GPIO_SETTING_MASK (0x0001) +#define MPI3_IOUNIT2_GPIO_SETTING_OFF (0x0000) +#define MPI3_IOUNIT2_GPIO_SETTING_ON (0x0001) +struct mpi3_io_unit3_sensor { + __le16 flags; + __le16 reserved02; + __le16 threshold[4]; + __le32 reserved0c; + __le32 reserved10; + __le32 reserved14; +}; + +#define MPI3_IOUNIT3_SENSOR_FLAGS_T3_ENABLE (0x0008) +#define MPI3_IOUNIT3_SENSOR_FLAGS_T2_ENABLE (0x0004) +#define MPI3_IOUNIT3_SENSOR_FLAGS_T1_ENABLE (0x0002) +#define MPI3_IOUNIT3_SENSOR_FLAGS_T0_ENABLE (0x0001) +#ifndef MPI3_IO_UNIT3_SENSOR_MAX +#define MPI3_IO_UNIT3_SENSOR_MAX (1) +#endif +struct mpi3_io_unit_page3 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_sensors; + u8 polling_interval; + __le16 reserved0e; + struct mpi3_io_unit3_sensor sensor[MPI3_IO_UNIT3_SENSOR_MAX]; +}; + +#define MPI3_IOUNIT3_PAGEVERSION (0x00) +struct mpi3_io_unit4_sensor { + __le16 current_temperature; + __le16 reserved02; + u8 flags; + u8 reserved05[3]; + __le32 reserved08; + __le32 reserved0c; +}; + +#define MPI3_IOUNIT4_SENSOR_FLAGS_TEMP_VALID (0x01) +#ifndef MPI3_IO_UNIT4_SENSOR_MAX +#define MPI3_IO_UNIT4_SENSOR_MAX (1) +#endif +struct mpi3_io_unit_page4 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_sensors; + u8 reserved0d[3]; + struct mpi3_io_unit4_sensor sensor[MPI3_IO_UNIT4_SENSOR_MAX]; +}; + +#define MPI3_IOUNIT4_PAGEVERSION (0x00) +struct mpi3_io_unit5_spinup_group { + u8 max_target_spinup; + u8 spinup_delay; + u8 spinup_flags; + u8 reserved03; +}; + +#define MPI3_IOUNIT5_SPINUP_FLAGS_DISABLE (0x01) +#ifndef MPI3_IO_UNIT5_PHY_MAX +#define MPI3_IO_UNIT5_PHY_MAX (4) +#endif +struct mpi3_io_unit_page5 { + struct mpi3_config_page_header header; + struct mpi3_io_unit5_spinup_group spinup_group_parameters[4]; + __le32 reserved18; + __le32 reserved1c; + __le32 reserved20; + u8 reserved24; + u8 sata_device_wait_time; + u8 spinup_encl_drive_count; + u8 spinup_encl_delay; + u8 num_phys; + u8 pe_initial_spinup_delay; + u8 topology_stable_time; + u8 flags; + u8 phy[MPI3_IO_UNIT5_PHY_MAX]; +}; + +#define MPI3_IOUNIT5_PAGEVERSION (0x00) +#define MPI3_IOUNIT5_FLAGS_POWER_CAPABLE_SPINUP (0x02) +#define MPI3_IOUNIT5_FLAGS_AUTO_PORT_ENABLE (0x01) +#define MPI3_IOUNIT5_PHY_SPINUP_GROUP_MASK (0x03) +struct mpi3_io_unit_page6 { + struct mpi3_config_page_header header; + __le32 board_power_requirement; + __le32 pci_slot_power_allocation; + u8 flags; + u8 reserved11[3]; +}; + +#define MPI3_IOUNIT6_PAGEVERSION (0x00) +#define MPI3_IOUNIT6_FLAGS_ACT_CABLE_PWR_EXC (0x01) +struct mpi3_io_unit_page7 { + struct mpi3_config_page_header header; + __le32 reserved08; +}; + +#define MPI3_IOUNIT7_PAGEVERSION (0x00) +#ifndef MPI3_IOUNIT8_DIGEST_MAX +#define MPI3_IOUNIT8_DIGEST_MAX (1) +#endif +union mpi3_iounit8_digest { + __le32 dword[16]; + __le16 word[32]; + u8 byte[64]; +}; + +struct mpi3_io_unit_page8 { + struct mpi3_config_page_header header; + u8 sb_mode; + u8 sb_state; + __le16 reserved0a; + u8 num_slots; + u8 slots_available; + u8 current_key_encryption_algo; + u8 key_digest_hash_algo; + __le32 reserved10[2]; + __le32 current_key[128]; + union mpi3_iounit8_digest digest[MPI3_IOUNIT8_DIGEST_MAX]; +}; + +#define MPI3_IOUNIT8_PAGEVERSION (0x00) +#define MPI3_IOUNIT8_SBMODE_SECURE_DEBUG (0x04) +#define MPI3_IOUNIT8_SBMODE_HARD_SECURE (0x02) +#define MPI3_IOUNIT8_SBMODE_CONFIG_SECURE (0x01) +#define MPI3_IOUNIT8_SBSTATE_KEY_UPDATE_PENDING (0x02) +#define MPI3_IOUNIT8_SBSTATE_SECURE_BOOT_ENABLED (0x01) +struct mpi3_io_unit_page9 { + struct mpi3_config_page_header header; + __le32 flags; + __le16 first_device; + __le16 reserved0e; +}; + +#define MPI3_IOUNIT9_PAGEVERSION (0x00) +#define MPI3_IOUNIT9_FLAGS_VDFIRST_ENABLED (0x01) +#define MPI3_IOUNIT9_FIRSTDEVICE_UNKNOWN (0xffff) +struct mpi3_ioc_page0 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le16 vendor_id; + __le16 device_id; + u8 revision_id; + u8 reserved11[3]; + __le32 class_code; + __le16 subsystem_vendor_id; + __le16 subsystem_id; +}; + +#define MPI3_IOC0_PAGEVERSION (0x00) +struct mpi3_ioc_page1 { + struct mpi3_config_page_header header; + __le32 coalescing_timeout; + u8 coalescing_depth; + u8 pci_slot_num; + __le16 reserved0e; +}; + +#define MPI3_IOC1_PAGEVERSION (0x00) +#define MPI3_IOC1_PCISLOTNUM_UNKNOWN (0xff) +#ifndef MPI3_IOC2_EVENTMASK_WORDS +#define MPI3_IOC2_EVENTMASK_WORDS (4) +#endif +struct mpi3_ioc_page2 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le16 sas_broadcast_primitive_masks; + __le16 sas_notify_primitive_masks; + __le32 event_masks[MPI3_IOC2_EVENTMASK_WORDS]; +}; + +#define MPI3_IOC2_PAGEVERSION (0x00) +struct mpi3_uefibsd_page0 { + struct mpi3_config_page_header header; + __le32 bsd_options; + u8 ssu_timeout; + u8 io_timeout; + u8 tur_retries; + u8 tur_interval; + u8 reserved10; + u8 security_key_timeout; + __le16 reserved12; + __le32 reserved14; + __le32 reserved18; +}; + +#define MPI3_UEFIBSD_PAGEVERSION (0x00) +#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_MASK (0x00000003) +#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_IOC_AND_DEVS (0x00000000) +#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_IOC_ONLY (0x00000001) +#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_NONE (0x00000002) +#define MPI3_UEFIBSD_BSDOPTS_DIS_HII_CONFIG_UTIL (0x00000004) +#define MPI3_UEFIBSD_BSDOPTS_EN_ADV_ADAPTER_CONFIG (0x00000008) +union mpi3_security_mac { + __le32 dword[16]; + __le16 word[32]; + u8 byte[64]; +}; + +union mpi3_security_nonce { + __le32 dword[16]; + __le16 word[32]; + u8 byte[64]; +}; + +union mpi3_security0_cert_chain { + __le32 dword[1024]; + __le16 word[2048]; + u8 byte[4096]; +}; + +struct mpi3_security_page0 { + struct mpi3_config_page_header header; + u8 slot_num_group; + u8 slot_num; + __le16 cert_chain_length; + u8 cert_chain_flags; + u8 reserved0d[3]; + __le32 base_asym_algo; + __le32 base_hash_algo; + __le32 reserved18[4]; + union mpi3_security_mac mac; + union mpi3_security_nonce nonce; + union mpi3_security0_cert_chain certificate_chain; +}; + +#define MPI3_SECURITY0_PAGEVERSION (0x00) +#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_MASK (0x0e) +#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_UNUSED (0x00) +#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_CERBERUS (0x02) +#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_SPDM (0x04) +#define MPI3_SECURITY0_CERTCHAIN_FLAGS_SEALED (0x01) +#ifndef MPI3_SECURITY1_KEY_RECORD_MAX +#define MPI3_SECURITY1_KEY_RECORD_MAX 1 +#endif +#ifndef MPI3_SECURITY1_PAD_MAX +#define MPI3_SECURITY1_PAD_MAX 1 +#endif +union mpi3_security1_key_data { + __le32 dword[128]; + __le16 word[256]; + u8 byte[512]; +}; + +struct mpi3_security1_key_record { + u8 flags; + u8 consumer; + __le16 key_data_size; + __le32 additional_key_data; + __le32 reserved08[2]; + union mpi3_security1_key_data key_data; +}; + +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_MASK (0x1f) +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_NOT_VALID (0x00) +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_HMAC (0x01) +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_AES (0x02) +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PRIVATE (0x03) +#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PUBLIC (0x04) +#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_NOT_VALID (0x00) +#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_SAFESTORE (0x01) +#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CERT_CHAIN (0x02) +#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_AUTH_DEV_KEY (0x03) +#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CACHE_OFFLOAD (0x04) +struct mpi3_security_page1 { + struct mpi3_config_page_header header; + __le32 reserved08[2]; + union mpi3_security_mac mac; + union mpi3_security_nonce nonce; + u8 num_keys; + u8 reserved91[3]; + __le32 reserved94[3]; + struct mpi3_security1_key_record key_record[MPI3_SECURITY1_KEY_RECORD_MAX]; + u8 pad[MPI3_SECURITY1_PAD_MAX]; +}; + +#define MPI3_SECURITY1_PAGEVERSION (0x00) +struct mpi3_sas_io_unit0_phy_data { + u8 io_unit_port; + u8 port_flags; + u8 phy_flags; + u8 negotiated_link_rate; + __le16 controller_phy_device_info; + __le16 reserved06; + __le16 attached_dev_handle; + __le16 controller_dev_handle; + __le32 discovery_status; + __le32 reserved10; +}; + +#ifndef MPI3_SAS_IO_UNIT0_PHY_MAX +#define MPI3_SAS_IO_UNIT0_PHY_MAX (1) +#endif +struct mpi3_sas_io_unit_page0 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_phys; + u8 reserved0d[3]; + struct mpi3_sas_io_unit0_phy_data phy_data[MPI3_SAS_IO_UNIT0_PHY_MAX]; +}; + +#define MPI3_SASIOUNIT0_PAGEVERSION (0x00) +#define MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS (0x08) +#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01) +#define MPI3_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) +#define MPI3_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) +#define MPI3_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) +struct mpi3_sas_io_unit1_phy_data { + u8 io_unit_port; + u8 port_flags; + u8 phy_flags; + u8 max_min_link_rate; + __le16 controller_phy_device_info; + __le16 max_target_port_connect_time; + __le32 reserved08; +}; + +#ifndef MPI3_SAS_IO_UNIT1_PHY_MAX +#define MPI3_SAS_IO_UNIT1_PHY_MAX (1) +#endif +struct mpi3_sas_io_unit_page1 { + struct mpi3_config_page_header header; + __le16 control_flags; + __le16 sas_narrow_max_queue_depth; + __le16 additional_control_flags; + __le16 sas_wide_max_queue_depth; + u8 num_phys; + u8 sata_max_q_depth; + __le16 reserved12; + struct mpi3_sas_io_unit1_phy_data phy_data[MPI3_SAS_IO_UNIT1_PHY_MAX]; +}; + +#define MPI3_SASIOUNIT1_PAGEVERSION (0x00) +#define MPI3_SASIOUNIT1_CONTROL_CONTROLLER_DEVICE_SELF_TEST (0x8000) +#define MPI3_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000) +#define MPI3_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080) +#define MPI3_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040) +#define MPI3_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020) +#define MPI3_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010) +#define MPI3_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008) +#define MPI3_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004) +#define MPI3_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002) +#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_MASK (0x0001) +#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_DEVICE_NAME (0x0000) +#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_SAS_ADDRESS (0x0001) +#define MPI3_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100) +#define MPI3_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) +#define MPI3_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) +#define MPI3_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020) +#define MPI3_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010) +#define MPI3_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008) +#define MPI3_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004) +#define MPI3_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002) +#define MPI3_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001) +#define MPI3_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) +#define MPI3_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40) +#define MPI3_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20) +#define MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) +#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_MASK (0xf0) +#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_SHIFT (4) +#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_6_0 (0xa0) +#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_12_0 (0xb0) +#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_22_5 (0xc0) +#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_MASK (0x0f) +#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_6_0 (0x0a) +#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_12_0 (0x0b) +#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_22_5 (0x0c) +struct mpi3_sas_io_unit2_phy_pm_settings { + u8 control_flags; + u8 reserved01; + __le16 inactivity_timer_exponent; + u8 sata_partial_timeout; + u8 reserved05; + u8 sata_slumber_timeout; + u8 reserved07; + u8 sas_partial_timeout; + u8 reserved09; + u8 sas_slumber_timeout; + u8 reserved0b; +}; + +#ifndef MPI3_SAS_IO_UNIT2_PHY_MAX +#define MPI3_SAS_IO_UNIT2_PHY_MAX (1) +#endif +struct mpi3_sas_io_unit_page2 { + struct mpi3_config_page_header header; + u8 num_phys; + u8 reserved09[3]; + __le32 reserved0c; + struct mpi3_sas_io_unit2_phy_pm_settings sas_phy_power_management_settings[MPI3_SAS_IO_UNIT2_PHY_MAX]; +}; + +#define MPI3_SASIOUNIT2_PAGEVERSION (0x00) +#define MPI3_SASIOUNIT2_CONTROL_SAS_SLUMBER_ENABLE (0x08) +#define MPI3_SASIOUNIT2_CONTROL_SAS_PARTIAL_ENABLE (0x04) +#define MPI3_SASIOUNIT2_CONTROL_SATA_SLUMBER_ENABLE (0x02) +#define MPI3_SASIOUNIT2_CONTROL_SATA_PARTIAL_ENABLE (0x01) +#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_MASK (0x7000) +#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_SHIFT (12) +#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_MASK (0x0700) +#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_SHIFT (8) +#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_MASK (0x0070) +#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_SHIFT (4) +#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_MASK (0x0007) +#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_SHIFT (0) +#define MPI3_SASIOUNIT2_ITE_EXP_TEN_SECONDS (7) +#define MPI3_SASIOUNIT2_ITE_EXP_ONE_SECOND (6) +#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MILLISECONDS (5) +#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MILLISECONDS (4) +#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MILLISECOND (3) +#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MICROSECONDS (2) +#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MICROSECONDS (1) +#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MICROSECOND (0) +struct mpi3_sas_io_unit_page3 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le32 power_management_capabilities; +}; + +#define MPI3_SASIOUNIT3_PAGEVERSION (0x00) +#define MPI3_SASIOUNIT3_PM_HOST_SAS_SLUMBER_MODE (0x00000800) +#define MPI3_SASIOUNIT3_PM_HOST_SAS_PARTIAL_MODE (0x00000400) +#define MPI3_SASIOUNIT3_PM_HOST_SATA_SLUMBER_MODE (0x00000200) +#define MPI3_SASIOUNIT3_PM_HOST_SATA_PARTIAL_MODE (0x00000100) +#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008) +#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004) +#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002) +#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001) +struct mpi3_sas_expander_page0 { + struct mpi3_config_page_header header; + u8 io_unit_port; + u8 report_gen_length; + __le16 enclosure_handle; + __le32 reserved0c; + __le64 sas_address; + __le32 discovery_status; + __le16 dev_handle; + __le16 parent_dev_handle; + __le16 expander_change_count; + __le16 expander_route_indexes; + u8 num_phys; + u8 sas_level; + __le16 flags; + __le16 stp_bus_inactivity_time_limit; + __le16 stp_max_connect_time_limit; + __le16 stp_smp_nexus_loss_time; + __le16 max_num_routed_sas_addresses; + __le64 active_zone_manager_sas_address; + __le16 zone_lock_inactivity_limit; + __le16 reserved3a; + u8 time_to_reduced_func; + u8 initial_time_to_reduced_func; + u8 max_reduced_func_time; + u8 exp_status; +}; + +#define MPI3_SASEXPANDER0_PAGEVERSION (0x00) +#define MPI3_SASEXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) +#define MPI3_SASEXPANDER0_FLAGS_ZONE_LOCKED (0x1000) +#define MPI3_SASEXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) +#define MPI3_SASEXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) +#define MPI3_SASEXPANDER0_FLAGS_ZONING_SUPPORT (0x0200) +#define MPI3_SASEXPANDER0_FLAGS_ENABLED_ZONING (0x0100) +#define MPI3_SASEXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080) +#define MPI3_SASEXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010) +#define MPI3_SASEXPANDER0_FLAGS_OTHERS_CONFIG (0x0004) +#define MPI3_SASEXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002) +#define MPI3_SASEXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001) +#define MPI3_SASEXPANDER0_ES_NOT_RESPONDING (0x02) +#define MPI3_SASEXPANDER0_ES_RESPONDING (0x03) +#define MPI3_SASEXPANDER0_ES_DELAY_NOT_RESPONDING (0x04) +struct mpi3_sas_expander_page1 { + struct mpi3_config_page_header header; + u8 io_unit_port; + u8 reserved09[3]; + u8 num_phys; + u8 phy; + __le16 num_table_entries_programmed; + u8 programmed_link_rate; + u8 hw_link_rate; + __le16 attached_dev_handle; + __le32 phy_info; + __le16 attached_device_info; + __le16 reserved1a; + __le16 expander_dev_handle; + u8 change_count; + u8 negotiated_link_rate; + u8 phy_identifier; + u8 attached_phy_identifier; + u8 reserved22; + u8 discovery_info; + __le32 attached_phy_info; + u8 zone_group; + u8 self_config_status; + __le16 reserved2a; + __le16 slot; + __le16 slot_index; +}; + +#define MPI3_SASEXPANDER1_PAGEVERSION (0x00) +#define MPI3_SASEXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) +#define MPI3_SASEXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) +#define MPI3_SASEXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) +struct mpi3_sas_port_page0 { + struct mpi3_config_page_header header; + u8 port_number; + u8 reserved09; + u8 port_width; + u8 reserved0b; + u8 zone_group; + u8 reserved0d[3]; + __le64 sas_address; + __le16 device_info; + __le16 reserved1a; + __le32 reserved1c; +}; + +#define MPI3_SASPORT0_PAGEVERSION (0x00) +struct mpi3_sas_phy_page0 { + struct mpi3_config_page_header header; + __le16 owner_dev_handle; + __le16 reserved0a; + __le16 attached_dev_handle; + u8 attached_phy_identifier; + u8 reserved0f; + __le32 attached_phy_info; + u8 programmed_link_rate; + u8 hw_link_rate; + u8 change_count; + u8 flags; + __le32 phy_info; + u8 negotiated_link_rate; + u8 reserved1d[3]; + __le16 slot; + __le16 slot_index; +}; + +#define MPI3_SASPHY0_PAGEVERSION (0x00) +#define MPI3_SASPHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) +struct mpi3_sas_phy_page1 { + struct mpi3_config_page_header header; + __le32 reserved08; + __le32 invalid_dword_count; + __le32 running_disparity_error_count; + __le32 loss_dword_synch_count; + __le32 phy_reset_problem_count; +}; + +#define MPI3_SASPHY1_PAGEVERSION (0x00) +struct mpi3_sas_phy2_phy_event { + u8 phy_event_code; + u8 reserved01[3]; + __le32 phy_event_info; +}; + +#ifndef MPI3_SAS_PHY2_PHY_EVENT_MAX +#define MPI3_SAS_PHY2_PHY_EVENT_MAX (1) +#endif +struct mpi3_sas_phy_page2 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_phy_events; + u8 reserved0d[3]; + struct mpi3_sas_phy2_phy_event phy_event[MPI3_SAS_PHY2_PHY_EVENT_MAX]; +}; + +#define MPI3_SASPHY2_PAGEVERSION (0x00) +struct mpi3_sas_phy3_phy_event_config { + u8 phy_event_code; + u8 reserved01[3]; + u8 counter_type; + u8 threshold_window; + u8 time_units; + u8 reserved07; + __le32 event_threshold; + __le16 threshold_flags; + __le16 reserved0e; +}; + +#define MPI3_SASPHY3_EVENT_CODE_NO_EVENT (0x00) +#define MPI3_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) +#define MPI3_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) +#define MPI3_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) +#define MPI3_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) +#define MPI3_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) +#define MPI3_SASPHY3_EVENT_CODE_RX_ERROR (0x06) +#define MPI3_SASPHY3_EVENT_CODE_INV_SPL_PACKETS (0x07) +#define MPI3_SASPHY3_EVENT_CODE_LOSS_SPL_PACKET_SYNC (0x08) +#define MPI3_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) +#define MPI3_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) +#define MPI3_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) +#define MPI3_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) +#define MPI3_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) +#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) +#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) +#define MPI3_SASPHY3_EVENT_CODE_TX_BREAK (0x27) +#define MPI3_SASPHY3_EVENT_CODE_RX_BREAK (0x28) +#define MPI3_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) +#define MPI3_SASPHY3_EVENT_CODE_CONNECTION (0x2a) +#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2b) +#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2c) +#define MPI3_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2d) +#define MPI3_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2e) +#define MPI3_SASPHY3_EVENT_CODE_PERSIST_CONN (0x2f) +#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) +#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) +#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) +#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) +#define MPI3_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) +#define MPI3_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) +#define MPI3_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) +#define MPI3_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) +#define MPI3_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) +#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) +#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) +#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) +#define MPI3_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xd0) +#define MPI3_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xd1) +#define MPI3_SASPHY3_EVENT_CODE_RX_AIP (0xd2) +#define MPI3_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xd3) +#define MPI3_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xd4) +#define MPI3_SASPHY3_EVENT_CODE_LCCONN_TIME (0xd5) +#define MPI3_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xd6) +#define MPI3_SASPHY3_EVENT_CODE_SATA_TX_START (0xd7) +#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xd8) +#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xd9) +#define MPI3_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xda) +#define MPI3_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xdb) +#define MPI3_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xdc) +#define MPI3_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) +#define MPI3_SASPHY3_COUNTER_TYPE_SATURATING (0x01) +#define MPI3_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) +#define MPI3_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) +#define MPI3_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) +#define MPI3_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) +#define MPI3_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) +#define MPI3_SASPHY3_TFLAGS_PHY_RESET (0x0002) +#define MPI3_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) +#ifndef MPI3_SAS_PHY3_PHY_EVENT_MAX +#define MPI3_SAS_PHY3_PHY_EVENT_MAX (1) +#endif +struct mpi3_sas_phy_page3 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_phy_events; + u8 reserved0d[3]; + struct mpi3_sas_phy3_phy_event_config phy_event_config[MPI3_SAS_PHY3_PHY_EVENT_MAX]; +}; + +#define MPI3_SASPHY3_PAGEVERSION (0x00) +struct mpi3_sas_phy_page4 { + struct mpi3_config_page_header header; + u8 reserved08[3]; + u8 flags; + u8 initial_frame[28]; +}; + +#define MPI3_SASPHY4_PAGEVERSION (0x00) +#define MPI3_SASPHY4_FLAGS_FRAME_VALID (0x02) +#define MPI3_SASPHY4_FLAGS_SATA_FRAME (0x01) +#define MPI3_PCIE_LINK_RETIMERS_MASK (0x30) +#define MPI3_PCIE_LINK_RETIMERS_SHIFT (4) +#define MPI3_PCIE_NEG_LINK_RATE_MASK (0x0f) +#define MPI3_PCIE_NEG_LINK_RATE_UNKNOWN (0x00) +#define MPI3_PCIE_NEG_LINK_RATE_PHY_DISABLED (0x01) +#define MPI3_PCIE_NEG_LINK_RATE_2_5 (0x02) +#define MPI3_PCIE_NEG_LINK_RATE_5_0 (0x03) +#define MPI3_PCIE_NEG_LINK_RATE_8_0 (0x04) +#define MPI3_PCIE_NEG_LINK_RATE_16_0 (0x05) +#define MPI3_PCIE_NEG_LINK_RATE_32_0 (0x06) +struct mpi3_pcie_io_unit0_phy_data { + u8 link; + u8 link_flags; + u8 phy_flags; + u8 negotiated_link_rate; + __le16 attached_dev_handle; + __le16 controller_dev_handle; + __le32 enumeration_status; + u8 io_unit_port; + u8 reserved0d[3]; +}; + +#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_MASK (0x10) +#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_IOUNIT1 (0x00) +#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_BKPLANE (0x10) +#define MPI3_PCIEIOUNIT0_LINKFLAGS_ENUM_IN_PROGRESS (0x08) +#define MPI3_PCIEIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) +#define MPI3_PCIEIOUNIT0_PHYFLAGS_HOST_PHY (0x01) +#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCH_DEPTH_EXCEEDED (0x80000000) +#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCHES_EXCEEDED (0x40000000) +#define MPI3_PCIEIOUNIT0_ES_MAX_ENDPOINTS_EXCEEDED (0x20000000) +#define MPI3_PCIEIOUNIT0_ES_INSUFFICIENT_RESOURCES (0x10000000) +#ifndef MPI3_PCIE_IO_UNIT0_PHY_MAX +#define MPI3_PCIE_IO_UNIT0_PHY_MAX (1) +#endif +struct mpi3_pcie_io_unit_page0 { + struct mpi3_config_page_header header; + __le32 reserved08; + u8 num_phys; + u8 init_status; + __le16 reserved0e; + struct mpi3_pcie_io_unit0_phy_data phy_data[MPI3_PCIE_IO_UNIT0_PHY_MAX]; +}; + +#define MPI3_PCIEIOUNIT0_PAGEVERSION (0x00) +#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_ERRORS (0x00) +#define MPI3_PCIEIOUNIT0_INITSTATUS_NEEDS_INITIALIZATION (0x01) +#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_TARGETS_ALLOCATED (0x02) +#define MPI3_PCIEIOUNIT0_INITSTATUS_RESOURCE_ALLOC_FAILED (0x03) +#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_NUM_PHYS (0x04) +#define MPI3_PCIEIOUNIT0_INITSTATUS_UNSUPPORTED_CONFIG (0x05) +#define MPI3_PCIEIOUNIT0_INITSTATUS_HOST_PORT_MISMATCH (0x06) +#define MPI3_PCIEIOUNIT0_INITSTATUS_PHYS_NOT_CONSECUTIVE (0x07) +#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_CLOCKING_MODE (0x08) +#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_START (0xf0) +#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_END (0xff) +struct mpi3_pcie_io_unit1_phy_data { + u8 link; + u8 link_flags; + u8 phy_flags; + u8 max_min_link_rate; + __le32 reserved04; + __le32 reserved08; +}; + +#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_MASK (0x03) +#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_DIS_SEPARATE_REFCLK (0x00) +#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRIS (0x01) +#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRNS (0x02) +#define MPI3_PCIEIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_MASK (0xf0) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_SHIFT (4) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_2_5 (0x20) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_5_0 (0x30) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_8_0 (0x40) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_16_0 (0x50) +#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_32_0 (0x60) +#ifndef MPI3_PCIE_IO_UNIT1_PHY_MAX +#define MPI3_PCIE_IO_UNIT1_PHY_MAX (1) +#endif +struct mpi3_pcie_io_unit_page1 { + struct mpi3_config_page_header header; + __le32 control_flags; + __le32 reserved0c; + u8 num_phys; + u8 reserved11; + __le16 reserved12; + struct mpi3_pcie_io_unit1_phy_data phy_data[MPI3_PCIE_IO_UNIT1_PHY_MAX]; +}; + +#define MPI3_PCIEIOUNIT1_PAGEVERSION (0x00) +struct mpi3_pcie_io_unit_page2 { + struct mpi3_config_page_header header; + __le16 nv_me_max_queue_depth; + __le16 reserved0a; + u8 nv_me_abort_to; + u8 reserved0d; + __le16 reserved0e; +}; + +#define MPI3_PCIEIOUNIT2_PAGEVERSION (0x00) +struct mpi3_pcie_switch_page0 { + struct mpi3_config_page_header header; + u8 io_unit_port; + u8 switch_status; + u8 reserved0a[2]; + __le16 dev_handle; + __le16 parent_dev_handle; + u8 num_ports; + u8 pc_ie_level; + __le16 reserved12; + __le32 reserved14; + __le32 reserved18; + __le32 reserved1c; +}; + +#define MPI3_PCIESWITCH0_PAGEVERSION (0x00) +#define MPI3_PCIESWITCH0_SS_NOT_RESPONDING (0x02) +#define MPI3_PCIESWITCH0_SS_RESPONDING (0x03) +#define MPI3_PCIESWITCH0_SS_DELAY_NOT_RESPONDING (0x04) +struct mpi3_pcie_switch_page1 { + struct mpi3_config_page_header header; + u8 io_unit_port; + u8 reserved09[3]; + u8 num_ports; + u8 port_num; + __le16 attached_dev_handle; + __le16 switch_dev_handle; + u8 negotiated_port_width; + u8 negotiated_link_rate; + __le16 slot; + __le16 slot_index; + __le32 reserved18; +}; + +#define MPI3_PCIESWITCH1_PAGEVERSION (0x00) +struct mpi3_pcie_link_page0 { + struct mpi3_config_page_header header; + u8 link; + u8 reserved09[3]; + __le32 correctable_error_count; + __le16 n_fatal_error_count; + __le16 reserved12; + __le16 fatal_error_count; + __le16 reserved16; +}; + +#define MPI3_PCIELINK0_PAGEVERSION (0x00) +struct mpi3_enclosure_page0 { + struct mpi3_config_page_header header; + __le64 enclosure_logical_id; + __le16 flags; + __le16 enclosure_handle; + __le16 num_slots; + __le16 start_slot; + u8 io_unit_port; + u8 enclosure_level; + __le16 sep_dev_handle; + __le32 reserved1c; +}; + +#define MPI3_ENCLOSURE0_PAGEVERSION (0x00) +#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_MASK (0xc000) +#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_VIRTUAL (0x0000) +#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_SAS (0x4000) +#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_PCIE (0x8000) +#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK (0x0010) +#define MPI3_ENCLS0_FLAGS_ENCL_DEV_NOT_FOUND (0x0000) +#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT (0x0010) +#define MPI3_ENCLS0_FLAGS_MNG_MASK (0x000f) +#define MPI3_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) +#define MPI3_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) +#define MPI3_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0002) +#define MPI3_DEVICE_DEVFORM_SAS_SATA (0x00) +#define MPI3_DEVICE_DEVFORM_PCIE (0x01) +#define MPI3_DEVICE_DEVFORM_VD (0x02) +struct mpi3_device0_sas_sata_format { + __le64 sas_address; + __le16 flags; + __le16 device_info; + u8 phy_num; + u8 attached_phy_identifier; + u8 max_port_connections; + u8 zone_group; +}; + +#define MPI3_DEVICE0_SASSATA_FLAGS_SLUMBER_CAP (0x0200) +#define MPI3_DEVICE0_SASSATA_FLAGS_PARTIAL_CAP (0x0100) +#define MPI3_DEVICE0_SASSATA_FLAGS_ASYNC_NOTIFY (0x0080) +#define MPI3_DEVICE0_SASSATA_FLAGS_SW_PRESERVE (0x0040) +#define MPI3_DEVICE0_SASSATA_FLAGS_UNSUPP_DEV (0x0020) +#define MPI3_DEVICE0_SASSATA_FLAGS_48BIT_LBA (0x0010) +#define MPI3_DEVICE0_SASSATA_FLAGS_SMART_SUPP (0x0008) +#define MPI3_DEVICE0_SASSATA_FLAGS_NCQ_SUPP (0x0004) +#define MPI3_DEVICE0_SASSATA_FLAGS_FUA_SUPP (0x0002) +#define MPI3_DEVICE0_SASSATA_FLAGS_PERSIST_CAP (0x0001) +struct mpi3_device0_pcie_format { + u8 supported_link_rates; + u8 max_port_width; + u8 negotiated_port_width; + u8 negotiated_link_rate; + u8 port_num; + u8 controller_reset_to; + __le16 device_info; + __le32 maximum_data_transfer_size; + __le32 capabilities; + __le16 noiob; + u8 nv_me_abort_to; + u8 page_size; + __le16 shutdown_latency; + __le16 reserved16; +}; + +#define MPI3_DEVICE0_PCIE_LINK_RATE_32_0_SUPP (0x10) +#define MPI3_DEVICE0_PCIE_LINK_RATE_16_0_SUPP (0x08) +#define MPI3_DEVICE0_PCIE_LINK_RATE_8_0_SUPP (0x04) +#define MPI3_DEVICE0_PCIE_LINK_RATE_5_0_SUPP (0x02) +#define MPI3_DEVICE0_PCIE_LINK_RATE_2_5_SUPP (0x01) +#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK (0x0003) +#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NO_DEVICE (0x0000) +#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE (0x0001) +#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SWITCH_DEVICE (0x0002) +#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SCSI_DEVICE (0x0003) +#define MPI3_DEVICE0_PCIE_CAP_METADATA_SEPARATED (0x00000010) +#define MPI3_DEVICE0_PCIE_CAP_SGL_DWORD_ALIGN_REQUIRED (0x00000008) +#define MPI3_DEVICE0_PCIE_CAP_NVME_SGL_ENABLED (0x00000004) +#define MPI3_DEVICE0_PCIE_CAP_BIT_BUCKET_SGL_SUPP (0x00000002) +#define MPI3_DEVICE0_PCIE_CAP_SGL_SUPP (0x00000001) +struct mpi3_device0_vd_format { + u8 vd_state; + u8 raid_level; + __le16 device_info; + __le16 flags; + __le16 reserved06; + __le32 reserved08[2]; +}; + +#define MPI3_DEVICE0_VD_STATE_OFFLINE (0x00) +#define MPI3_DEVICE0_VD_STATE_PARTIALLY_DEGRADED (0x01) +#define MPI3_DEVICE0_VD_STATE_DEGRADED (0x02) +#define MPI3_DEVICE0_VD_STATE_OPTIMAL (0x03) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_0 (0) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_1 (1) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_5 (5) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_6 (6) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_10 (10) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_50 (50) +#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_60 (60) +#define MPI3_DEVICE0_VD_DEVICE_INFO_HDD (0x0010) +#define MPI3_DEVICE0_VD_DEVICE_INFO_SSD (0x0008) +#define MPI3_DEVICE0_VD_DEVICE_INFO_NVME (0x0004) +#define MPI3_DEVICE0_VD_DEVICE_INFO_SATA (0x0002) +#define MPI3_DEVICE0_VD_DEVICE_INFO_SAS (0x0001) +#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_MASK (0x0003) +#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_NONE (0x0000) +#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_HOST (0x0001) +#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_IOC (0x0002) +union mpi3_device0_dev_spec_format { + struct mpi3_device0_sas_sata_format sas_sata_format; + struct mpi3_device0_pcie_format pcie_format; + struct mpi3_device0_vd_format vd_format; +}; + +struct mpi3_device_page0 { + struct mpi3_config_page_header header; + __le16 dev_handle; + __le16 parent_dev_handle; + __le16 slot; + __le16 enclosure_handle; + __le64 wwid; + __le16 persistent_id; + u8 io_unit_port; + u8 access_status; + __le16 flags; + __le16 reserved1e; + __le16 slot_index; + __le16 queue_depth; + u8 reserved24[3]; + u8 device_form; + union mpi3_device0_dev_spec_format device_specific; +}; + +#define MPI3_DEVICE0_PAGEVERSION (0x00) +#define MPI3_DEVICE0_WWID_INVALID (0xffffffffffffffff) +#define MPI3_DEVICE0_PERSISTENTID_INVALID (0xffff) +#define MPI3_DEVICE0_IOUNITPORT_INVALID (0xff) +#define MPI3_DEVICE0_ASTATUS_NO_ERRORS (0x00) +#define MPI3_DEVICE0_ASTATUS_NEEDS_INITIALIZATION (0x01) +#define MPI3_DEVICE0_ASTATUS_CAP_UNSUPPORTED (0x02) +#define MPI3_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x03) +#define MPI3_DEVICE0_ASTATUS_UNAUTHORIZED (0x04) +#define MPI3_DEVICE0_ASTATUS_DEVICE_MISSING_DELAY (0x05) +#define MPI3_DEVICE0_ASTATUS_SAS_UNKNOWN (0x10) +#define MPI3_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x11) +#define MPI3_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x12) +#define MPI3_DEVICE0_ASTATUS_SIF_UNKNOWN (0x20) +#define MPI3_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x21) +#define MPI3_DEVICE0_ASTATUS_SIF_DIAG (0x22) +#define MPI3_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x23) +#define MPI3_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x24) +#define MPI3_DEVICE0_ASTATUS_SIF_PIO_SN (0x25) +#define MPI3_DEVICE0_ASTATUS_SIF_MDMA_SN (0x26) +#define MPI3_DEVICE0_ASTATUS_SIF_UDMA_SN (0x27) +#define MPI3_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x28) +#define MPI3_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x29) +#define MPI3_DEVICE0_ASTATUS_SIF_MAX (0x2f) +#define MPI3_DEVICE0_ASTATUS_PCIE_UNKNOWN (0x30) +#define MPI3_DEVICE0_ASTATUS_PCIE_MEM_SPACE_ACCESS (0x31) +#define MPI3_DEVICE0_ASTATUS_PCIE_UNSUPPORTED (0x32) +#define MPI3_DEVICE0_ASTATUS_PCIE_MSIX_REQUIRED (0x33) +#define MPI3_DEVICE0_ASTATUS_NVME_UNKNOWN (0x40) +#define MPI3_DEVICE0_ASTATUS_NVME_READY_TIMEOUT (0x41) +#define MPI3_DEVICE0_ASTATUS_NVME_DEVCFG_UNSUPPORTED (0x42) +#define MPI3_DEVICE0_ASTATUS_NVME_IDENTIFY_FAILED (0x43) +#define MPI3_DEVICE0_ASTATUS_NVME_QCONFIG_FAILED (0x44) +#define MPI3_DEVICE0_ASTATUS_NVME_QCREATION_FAILED (0x45) +#define MPI3_DEVICE0_ASTATUS_NVME_EVENTCFG_FAILED (0x46) +#define MPI3_DEVICE0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED (0x47) +#define MPI3_DEVICE0_ASTATUS_NVME_IDLE_TIMEOUT (0x48) +#define MPI3_DEVICE0_ASTATUS_NVME_CTRL_FAILURE_STATUS (0x49) +#define MPI3_DEVICE0_ASTATUS_VD_UNKNOWN (0x50) +#define MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE (0x0080) +#define MPI3_DEVICE0_FLAGS_HIDDEN (0x0008) +#define MPI3_DEVICE0_FLAGS_ATT_METHOD_MASK (0x0006) +#define MPI3_DEVICE0_FLAGS_ATT_METHOD_NOT_DIR_ATTACHED (0x0000) +#define MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED (0x0002) +#define MPI3_DEVICE0_FLAGS_ATT_METHOD_VIRTUAL (0x0004) +#define MPI3_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) +#define MPI3_DEVICE0_QUEUE_DEPTH_NOT_APPLICABLE (0x0000) +struct mpi3_device1_sas_sata_format { + __le32 reserved00; +}; + +struct mpi3_device1_pcie_format { + __le16 vendor_id; + __le16 device_id; + __le16 subsystem_vendor_id; + __le16 subsystem_id; + __le32 reserved08; + u8 revision_id; + u8 reserved0d; + __le16 pci_parameters; +}; + +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_128B (0x0) +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_256B (0x1) +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_512B (0x2) +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_1024B (0x3) +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_2048B (0x4) +#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_4096B (0x5) +#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_MASK (0x01c0) +#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_SHIFT (6) +#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_MASK (0x0038) +#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_SHIFT (3) +#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_MASK (0x0007) +#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_SHIFT (0) +struct mpi3_device1_vd_format { + __le32 reserved00; +}; + +union mpi3_device1_dev_spec_format { + struct mpi3_device1_sas_sata_format sas_sata_format; + struct mpi3_device1_pcie_format pcie_format; + struct mpi3_device1_vd_format vd_format; +}; + +struct mpi3_device_page1 { + struct mpi3_config_page_header header; + __le16 dev_handle; + __le16 reserved0a; + __le32 reserved0c[12]; + u8 reserved3c[3]; + u8 device_form; + union mpi3_device1_dev_spec_format device_specific; +}; + +#define MPI3_DEVICE1_PAGEVERSION (0x00) +#endif diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_image.h b/drivers/scsi/mpi3mr/mpi/mpi30_image.h new file mode 100644 index 000000000000..169e4f9b7b7c --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_image.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2018-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_IMAGE_H +#define MPI30_IMAGE_H 1 +struct mpi3_comp_image_version { + __le16 build_num; + __le16 customer_id; + u8 phase_minor; + u8 phase_major; + u8 gen_minor; + u8 gen_major; +}; + +struct mpi3_hash_exclusion_format { + __le32 offset; + __le32 size; +}; + +#define MPI3_IMAGE_HASH_EXCUSION_NUM (4) +struct mpi3_component_image_header { + __le32 signature0; + __le32 load_address; + __le32 data_size; + __le32 start_offset; + __le32 signature1; + __le32 flash_offset; + __le32 image_size; + __le32 version_string_offset; + __le32 build_date_string_offset; + __le32 build_time_string_offset; + __le32 environment_variable_offset; + __le32 application_specific; + __le32 signature2; + __le32 header_size; + __le32 crc; + __le32 flags; + __le32 secondary_flash_offset; + __le32 etp_offset; + __le32 etp_size; + union mpi3_version_union rmc_interface_version; + union mpi3_version_union etp_interface_version; + struct mpi3_comp_image_version component_image_version; + struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCUSION_NUM]; + __le32 next_image_header_offset; + union mpi3_version_union security_version; + __le32 reserved84[31]; +}; + +#define MPI3_IMAGE_HEADER_SIGNATURE0_MPI3 (0xeb00003e) +#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_INVALID (0x00000000) +#define MPI3_IMAGE_HEADER_SIGNATURE1_APPLICATION (0x20505041) +#define MPI3_IMAGE_HEADER_SIGNATURE1_FIRST_MUTABLE (0x20434d46) +#define MPI3_IMAGE_HEADER_SIGNATURE1_BSP (0x20505342) +#define MPI3_IMAGE_HEADER_SIGNATURE1_ROM_BIOS (0x534f4942) +#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_X64 (0x4d494948) +#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_ARM (0x41494948) +#define MPI3_IMAGE_HEADER_SIGNATURE1_CPLD (0x444c5043) +#define MPI3_IMAGE_HEADER_SIGNATURE1_SPD (0x20445053) +#define MPI3_IMAGE_HEADER_SIGNATURE1_GAS_GAUGE (0x20534147) +#define MPI3_IMAGE_HEADER_SIGNATURE1_PBLP (0x504c4250) +#define MPI3_IMAGE_HEADER_SIGNATURE2_VALUE (0x50584546) +#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_MASK (0x00000030) +#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_CDI (0x00000000) +#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_DI (0x00000010) +#define MPI3_IMAGE_HEADER_FLAGS_SIGNED_NVDATA (0x00000008) +#define MPI3_IMAGE_HEADER_FLAGS_REQUIRES_ACTIVATION (0x00000004) +#define MPI3_IMAGE_HEADER_FLAGS_COMPRESSED (0x00000002) +#define MPI3_IMAGE_HEADER_FLAGS_FLASH (0x00000001) +#define MPI3_IMAGE_HEADER_SIGNATURE0_OFFSET (0x00) +#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_OFFSET (0x04) +#define MPI3_IMAGE_HEADER_DATA_SIZE_OFFSET (0x08) +#define MPI3_IMAGE_HEADER_START_OFFSET_OFFSET (0x0c) +#define MPI3_IMAGE_HEADER_SIGNATURE1_OFFSET (0x10) +#define MPI3_IMAGE_HEADER_FLASH_OFFSET_OFFSET (0x14) +#define MPI3_IMAGE_HEADER_FLASH_SIZE_OFFSET (0x18) +#define MPI3_IMAGE_HEADER_VERSION_STRING_OFFSET_OFFSET (0x1c) +#define MPI3_IMAGE_HEADER_BUILD_DATE_STRING_OFFSET_OFFSET (0x20) +#define MPI3_IMAGE_HEADER_BUILD_TIME_OFFSET_OFFSET (0x24) +#define MPI3_IMAGE_HEADER_ENVIROMENT_VAR_OFFSET_OFFSET (0x28) +#define MPI3_IMAGE_HEADER_APPLICATION_SPECIFIC_OFFSET (0x2c) +#define MPI3_IMAGE_HEADER_SIGNATURE2_OFFSET (0x30) +#define MPI3_IMAGE_HEADER_HEADER_SIZE_OFFSET (0x34) +#define MPI3_IMAGE_HEADER_CRC_OFFSET (0x38) +#define MPI3_IMAGE_HEADER_FLAGS_OFFSET (0x3c) +#define MPI3_IMAGE_HEADER_SECONDARY_FLASH_OFFSET_OFFSET (0x40) +#define MPI3_IMAGE_HEADER_ETP_OFFSET_OFFSET (0x44) +#define MPI3_IMAGE_HEADER_ETP_SIZE_OFFSET (0x48) +#define MPI3_IMAGE_HEADER_RMC_INTERFACE_VER_OFFSET (0x4c) +#define MPI3_IMAGE_HEADER_ETP_INTERFACE_VER_OFFSET (0x50) +#define MPI3_IMAGE_HEADER_COMPONENT_IMAGE_VER_OFFSET (0x54) +#define MPI3_IMAGE_HEADER_HASH_EXCLUSION_OFFSET (0x5c) +#define MPI3_IMAGE_HEADER_NEXT_IMAGE_HEADER_OFFSET_OFFSET (0x7c) +#define MPI3_IMAGE_HEADER_SIZE (0x100) +struct mpi3_extended_image_header { + u8 image_type; + u8 reserved01[3]; + __le32 checksum; + __le32 image_size; + __le32 next_image_header_offset; + __le32 reserved10[4]; + __le32 identify_string[8]; +}; + +#define MPI3_EXT_IMAGE_IMAGETYPE_OFFSET (0x00) +#define MPI3_EXT_IMAGE_IMAGESIZE_OFFSET (0x08) +#define MPI3_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0c) +#define MPI3_EXT_IMAGE_HEADER_SIZE (0x40) +#define MPI3_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) +#define MPI3_EXT_IMAGE_TYPE_NVDATA (0x03) +#define MPI3_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) +#define MPI3_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09) +#define MPI3_EXT_IMAGE_TYPE_RDE (0x0a) +#define MPI3_EXT_IMAGE_TYPE_AUXILIARY_PROCESSOR (0x0b) +#define MPI3_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) +#define MPI3_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xff) +struct mpi3_supported_device { + __le16 device_id; + __le16 vendor_id; + __le16 device_id_mask; + __le16 reserved06; + u8 low_pci_rev; + u8 high_pci_rev; + __le16 reserved0a; + __le32 reserved0c; +}; + +#ifndef MPI3_SUPPORTED_DEVICE_MAX +#define MPI3_SUPPORTED_DEVICE_MAX (1) +#endif +struct mpi3_supported_devices_data { + u8 image_version; + u8 reserved01; + u8 num_devices; + u8 reserved03; + __le32 reserved04; + struct mpi3_supported_device supported_device[MPI3_SUPPORTED_DEVICE_MAX]; +}; + +#ifndef MPI3_ENCRYPTED_HASH_MAX +#define MPI3_ENCRYPTED_HASH_MAX (1) +#endif +struct mpi3_encrypted_hash_entry { + u8 hash_image_type; + u8 hash_algorithm; + u8 encryption_algorithm; + u8 reserved03; + __le32 reserved04; + __le32 encrypted_hash[MPI3_ENCRYPTED_HASH_MAX]; +}; + +#define MPI3_HASH_IMAGE_TYPE_KEY_WITH_SIGNATURE (0x03) +#define MPI3_HASH_ALGORITHM_VERSION_MASK (0xe0) +#define MPI3_HASH_ALGORITHM_VERSION_NONE (0x00) +#define MPI3_HASH_ALGORITHM_VERSION_SHA1 (0x20) +#define MPI3_HASH_ALGORITHM_VERSION_SHA2 (0x40) +#define MPI3_HASH_ALGORITHM_VERSION_SHA3 (0x60) +#define MPI3_HASH_ALGORITHM_SIZE_MASK (0x1f) +#define MPI3_HASH_ALGORITHM_SIZE_UNUSED (0x00) +#define MPI3_HASH_ALGORITHM_SIZE_SHA256 (0x01) +#define MPI3_HASH_ALGORITHM_SIZE_SHA512 (0x02) +#define MPI3_ENCRYPTION_ALGORITHM_UNUSED (0x00) +#define MPI3_ENCRYPTION_ALGORITHM_RSA256 (0x01) +#define MPI3_ENCRYPTION_ALGORITHM_RSA512 (0x02) +#define MPI3_ENCRYPTION_ALGORITHM_RSA1024 (0x03) +#define MPI3_ENCRYPTION_ALGORITHM_RSA2048 (0x04) +#define MPI3_ENCRYPTION_ALGORITHM_RSA4096 (0x05) +#define MPI3_ENCRYPTION_ALGORITHM_RSA3072 (0x06) +#ifndef MPI3_PUBLIC_KEY_MAX +#define MPI3_PUBLIC_KEY_MAX (1) +#endif +struct mpi3_encrypted_key_with_hash_entry { + u8 hash_image_type; + u8 hash_algorithm; + u8 encryption_algorithm; + u8 reserved03; + __le32 reserved04; + __le32 public_key[MPI3_PUBLIC_KEY_MAX]; + __le32 encrypted_hash[MPI3_ENCRYPTED_HASH_MAX]; +}; + +#ifndef MPI3_ENCRYPTED_HASH_ENTRY_MAX +#define MPI3_ENCRYPTED_HASH_ENTRY_MAX (1) +#endif +struct mpi3_encrypted_hash_data { + u8 image_version; + u8 num_hash; + __le16 reserved02; + __le32 reserved04; + struct mpi3_encrypted_hash_entry encrypted_hash_entry[MPI3_ENCRYPTED_HASH_ENTRY_MAX]; +}; + +#ifndef MPI3_AUX_PROC_DATA_MAX +#define MPI3_AUX_PROC_DATA_MAX (1) +#endif +struct mpi3_aux_processor_data { + u8 boot_method; + u8 num_load_addr; + u8 reserved02; + u8 type; + __le32 version; + __le32 load_address[8]; + __le32 reserved28[22]; + __le32 aux_processor_data[MPI3_AUX_PROC_DATA_MAX]; +}; + +#define MPI3_AUX_PROC_DATA_OFFSET (0x80) +#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_MSG (0x00) +#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_DOORBELL (0x01) +#define MPI3_AUXPROCESSOR_BOOT_METHOD_COMPONENT (0x02) +#define MPI3_AUXPROCESSOR_TYPE_ARM_A15 (0x00) +#define MPI3_AUXPROCESSOR_TYPE_ARM_M0 (0x01) +#define MPI3_AUXPROCESSOR_TYPE_ARM_R4 (0x02) +#endif diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_init.h b/drivers/scsi/mpi3mr/mpi/mpi30_init.h new file mode 100644 index 000000000000..e02b6d3cfba2 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_init.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2016-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_INIT_H +#define MPI30_INIT_H 1 +struct mpi3_scsi_io_cdb_eedp32 { + u8 cdb[20]; + __be32 primary_reference_tag; + __le16 primary_application_tag; + __le16 primary_application_tag_mask; + __le32 transfer_length; +}; + +union mpi3_scso_io_cdb_union { + u8 cdb32[32]; + struct mpi3_scsi_io_cdb_eedp32 eedp32; + struct mpi3_sge_common sge; +}; + +struct mpi3_scsi_io_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 dev_handle; + __le32 flags; + __le32 skip_count; + __le32 data_length; + u8 lun[8]; + union mpi3_scso_io_cdb_union cdb; + union mpi3_sge_union sgl[4]; +}; + +#define MPI3_SCSIIO_MSGFLAGS_METASGL_VALID (0x80) +#define MPI3_SCSIIO_FLAGS_LARGE_CDB (0x60000000) +#define MPI3_SCSIIO_FLAGS_CDB_16_OR_LESS (0x00000000) +#define MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16 (0x20000000) +#define MPI3_SCSIIO_FLAGS_CDB_IN_SEPARATE_BUFFER (0x40000000) +#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_MASK (0x07000000) +#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ (0x00000000) +#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_HEADOFQ (0x01000000) +#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ORDEREDQ (0x02000000) +#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ACAQ (0x04000000) +#define MPI3_SCSIIO_FLAGS_CMDPRI_MASK (0x00f00000) +#define MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT (20) +#define MPI3_SCSIIO_FLAGS_DATADIRECTION_MASK (0x000c0000) +#define MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER (0x00000000) +#define MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE (0x00040000) +#define MPI3_SCSIIO_FLAGS_DATADIRECTION_READ (0x00080000) +#define MPI3_SCSIIO_FLAGS_DMAOPERATION_MASK (0x00030000) +#define MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI (0x00010000) +#define MPI3_SCSIIO_METASGL_INDEX (3) +struct mpi3_scsi_io_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + u8 scsi_status; + u8 scsi_state; + __le16 dev_handle; + __le32 transfer_count; + __le32 sense_count; + __le32 response_data; + __le16 task_tag; + __le16 scsi_status_qualifier; + __le32 eedp_error_offset; + __le16 eedp_observed_app_tag; + __le16 eedp_observed_guard; + __le32 eedp_observed_ref_tag; + __le64 sense_data_buffer_address; +}; + +#define MPI3_SCSIIO_REPLY_MSGFLAGS_REFTAG_OBSERVED_VALID (0x01) +#define MPI3_SCSIIO_REPLY_MSGFLAGS_APPTAG_OBSERVED_VALID (0x02) +#define MPI3_SCSIIO_REPLY_MSGFLAGS_GUARD_OBSERVED_VALID (0x04) +#define MPI3_SCSI_STATUS_GOOD (0x00) +#define MPI3_SCSI_STATUS_CHECK_CONDITION (0x02) +#define MPI3_SCSI_STATUS_CONDITION_MET (0x04) +#define MPI3_SCSI_STATUS_BUSY (0x08) +#define MPI3_SCSI_STATUS_INTERMEDIATE (0x10) +#define MPI3_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) +#define MPI3_SCSI_STATUS_RESERVATION_CONFLICT (0x18) +#define MPI3_SCSI_STATUS_COMMAND_TERMINATED (0x22) +#define MPI3_SCSI_STATUS_TASK_SET_FULL (0x28) +#define MPI3_SCSI_STATUS_ACA_ACTIVE (0x30) +#define MPI3_SCSI_STATUS_TASK_ABORTED (0x40) +#define MPI3_SCSI_STATE_SENSE_MASK (0x03) +#define MPI3_SCSI_STATE_SENSE_VALID (0x00) +#define MPI3_SCSI_STATE_SENSE_FAILED (0x01) +#define MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY (0x02) +#define MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE (0x03) +#define MPI3_SCSI_STATE_NO_SCSI_STATUS (0x04) +#define MPI3_SCSI_STATE_TERMINATED (0x08) +#define MPI3_SCSI_STATE_RESPONSE_DATA_VALID (0x10) +#define MPI3_SCSI_RSP_RESPONSECODE_MASK (0x000000ff) +#define MPI3_SCSI_RSP_RESPONSECODE_SHIFT (0) +#define MPI3_SCSI_RSP_ARI2_MASK (0x0000ff00) +#define MPI3_SCSI_RSP_ARI2_SHIFT (8) +#define MPI3_SCSI_RSP_ARI1_MASK (0x00ff0000) +#define MPI3_SCSI_RSP_ARI1_SHIFT (16) +#define MPI3_SCSI_RSP_ARI0_MASK (0xff000000) +#define MPI3_SCSI_RSP_ARI0_SHIFT (24) +#define MPI3_SCSI_TASKTAG_UNKNOWN (0xffff) +struct mpi3_scsi_task_mgmt_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 dev_handle; + __le16 task_host_tag; + u8 task_type; + u8 reserved0f; + __le16 task_request_queue_id; + __le16 reserved12; + __le32 reserved14; + u8 lun[8]; +}; + +#define MPI3_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x08) +#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) +#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK_SET (0x02) +#define MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) +#define MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) +#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) +#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) +#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_ACA (0x08) +#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK_SET (0x09) +#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_ASYNC_EVENT (0x0a) +#define MPI3_SCSITASKMGMT_TASKTYPE_I_T_NEXUS_RESET (0x0b) +struct mpi3_scsi_task_mgmt_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + __le32 termination_count; + __le32 response_data; + __le32 reserved18; +}; + +#define MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC (0x80) +#endif diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h new file mode 100644 index 000000000000..1af99a5382d5 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h @@ -0,0 +1,1004 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2016-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_IOC_H +#define MPI30_IOC_H 1 +struct mpi3_ioc_init_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + union mpi3_version_union mpi_version; + __le64 time_stamp; + u8 reserved18; + u8 who_init; + __le16 reserved1a; + __le16 reply_free_queue_depth; + __le16 reserved1e; + __le64 reply_free_queue_address; + __le32 reserved28; + __le16 sense_buffer_free_queue_depth; + __le16 sense_buffer_length; + __le64 sense_buffer_free_queue_address; + __le64 driver_information_address; +}; + +#define MPI3_WHOINIT_NOT_INITIALIZED (0x00) +#define MPI3_WHOINIT_ROM_BIOS (0x02) +#define MPI3_WHOINIT_HOST_DRIVER (0x03) +#define MPI3_WHOINIT_MANUFACTURER (0x04) +struct mpi3_driver_info_layout { + __le32 information_length; + u8 driver_signature[12]; + u8 os_name[16]; + u8 os_version[12]; + u8 driver_name[20]; + u8 driver_version[32]; + u8 driver_release_date[20]; + __le32 driver_capabilities; +}; + +struct mpi3_ioc_facts_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + __le32 reserved0c; + union mpi3_sge_union sgl; +}; + +struct mpi3_ioc_facts_data { + __le16 ioc_facts_data_length; + __le16 reserved02; + union mpi3_version_union mpi_version; + struct mpi3_comp_image_version fw_version; + __le32 ioc_capabilities; + u8 ioc_number; + u8 who_init; + __le16 max_msix_vectors; + __le16 max_outstanding_request; + __le16 product_id; + __le16 ioc_request_frame_size; + __le16 reply_frame_size; + __le16 ioc_exceptions; + __le16 max_persistent_id; + u8 sge_modifier_mask; + u8 sge_modifier_value; + u8 sge_modifier_shift; + u8 protocol_flags; + __le16 max_sas_initiators; + __le16 max_sas_targets; + __le16 max_sas_expanders; + __le16 max_enclosures; + __le16 min_dev_handle; + __le16 max_dev_handle; + __le16 max_pc_ie_switches; + __le16 max_nvme; + __le16 max_pds; + __le16 max_vds; + __le16 max_host_pds; + __le16 max_advanced_host_pds; + __le16 max_raid_pds; + __le16 max_posted_cmd_buffers; + __le32 flags; + __le16 max_operational_request_queues; + __le16 max_operational_reply_queues; + __le16 shutdown_timeout; + __le16 reserved4e; + __le32 diag_trace_size; + __le32 diag_fw_size; +}; + +#define MPI3_IOCFACTS_CAPABILITY_ADVANCED_HOST_PD (0x00000010) +#define MPI3_IOCFACTS_CAPABILITY_RAID_CAPABLE (0x00000008) +#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_GRAN_MASK (0x00000001) +#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_IOC_GRAN (0x00000000) +#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_REPLY_Q_GRAN (0x00000001) +#define MPI3_IOCFACTS_PID_TYPE_MASK (0xf000) +#define MPI3_IOCFACTS_PID_TYPE_SHIFT (12) +#define MPI3_IOCFACTS_PID_PRODUCT_MASK (0x0f00) +#define MPI3_IOCFACTS_PID_PRODUCT_SHIFT (8) +#define MPI3_IOCFACTS_PID_FAMILY_MASK (0x00ff) +#define MPI3_IOCFACTS_PID_FAMILY_SHIFT (0) +#define MPI3_IOCFACTS_EXCEPT_SAFE_MODE (0x0800) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_MASK (0x0700) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_NONE (0x0000) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_RAID (0x0100) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_OOB (0x0200) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_RAID (0x0300) +#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_OOB (0x0400) +#define MPI3_IOCFACTS_EXCEPT_PCIE_DISABLED (0x0080) +#define MPI3_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0040) +#define MPI3_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0020) +#define MPI3_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0010) +#define MPI3_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0008) +#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x0001) +#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_PRIMARY (0x0000) +#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_SECONDARY (0x0001) +#define MPI3_IOCFACTS_PROTOCOL_SAS (0x0010) +#define MPI3_IOCFACTS_PROTOCOL_SATA (0x0008) +#define MPI3_IOCFACTS_PROTOCOL_NVME (0x0004) +#define MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) +#define MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) +#define MPI3_IOCFACTS_FLAGS_SIGNED_NVDATA_REQUIRED (0x00010000) +#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK (0x0000ff00) +#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT (8) +#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK (0x00000030) +#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_NOT_STARTED (0x00000000) +#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_IN_PROGRESS (0x00000010) +#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_COMPLETE (0x00000020) +#define MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK (0x0000000f) +#define MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA (0x00000000) +#define MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR (0x00000002) +struct mpi3_mgmt_passthrough_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + __le32 reserved0c[5]; + union mpi3_sge_union command_sgl; + union mpi3_sge_union response_sgl; +}; + +struct mpi3_create_request_queue_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 flags; + u8 burst; + __le16 size; + __le16 queue_id; + __le16 reply_queue_id; + __le16 reserved12; + __le32 reserved14; + __le64 base_address; +}; + +#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_MASK (0x80) +#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80) +#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00) +struct mpi3_delete_request_queue_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 queue_id; +}; + +struct mpi3_create_reply_queue_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 flags; + u8 reserved0b; + __le16 size; + __le16 queue_id; + __le16 msix_index; + __le16 reserved12; + __le32 reserved14; + __le64 base_address; +}; + +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_MASK (0x80) +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80) +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00) +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_MASK (0x01) +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_DISABLE (0x00) +#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE (0x01) +struct mpi3_delete_reply_queue_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 queue_id; +}; + +struct mpi3_port_enable_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; +}; + +#define MPI3_EVENT_LOG_DATA (0x01) +#define MPI3_EVENT_CHANGE (0x02) +#define MPI3_EVENT_GPIO_INTERRUPT (0x04) +#define MPI3_EVENT_TEMP_THRESHOLD (0x05) +#define MPI3_EVENT_CABLE_MGMT (0x06) +#define MPI3_EVENT_DEVICE_ADDED (0x07) +#define MPI3_EVENT_DEVICE_INFO_CHANGED (0x08) +#define MPI3_EVENT_PREPARE_FOR_RESET (0x09) +#define MPI3_EVENT_COMP_IMAGE_ACT_START (0x0a) +#define MPI3_EVENT_ENCL_DEVICE_ADDED (0x0b) +#define MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE (0x0c) +#define MPI3_EVENT_DEVICE_STATUS_CHANGE (0x0d) +#define MPI3_EVENT_ENERGY_PACK_CHANGE (0x0e) +#define MPI3_EVENT_SAS_DISCOVERY (0x11) +#define MPI3_EVENT_SAS_BROADCAST_PRIMITIVE (0x12) +#define MPI3_EVENT_SAS_NOTIFY_PRIMITIVE (0x13) +#define MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x14) +#define MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW (0x15) +#define MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x16) +#define MPI3_EVENT_SAS_PHY_COUNTER (0x18) +#define MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR (0x19) +#define MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST (0x20) +#define MPI3_EVENT_PCIE_ENUMERATION (0x22) +#define MPI3_EVENT_HARD_RESET_RECEIVED (0x40) +#define MPI3_EVENT_MIN_PRODUCT_SPECIFIC (0x60) +#define MPI3_EVENT_MAX_PRODUCT_SPECIFIC (0x7f) +#define MPI3_EVENT_NOTIFY_EVENTMASK_WORDS (4) +struct mpi3_event_notification_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + __le16 sas_broadcast_primitive_masks; + __le16 sas_notify_primitive_masks; + __le32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS]; +}; + +struct mpi3_event_notification_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + u8 event_data_length; + u8 event; + __le16 ioc_change_count; + __le32 event_context; + __le32 event_data[1]; +}; + +#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK (0x01) +#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED (0x01) +#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_NOT_REQUIRED (0x00) +#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_MASK (0x02) +#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_ORIGINAL (0x00) +#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_REPLAY (0x02) +struct mpi3_event_data_gpio_interrupt { + u8 gpio_num; + u8 reserved01[3]; +}; + +struct mpi3_event_data_temp_threshold { + __le16 status; + u8 sensor_num; + u8 reserved03; + __le16 current_temperature; + __le16 reserved06; + __le32 reserved08; + __le32 reserved0c; +}; + +#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD3_EXCEEDED (0x0008) +#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD2_EXCEEDED (0x0004) +#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD1_EXCEEDED (0x0002) +#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD0_EXCEEDED (0x0001) +struct mpi3_event_data_cable_management { + __le32 active_cable_power_requirement; + u8 status; + u8 receptacle_id; + __le16 reserved06; +}; + +#define MPI3_EVENT_CABLE_MGMT_ACT_CABLE_PWR_INVALID (0xffffffff) +#define MPI3_EVENT_CABLE_MGMT_STATUS_INSUFFICIENT_POWER (0x00) +#define MPI3_EVENT_CABLE_MGMT_STATUS_PRESENT (0x01) +#define MPI3_EVENT_CABLE_MGMT_STATUS_DEGRADED (0x02) +struct mpi3_event_ack_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + u8 event; + u8 reserved0d[3]; + __le32 event_context; +}; + +struct mpi3_event_data_prepare_for_reset { + u8 reason_code; + u8 reserved01; + __le16 reserved02; +}; + +#define MPI3_EVENT_PREPARE_RESET_RC_START (0x01) +#define MPI3_EVENT_PREPARE_RESET_RC_ABORT (0x02) +struct mpi3_event_data_comp_image_activation { + __le32 reserved00; +}; + +struct mpi3_event_data_device_status_change { + __le16 task_tag; + u8 reason_code; + u8 io_unit_port; + __le16 parent_dev_handle; + __le16 dev_handle; + __le64 wwid; + u8 lun[8]; +}; + +#define MPI3_EVENT_DEV_STAT_RC_MOVED (0x01) +#define MPI3_EVENT_DEV_STAT_RC_HIDDEN (0x02) +#define MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN (0x03) +#define MPI3_EVENT_DEV_STAT_RC_ASYNC_NOTIFICATION (0x04) +#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT (0x20) +#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP (0x21) +#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_STRT (0x22) +#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_CMP (0x23) +#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT (0x24) +#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP (0x25) +#define MPI3_EVENT_DEV_STAT_RC_PCIE_HOT_RESET_FAILED (0x30) +#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_STRT (0x40) +#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_CMP (0x41) +#define MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING (0x50) +struct mpi3_event_data_energy_pack_change { + __le32 reserved00; + __le16 shutdown_timeout; + __le16 reserved06; +}; + +struct mpi3_event_data_sas_discovery { + u8 flags; + u8 reason_code; + u8 io_unit_port; + u8 reserved03; + __le32 discovery_status; +}; + +#define MPI3_EVENT_SAS_DISC_FLAGS_DEVICE_CHANGE (0x02) +#define MPI3_EVENT_SAS_DISC_FLAGS_IN_PROGRESS (0x01) +#define MPI3_EVENT_SAS_DISC_RC_STARTED (0x01) +#define MPI3_EVENT_SAS_DISC_RC_COMPLETED (0x02) +#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEED (0x80000000) +#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEED (0x40000000) +#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEED (0x20000000) +#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEED (0x10000000) +#define MPI3_SAS_DISC_STATUS_MULTIPLE_DEVICES_IN_SLOT (0x00004000) +#define MPI3_SAS_DISC_STATUS_SLOT_COUNT_MISMATCH (0x00002000) +#define MPI3_SAS_DISC_STATUS_TOO_MANY_SLOTS (0x00001000) +#define MPI3_SAS_DISC_STATUS_EXP_MULTI_SUBTRACTIVE (0x00000800) +#define MPI3_SAS_DISC_STATUS_MULTI_PORT_DOMAIN (0x00000400) +#define MPI3_SAS_DISC_STATUS_TABLE_TO_SUBTRACTIVE_LINK (0x00000200) +#define MPI3_SAS_DISC_STATUS_UNSUPPORTED_DEVICE (0x00000100) +#define MPI3_SAS_DISC_STATUS_TABLE_LINK (0x00000080) +#define MPI3_SAS_DISC_STATUS_SUBTRACTIVE_LINK (0x00000040) +#define MPI3_SAS_DISC_STATUS_SMP_CRC_ERROR (0x00000020) +#define MPI3_SAS_DISC_STATUS_SMP_FUNCTION_FAILED (0x00000010) +#define MPI3_SAS_DISC_STATUS_SMP_TIMEOUT (0x00000008) +#define MPI3_SAS_DISC_STATUS_MULTIPLE_PORTS (0x00000004) +#define MPI3_SAS_DISC_STATUS_INVALID_SAS_ADDRESS (0x00000002) +#define MPI3_SAS_DISC_STATUS_LOOP_DETECTED (0x00000001) +struct mpi3_event_data_sas_broadcast_primitive { + u8 phy_num; + u8 io_unit_port; + u8 port_width; + u8 primitive; +}; + +#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE (0x01) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_SES (0x02) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_EXPANDER (0x03) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED3 (0x05) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED4 (0x06) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE0_RESERVED (0x07) +#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE1_RESERVED (0x08) +struct mpi3_event_data_sas_notify_primitive { + u8 phy_num; + u8 io_unit_port; + u8 reserved02; + u8 primitive; +}; + +#define MPI3_EVENT_NOTIFY_PRIMITIVE_ENABLE_SPINUP (0x01) +#define MPI3_EVENT_NOTIFY_PRIMITIVE_POWER_LOSS_EXPECTED (0x02) +#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED1 (0x03) +#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED2 (0x04) +#ifndef MPI3_EVENT_SAS_TOPO_PHY_COUNT +#define MPI3_EVENT_SAS_TOPO_PHY_COUNT (1) +#endif +struct mpi3_event_sas_topo_phy_entry { + __le16 attached_dev_handle; + u8 link_rate; + u8 status; +}; + +#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xf0) +#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4) +#define MPI3_EVENT_SAS_TOPO_LR_PREV_MASK (0x0f) +#define MPI3_EVENT_SAS_TOPO_LR_PREV_SHIFT (0) +#define MPI3_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00) +#define MPI3_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01) +#define MPI3_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02) +#define MPI3_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) +#define MPI3_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) +#define MPI3_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) +#define MPI3_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06) +#define MPI3_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0a) +#define MPI3_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0b) +#define MPI3_EVENT_SAS_TOPO_LR_RATE_22_5 (0x0c) +#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_MASK (0xc0) +#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_SHIFT (6) +#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_ACCESSIBLE (0x00) +#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_NO_EXIST (0x40) +#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_VACANT (0x80) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_MASK (0x0f) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING (0x02) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED (0x03) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE (0x04) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING (0x05) +#define MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING (0x06) +struct mpi3_event_data_sas_topology_change_list { + __le16 enclosure_handle; + __le16 expander_dev_handle; + u8 num_phys; + u8 reserved05[3]; + u8 num_entries; + u8 start_phy_num; + u8 exp_status; + u8 io_unit_port; + struct mpi3_event_sas_topo_phy_entry phy_entry[MPI3_EVENT_SAS_TOPO_PHY_COUNT]; +}; + +#define MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00) +#define MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) +#define MPI3_EVENT_SAS_TOPO_ES_RESPONDING (0x03) +#define MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04) +struct mpi3_event_data_sas_phy_counter { + __le64 time_stamp; + __le32 reserved08; + u8 phy_event_code; + u8 phy_num; + __le16 reserved0e; + __le32 phy_event_info; + u8 counter_type; + u8 threshold_window; + u8 time_units; + u8 reserved17; + __le32 event_threshold; + __le16 threshold_flags; + __le16 reserved1e; +}; + +struct mpi3_event_data_sas_device_disc_err { + __le16 dev_handle; + u8 reason_code; + u8 io_unit_port; + __le32 reserved04; + __le64 sas_address; +}; + +#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_FAILED (0x01) +#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_TIMEOUT (0x02) +struct mpi3_event_data_pcie_enumeration { + u8 flags; + u8 reason_code; + u8 io_unit_port; + u8 reserved03; + __le32 enumeration_status; +}; + +#define MPI3_EVENT_PCIE_ENUM_FLAGS_DEVICE_CHANGE (0x02) +#define MPI3_EVENT_PCIE_ENUM_FLAGS_IN_PROGRESS (0x01) +#define MPI3_EVENT_PCIE_ENUM_RC_STARTED (0x01) +#define MPI3_EVENT_PCIE_ENUM_RC_COMPLETED (0x02) +#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCH_DEPTH_EXCEED (0x80000000) +#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCHES_EXCEED (0x40000000) +#define MPI3_EVENT_PCIE_ENUM_ES_MAX_DEVICES_EXCEED (0x20000000) +#define MPI3_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED (0x10000000) +#ifndef MPI3_EVENT_PCIE_TOPO_PORT_COUNT +#define MPI3_EVENT_PCIE_TOPO_PORT_COUNT (1) +#endif +struct mpi3_event_pcie_topo_port_entry { + __le16 attached_dev_handle; + u8 port_status; + u8 reserved03; + u8 current_port_info; + u8 reserved05; + u8 previous_port_info; + u8 reserved07; +}; + +#define MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING (0x02) +#define MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED (0x03) +#define MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE (0x04) +#define MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING (0x05) +#define MPI3_EVENT_PCIE_TOPO_PS_RESPONDING (0x06) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_MASK (0xf0) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_UNKNOWN (0x00) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_1 (0x10) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_2 (0x20) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_4 (0x30) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_8 (0x40) +#define MPI3_EVENT_PCIE_TOPO_PI_LANES_16 (0x50) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK (0x0f) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_UNKNOWN (0x00) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_DISABLED (0x01) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_2_5 (0x02) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_5_0 (0x03) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_8_0 (0x04) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_16_0 (0x05) +#define MPI3_EVENT_PCIE_TOPO_PI_RATE_32_0 (0x06) +struct mpi3_event_data_pcie_topology_change_list { + __le16 enclosure_handle; + __le16 switch_dev_handle; + u8 num_ports; + u8 reserved05[3]; + u8 num_entries; + u8 start_port_num; + u8 switch_status; + u8 io_unit_port; + __le32 reserved0c; + struct mpi3_event_pcie_topo_port_entry port_entry[MPI3_EVENT_PCIE_TOPO_PORT_COUNT]; +}; + +#define MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH (0x00) +#define MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING (0x02) +#define MPI3_EVENT_PCIE_TOPO_SS_RESPONDING (0x03) +#define MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING (0x04) +struct mpi3_event_data_sas_init_dev_status_change { + u8 reason_code; + u8 io_unit_port; + __le16 dev_handle; + __le32 reserved04; + __le64 sas_address; +}; + +#define MPI3_EVENT_SAS_INIT_RC_ADDED (0x01) +#define MPI3_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02) +struct mpi3_event_data_sas_init_table_overflow { + __le16 max_init; + __le16 current_init; + __le32 reserved04; + __le64 sas_address; +}; + +struct mpi3_event_data_hard_reset_received { + u8 reserved00; + u8 io_unit_port; + __le16 reserved02; +}; + +#define MPI3_PEL_LOCALE_FLAGS_NON_BLOCKING_BOOT_EVENT (0x0200) +#define MPI3_PEL_LOCALE_FLAGS_BLOCKING_BOOT_EVENT (0x0100) +#define MPI3_PEL_LOCALE_FLAGS_PCIE (0x0080) +#define MPI3_PEL_LOCALE_FLAGS_CONFIGURATION (0x0040) +#define MPI3_PEL_LOCALE_FLAGS_CONTROLER (0x0020) +#define MPI3_PEL_LOCALE_FLAGS_SAS (0x0010) +#define MPI3_PEL_LOCALE_FLAGS_EPACK (0x0008) +#define MPI3_PEL_LOCALE_FLAGS_ENCLOSURE (0x0004) +#define MPI3_PEL_LOCALE_FLAGS_PD (0x0002) +#define MPI3_PEL_LOCALE_FLAGS_VD (0x0001) +#define MPI3_PEL_CLASS_DEBUG (0x00) +#define MPI3_PEL_CLASS_PROGRESS (0x01) +#define MPI3_PEL_CLASS_INFORMATIONAL (0x02) +#define MPI3_PEL_CLASS_WARNING (0x03) +#define MPI3_PEL_CLASS_CRITICAL (0x04) +#define MPI3_PEL_CLASS_FATAL (0x05) +#define MPI3_PEL_CLASS_FAULT (0x06) +#define MPI3_PEL_CLEARTYPE_CLEAR (0x00) +#define MPI3_PEL_WAITTIME_INFINITE_WAIT (0x00) +#define MPI3_PEL_ACTION_GET_SEQNUM (0x01) +#define MPI3_PEL_ACTION_MARK_CLEAR (0x02) +#define MPI3_PEL_ACTION_GET_LOG (0x03) +#define MPI3_PEL_ACTION_GET_COUNT (0x04) +#define MPI3_PEL_ACTION_WAIT (0x05) +#define MPI3_PEL_ACTION_ABORT (0x06) +#define MPI3_PEL_ACTION_GET_PRINT_STRINGS (0x07) +#define MPI3_PEL_ACTION_ACKNOWLEDGE (0x08) +#define MPI3_PEL_STATUS_SUCCESS (0x00) +#define MPI3_PEL_STATUS_NOT_FOUND (0x01) +#define MPI3_PEL_STATUS_ABORTED (0x02) +#define MPI3_PEL_STATUS_NOT_READY (0x03) +struct mpi3_pel_seq { + __le32 newest; + __le32 oldest; + __le32 clear; + __le32 shutdown; + __le32 boot; + __le32 last_acknowledged; +}; + +struct mpi3_pel_entry { + __le32 sequence_number; + __le32 time_stamp[2]; + __le16 log_code; + __le16 arg_type; + __le16 locale; + u8 class; + u8 reserved13; + u8 ext_num; + u8 num_exts; + u8 arg_data_size; + u8 fixed_format_size; + __le32 reserved18[2]; + __le32 pel_info[24]; +}; + +struct mpi3_pel_list { + __le32 log_count; + __le32 reserved04; + struct mpi3_pel_entry entry[1]; +}; + +struct mpi3_pel_arg_map { + u8 arg_type; + u8 length; + __le16 start_location; +}; + +#define MPI3_PEL_ARG_MAP_ARG_TYPE_APPEND_STRING (0x00) +#define MPI3_PEL_ARG_MAP_ARG_TYPE_INTEGER (0x01) +#define MPI3_PEL_ARG_MAP_ARG_TYPE_STRING (0x02) +#define MPI3_PEL_ARG_MAP_ARG_TYPE_BIT_FIELD (0x03) +struct mpi3_pel_print_string { + __le16 log_code; + __le16 string_length; + u8 num_arg_map; + u8 reserved05[3]; + struct mpi3_pel_arg_map arg_map[1]; +}; + +struct mpi3_pel_print_string_list { + __le32 num_print_strings; + __le32 residual_bytes_remain; + __le32 reserved08[2]; + struct mpi3_pel_print_string print_string[1]; +}; + +#ifndef MPI3_PEL_ACTION_SPECIFIC_MAX +#define MPI3_PEL_ACTION_SPECIFIC_MAX (1) +#endif +struct mpi3_pel_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 action_specific[MPI3_PEL_ACTION_SPECIFIC_MAX]; +}; + +struct mpi3_pel_req_action_get_sequence_numbers { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 reserved0c[5]; + union mpi3_sge_union sgl; +}; + +struct mpi3_pel_req_action_clear_log_marker { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + u8 clear_type; + u8 reserved0d[3]; +}; + +struct mpi3_pel_req_action_get_log { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 starting_sequence_number; + __le16 locale; + u8 class; + u8 reserved13; + __le32 reserved14[3]; + union mpi3_sge_union sgl; +}; + +struct mpi3_pel_req_action_get_count { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 starting_sequence_number; + __le16 locale; + u8 class; + u8 reserved13; + __le32 reserved14[3]; + union mpi3_sge_union sgl; +}; + +struct mpi3_pel_req_action_wait { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 starting_sequence_number; + __le16 locale; + u8 class; + u8 reserved13; + __le16 wait_time; + __le16 reserved16; + __le32 reserved18[2]; +}; + +struct mpi3_pel_req_action_abort { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 reserved0c; + __le16 abort_host_tag; + __le16 reserved12; + __le32 reserved14; +}; + +struct mpi3_pel_req_action_get_print_strings { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 reserved0c; + __le16 start_log_code; + __le16 reserved12; + __le32 reserved14[3]; + union mpi3_sge_union sgl; +}; + +struct mpi3_pel_req_action_acknowledge { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 sequence_number; + __le32 reserved10; +}; + +#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT (0x01) +struct mpi3_pel_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + u8 action; + u8 reserved11; + __le16 reserved12; + __le16 pe_log_status; + __le16 reserved16; + __le32 transfer_length; +}; + +struct mpi3_ci_download_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 action; + u8 reserved0b; + __le32 signature1; + __le32 total_image_size; + __le32 image_offset; + __le32 segment_size; + __le32 reserved1c; + union mpi3_sge_union sgl; +}; + +#define MPI3_CI_DOWNLOAD_MSGFLAGS_LAST_SEGMENT (0x80) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_FORCE_FMC_ENABLE (0x40) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_SIGNED_NVDATA (0x20) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MASK (0x03) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_FAST (0x00) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MEDIUM (0x01) +#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_SLOW (0x02) +#define MPI3_CI_DOWNLOAD_ACTION_DOWNLOAD (0x01) +#define MPI3_CI_DOWNLOAD_ACTION_ONLINE_ACTIVATION (0x02) +#define MPI3_CI_DOWNLOAD_ACTION_OFFLINE_ACTIVATION (0x03) +#define MPI3_CI_DOWNLOAD_ACTION_GET_STATUS (0x04) +struct mpi3_ci_download_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + u8 flags; + u8 cache_dirty; + u8 pending_count; + u8 reserved13; +}; + +#define MPI3_CI_DOWNLOAD_FLAGS_DOWNLOAD_IN_PROGRESS (0x80) +#define MPI3_CI_DOWNLOAD_FLAGS_KEY_UPDATE_PENDING (0x10) +#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_MASK (0x0e) +#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_NOT_NEEDED (0x00) +#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_AWAITING (0x02) +#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_ONLINE_PENDING (0x04) +#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_OFFLINE_PENDING (0x06) +#define MPI3_CI_DOWNLOAD_FLAGS_COMPATIBLE (0x01) +struct mpi3_ci_upload_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 reserved0a; + __le32 signature1; + __le32 reserved10; + __le32 image_offset; + __le32 segment_size; + __le32 reserved1c; + union mpi3_sge_union sgl; +}; + +#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_MASK (0x01) +#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_PRIMARY (0x00) +#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_SECONDARY (0x01) +#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_MASK (0x02) +#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_FLASH (0x00) +#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_EXECUTABLE (0x02) +#define MPI3_CTRL_OP_FORCE_FULL_DISCOVERY (0x01) +#define MPI3_CTRL_OP_LOOKUP_MAPPING (0x02) +#define MPI3_CTRL_OP_UPDATE_TIMESTAMP (0x04) +#define MPI3_CTRL_OP_GET_TIMESTAMP (0x05) +#define MPI3_CTRL_OP_REMOVE_DEVICE (0x10) +#define MPI3_CTRL_OP_CLOSE_PERSISTENT_CONNECTION (0x11) +#define MPI3_CTRL_OP_HIDDEN_ACK (0x12) +#define MPI3_CTRL_OP_SAS_SEND_PRIMITIVE (0x20) +#define MPI3_CTRL_OP_SAS_CLEAR_ERROR_LOG (0x21) +#define MPI3_CTRL_OP_PCIE_CLEAR_ERROR_LOG (0x22) +#define MPI3_CTRL_OP_LOOKUP_MAPPING_PARAM8_LOOKUP_METHOD_INDEX (0x00) +#define MPI3_CTRL_OP_UPDATE_TIMESTAMP_PARAM64_TIMESTAMP_INDEX (0x00) +#define MPI3_CTRL_OP_REMOVE_DEVICE_PARAM16_DEVHANDLE_INDEX (0x00) +#define MPI3_CTRL_OP_CLOSE_PERSIST_CONN_PARAM16_DEVHANDLE_INDEX (0x00) +#define MPI3_CTRL_OP_HIDDEN_ACK_PARAM16_DEVHANDLE_INDEX (0x00) +#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM8_PHY_INDEX (0x00) +#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM8_PRIMSEQ_INDEX (0x01) +#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM32_PRIMITIVE_INDEX (0x00) +#define MPI3_CTRL_OP_SAS_CLEAR_ERR_LOG_PARAM8_PHY_INDEX (0x00) +#define MPI3_CTRL_OP_PCIE_CLEAR_ERR_LOG_PARAM8_PHY_INDEX (0x00) +#define MPI3_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01) +#define MPI3_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02) +#define MPI3_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) +#define MPI3_CTRL_LOOKUP_METHOD_PERSISTENT_ID (0x04) +#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM16_DEVH_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM64_WWID_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM16_SLOTNUM_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM64_ENCLOSURELID_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM16_DEVH_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM64_DEVNAME_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_DEVH_INDEX (0) +#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_PERSISTENT_ID_INDEX (1) +#define MPI3_CTRL_LOOKUP_METHOD_VALUE16_DEVH_INDEX (0) +#define MPI3_CTRL_GET_TIMESTAMP_VALUE64_TIMESTAMP_INDEX (0) +#define MPI3_CTRL_PRIMFLAGS_SINGLE (0x01) +#define MPI3_CTRL_PRIMFLAGS_TRIPLE (0x03) +#define MPI3_CTRL_PRIMFLAGS_REDUNDANT (0x06) +struct mpi3_iounit_control_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 reserved0a; + u8 operation; + __le32 reserved0c; + __le64 param64[2]; + __le32 param32[4]; + __le16 param16[4]; + u8 param8[8]; +}; + +struct mpi3_iounit_control_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; + __le64 value64[2]; + __le32 value32[4]; + __le16 value16[4]; + u8 value8[8]; +}; +#endif diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_sas.h b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h new file mode 100644 index 000000000000..ba5018702960 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2016-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_SAS_H +#define MPI30_SAS_H 1 +#define MPI3_SAS_DEVICE_INFO_SSP_TARGET (0x00000100) +#define MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET (0x00000080) +#define MPI3_SAS_DEVICE_INFO_SMP_TARGET (0x00000040) +#define MPI3_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000020) +#define MPI3_SAS_DEVICE_INFO_STP_INITIATOR (0x00000010) +#define MPI3_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000008) +#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK (0x00000007) +#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE (0x00000000) +#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE (0x00000001) +#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER (0x00000002) +struct mpi3_smp_passthrough_request { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + u8 reserved0a; + u8 io_unit_port; + __le32 reserved0c[3]; + __le64 sas_address; + struct mpi3_sge_common request_sge; + struct mpi3_sge_common response_sge; +}; +#endif diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h new file mode 100644 index 000000000000..63e4e81d5397 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h @@ -0,0 +1,463 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2016-2021 Broadcom Inc. All rights reserved. + * + */ +#ifndef MPI30_TRANSPORT_H +#define MPI30_TRANSPORT_H 1 +struct mpi3_version_struct { + u8 dev; + u8 unit; + u8 minor; + u8 major; +}; + +union mpi3_version_union { + struct mpi3_version_struct mpi3_version; + __le32 word; +}; + +#define MPI3_VERSION_MAJOR (3) +#define MPI3_VERSION_MINOR (0) +#define MPI3_VERSION_UNIT (0) +#define MPI3_VERSION_DEV (18) +struct mpi3_sysif_oper_queue_indexes { + __le16 producer_index; + __le16 reserved02; + __le16 consumer_index; + __le16 reserved06; +}; + +struct mpi3_sysif_registers { + __le64 ioc_information; + union mpi3_version_union version; + __le32 reserved0c[2]; + __le32 ioc_configuration; + __le32 reserved18; + __le32 ioc_status; + __le32 reserved20; + __le32 admin_queue_num_entries; + __le64 admin_request_queue_address; + __le64 admin_reply_queue_address; + __le32 reserved38[2]; + __le32 coalesce_control; + __le32 reserved44[1007]; + __le16 admin_request_queue_pi; + __le16 reserved1002; + __le16 admin_reply_queue_ci; + __le16 reserved1006; + struct mpi3_sysif_oper_queue_indexes oper_queue_indexes[383]; + __le32 reserved1c00; + __le32 write_sequence; + __le32 host_diagnostic; + __le32 reserved1c0c; + __le32 fault; + __le32 fault_info[3]; + __le32 reserved1c20[4]; + __le64 hcb_address; + __le32 hcb_size; + __le32 reserved1c3c; + __le32 reply_free_host_index; + __le32 sense_buffer_free_host_index; + __le32 reserved1c48[2]; + __le64 diag_rw_data; + __le64 diag_rw_address; + __le16 diag_rw_control; + __le16 diag_rw_status; + __le32 reserved1c64[35]; + __le32 scratchpad[4]; + __le32 reserved1d00[192]; + __le32 device_assigned_registers[2048]; +}; + +#define MPI3_SYSIF_IOC_INFO_LOW_OFFSET (0x00000000) +#define MPI3_SYSIF_IOC_INFO_HIGH_OFFSET (0x00000004) +#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK (0xff000000) +#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT (24) +#define MPI3_SYSIF_IOC_CONFIG_OFFSET (0x00000014) +#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ (0x00f00000) +#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT (20) +#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ (0x000f0000) +#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT (16) +#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_MASK (0x0000c000) +#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NO (0x00000000) +#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL (0x00004000) +#define MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN (0x00002000) +#define MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE (0x00000010) +#define MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC (0x00000001) +#define MPI3_SYSIF_IOC_STATUS_OFFSET (0x0000001c) +#define MPI3_SYSIF_IOC_STATUS_RESET_HISTORY (0x00000010) +#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK (0x0000000c) +#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_NONE (0x00000000) +#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS (0x00000004) +#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE (0x00000008) +#define MPI3_SYSIF_IOC_STATUS_FAULT (0x00000002) +#define MPI3_SYSIF_IOC_STATUS_READY (0x00000001) +#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_OFFSET (0x00000024) +#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REQ_MASK (0x0fff) +#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_OFFSET (0x00000026) +#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_MASK (0x0fff0000) +#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_SHIFT (16) +#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_LOW_OFFSET (0x00000028) +#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_HIGH_OFFSET (0x0000002c) +#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_LOW_OFFSET (0x00000030) +#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_HIGH_OFFSET (0x00000034) +#define MPI3_SYSIF_COALESCE_CONTROL_OFFSET (0x00000040) +#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_MASK (0xc0000000) +#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_NO_CHANGE (0x00000000) +#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_DISABLE (0x40000000) +#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_ENABLE (0xc0000000) +#define MPI3_SYSIF_COALESCE_CONTROL_VALID (0x30000000) +#define MPI3_SYSIF_COALESCE_CONTROL_QUEUE_ID_MASK (0x00ff0000) +#define MPI3_SYSIF_COALESCE_CONTROL_QUEUE_ID_SHIFT (16) +#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_MASK (0x0000ff00) +#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_SHIFT (8) +#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_MASK (0x000000ff) +#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_SHIFT (0) +#define MPI3_SYSIF_ADMIN_REQ_Q_PI_OFFSET (0x00001000) +#define MPI3_SYSIF_ADMIN_REPLY_Q_CI_OFFSET (0x00001004) +#define MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET (0x00001008) +#define MPI3_SYSIF_OPER_REQ_Q_N_PI_OFFSET(n) (MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET + (((n) - 1) * 8)) +#define MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET (0x0000100c) +#define MPI3_SYSIF_OPER_REPLY_Q_N_CI_OFFSET(n) (MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET + (((n) - 1) * 8)) +#define MPI3_SYSIF_WRITE_SEQUENCE_OFFSET (0x00001c04) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_MASK (0x0000000f) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH (0x0) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST (0xf) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND (0x4) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD (0xb) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH (0x2) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH (0x7) +#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH (0xd) +#define MPI3_SYSIF_HOST_DIAG_OFFSET (0x00001c08) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_MASK (0x00000700) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_NO_RESET (0x00000000) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET (0x00000100) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_FLASH_RCVRY_RESET (0x00000200) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_COMPLETE_RESET (0x00000300) +#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT (0x00000700) +#define MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS (0x00000080) +#define MPI3_SYSIF_HOST_DIAG_SECURE_BOOT (0x00000040) +#define MPI3_SYSIF_HOST_DIAG_CLEAR_INVALID_FW_IMAGE (0x00000020) +#define MPI3_SYSIF_HOST_DIAG_INVALID_FW_IMAGE (0x00000010) +#define MPI3_SYSIF_HOST_DIAG_HCBENABLE (0x00000008) +#define MPI3_SYSIF_HOST_DIAG_HCBMODE (0x00000004) +#define MPI3_SYSIF_HOST_DIAG_DIAG_RW_ENABLE (0x00000002) +#define MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE (0x00000001) +#define MPI3_SYSIF_FAULT_OFFSET (0x00001c10) +#define MPI3_SYSIF_FAULT_FUNC_AREA_MASK (0xff000000) +#define MPI3_SYSIF_FAULT_FUNC_AREA_SHIFT (24) +#define MPI3_SYSIF_FAULT_FUNC_AREA_MPI_DEFINED (0x00000000) +#define MPI3_SYSIF_FAULT_CODE_MASK (0x0000ffff) +#define MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET (0x0000f000) +#define MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET (0x0000f001) +#define MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS (0x0000f002) +#define MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED (0x0000f003) +#define MPI3_SYSIF_FAULT_CODE_SAFE_MODE_EXIT (0x0000f004) +#define MPI3_SYSIF_FAULT_CODE_FACTORY_RESET (0x0000f005) +#define MPI3_SYSIF_FAULT_INFO0_OFFSET (0x00001c14) +#define MPI3_SYSIF_FAULT_INFO1_OFFSET (0x00001c18) +#define MPI3_SYSIF_FAULT_INFO2_OFFSET (0x00001c1c) +#define MPI3_SYSIF_HCB_ADDRESS_LOW_OFFSET (0x00001c30) +#define MPI3_SYSIF_HCB_ADDRESS_HIGH_OFFSET (0x00001c34) +#define MPI3_SYSIF_HCB_SIZE_OFFSET (0x00001c38) +#define MPI3_SYSIF_HCB_SIZE_SIZE_MASK (0xfffff000) +#define MPI3_SYSIF_HCB_SIZE_SIZE_SHIFT (12) +#define MPI3_SYSIF_HCB_SIZE_HCDW_ENABLE (0x00000001) +#define MPI3_SYSIF_REPLY_FREE_HOST_INDEX_OFFSET (0x00001c40) +#define MPI3_SYSIF_SENSE_BUF_FREE_HOST_INDEX_OFFSET (0x00001c44) +#define MPI3_SYSIF_DIAG_RW_DATA_LOW_OFFSET (0x00001c50) +#define MPI3_SYSIF_DIAG_RW_DATA_HIGH_OFFSET (0x00001c54) +#define MPI3_SYSIF_DIAG_RW_ADDRESS_LOW_OFFSET (0x00001c58) +#define MPI3_SYSIF_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00001c5c) +#define MPI3_SYSIF_DIAG_RW_CONTROL_OFFSET (0x00001c60) +#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_MASK (0x00000030) +#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_1BYTE (0x00000000) +#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_2BYTES (0x00000010) +#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_4BYTES (0x00000020) +#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_8BYTES (0x00000030) +#define MPI3_SYSIF_DIAG_RW_CONTROL_RESET (0x00000004) +#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_MASK (0x00000002) +#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_READ (0x00000000) +#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_WRITE (0x00000002) +#define MPI3_SYSIF_DIAG_RW_CONTROL_START (0x00000001) +#define MPI3_SYSIF_DIAG_RW_STATUS_OFFSET (0x00001c62) +#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_MASK (0x0000000e) +#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_SUCCESS (0x00000000) +#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_INV_ADDR (0x00000002) +#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_ACC_ERR (0x00000004) +#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_PAR_ERR (0x00000006) +#define MPI3_SYSIF_DIAG_RW_STATUS_BUSY (0x00000001) +#define MPI3_SYSIF_SCRATCHPAD0_OFFSET (0x00001cf0) +#define MPI3_SYSIF_SCRATCHPAD1_OFFSET (0x00001cf4) +#define MPI3_SYSIF_SCRATCHPAD2_OFFSET (0x00001cf8) +#define MPI3_SYSIF_SCRATCHPAD3_OFFSET (0x00001cfc) +#define MPI3_SYSIF_DEVICE_ASSIGNED_REGS_OFFSET (0x00002000) +#define MPI3_SYSIF_DIAG_SAVE_TIMEOUT (60) +struct mpi3_default_reply_descriptor { + __le32 descriptor_type_dependent1[2]; + __le16 request_queue_ci; + __le16 request_queue_id; + __le16 descriptor_type_dependent2; + __le16 reply_flags; +}; + +#define MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK (0x0001) +#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK (0xf000) +#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY (0x0000) +#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS (0x1000) +#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_TARGET_COMMAND_BUFFER (0x2000) +#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS (0x3000) +struct mpi3_address_reply_descriptor { + __le64 reply_frame_address; + __le16 request_queue_ci; + __le16 request_queue_id; + __le16 reserved0c; + __le16 reply_flags; +}; + +struct mpi3_success_reply_descriptor { + __le32 reserved00[2]; + __le16 request_queue_ci; + __le16 request_queue_id; + __le16 host_tag; + __le16 reply_flags; +}; + +struct mpi3_target_command_buffer_reply_descriptor { + __le32 reserved00; + __le16 initiator_dev_handle; + u8 phy_num; + u8 reserved07; + __le16 request_queue_ci; + __le16 request_queue_id; + __le16 io_index; + __le16 reply_flags; +}; + +struct mpi3_status_reply_descriptor { + __le16 ioc_status; + __le16 reserved02; + __le32 ioc_log_info; + __le16 request_queue_ci; + __le16 request_queue_id; + __le16 host_tag; + __le16 reply_flags; +}; + +#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL (0x8000) +#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK (0x7fff) +#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_MASK (0xf0000000) +#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_NO_INFO (0x00000000) +#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_SAS (0x30000000) +#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_DATA_MASK (0x0fffffff) +union mpi3_reply_descriptors_union { + struct mpi3_default_reply_descriptor default_reply; + struct mpi3_address_reply_descriptor address_reply; + struct mpi3_success_reply_descriptor success; + struct mpi3_target_command_buffer_reply_descriptor target_command_buffer; + struct mpi3_status_reply_descriptor status; + __le32 words[4]; +}; + +struct mpi3_sge_common { + __le64 address; + __le32 length; + u8 reserved0c[3]; + u8 flags; +}; + +struct mpi3_sge_bit_bucket { + __le64 reserved00; + __le32 length; + u8 reserved0c[3]; + u8 flags; +}; + +struct mpi3_sge_extended_eedp { + u8 user_data_size; + u8 reserved01; + __le16 eedp_flags; + __le32 secondary_reference_tag; + __le16 secondary_application_tag; + __le16 application_tag_translation_mask; + __le16 reserved0c; + u8 extended_operation; + u8 flags; +}; + +union mpi3_sge_union { + struct mpi3_sge_common simple; + struct mpi3_sge_common chain; + struct mpi3_sge_common last_chain; + struct mpi3_sge_bit_bucket bit_bucket; + struct mpi3_sge_extended_eedp eedp; + __le32 words[4]; +}; + +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_MASK (0xf0) +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE (0x00) +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_BIT_BUCKET (0x10) +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_CHAIN (0x20) +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN (0x30) +#define MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED (0xf0) +#define MPI3_SGE_FLAGS_END_OF_LIST (0x08) +#define MPI3_SGE_FLAGS_END_OF_BUFFER (0x04) +#define MPI3_SGE_FLAGS_DLAS_MASK (0x03) +#define MPI3_SGE_FLAGS_DLAS_SYSTEM (0x00) +#define MPI3_SGE_FLAGS_DLAS_IOC_DDR (0x01) +#define MPI3_SGE_FLAGS_DLAS_IOC_CTL (0x02) +#define MPI3_SGE_EXT_OPER_EEDP (0x00) +#define MPI3_EEDPFLAGS_INCR_PRI_REF_TAG (0x8000) +#define MPI3_EEDPFLAGS_INCR_SEC_REF_TAG (0x4000) +#define MPI3_EEDPFLAGS_INCR_PRI_APP_TAG (0x2000) +#define MPI3_EEDPFLAGS_INCR_SEC_APP_TAG (0x1000) +#define MPI3_EEDPFLAGS_ESC_PASSTHROUGH (0x0800) +#define MPI3_EEDPFLAGS_CHK_REF_TAG (0x0400) +#define MPI3_EEDPFLAGS_CHK_APP_TAG (0x0200) +#define MPI3_EEDPFLAGS_CHK_GUARD (0x0100) +#define MPI3_EEDPFLAGS_ESC_MODE_MASK (0x00c0) +#define MPI3_EEDPFLAGS_ESC_MODE_DO_NOT_DISABLE (0x0040) +#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE (0x0080) +#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_REFTAG_DISABLE (0x00c0) +#define MPI3_EEDPFLAGS_HOST_GUARD_MASK (0x0030) +#define MPI3_EEDPFLAGS_HOST_GUARD_T10_CRC (0x0000) +#define MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM (0x0010) +#define MPI3_EEDPFLAGS_HOST_GUARD_OEM_SPECIFIC (0x0020) +#define MPI3_EEDPFLAGS_PT_REF_TAG (0x0008) +#define MPI3_EEDPFLAGS_EEDP_OP_MASK (0x0007) +#define MPI3_EEDPFLAGS_EEDP_OP_NOOP (0x0000) +#define MPI3_EEDPFLAGS_EEDP_OP_CHECK (0x0001) +#define MPI3_EEDPFLAGS_EEDP_OP_STRIP (0x0002) +#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE (0x0003) +#define MPI3_EEDPFLAGS_EEDP_OP_INSERT (0x0004) +#define MPI3_EEDPFLAGS_EEDP_OP_REPLACE (0x0006) +#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN (0x0007) +#define MPI3_EEDP_UDS_512 (0x01) +#define MPI3_EEDP_UDS_520 (0x02) +#define MPI3_EEDP_UDS_4080 (0x03) +#define MPI3_EEDP_UDS_4088 (0x04) +#define MPI3_EEDP_UDS_4096 (0x05) +#define MPI3_EEDP_UDS_4104 (0x06) +#define MPI3_EEDP_UDS_4160 (0x07) +struct mpi3_request_header { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 change_count; + __le16 function_dependent; +}; + +struct mpi3_default_reply { + __le16 host_tag; + u8 ioc_use_only02; + u8 function; + __le16 ioc_use_only04; + u8 ioc_use_only06; + u8 msg_flags; + __le16 ioc_use_only08; + __le16 ioc_status; + __le32 ioc_log_info; +}; + +#define MPI3_HOST_TAG_INVALID (0xffff) +#define MPI3_FUNCTION_IOC_FACTS (0x01) +#define MPI3_FUNCTION_IOC_INIT (0x02) +#define MPI3_FUNCTION_PORT_ENABLE (0x03) +#define MPI3_FUNCTION_EVENT_NOTIFICATION (0x04) +#define MPI3_FUNCTION_EVENT_ACK (0x05) +#define MPI3_FUNCTION_CI_DOWNLOAD (0x06) +#define MPI3_FUNCTION_CI_UPLOAD (0x07) +#define MPI3_FUNCTION_IO_UNIT_CONTROL (0x08) +#define MPI3_FUNCTION_PERSISTENT_EVENT_LOG (0x09) +#define MPI3_FUNCTION_MGMT_PASSTHROUGH (0x0a) +#define MPI3_FUNCTION_CONFIG (0x10) +#define MPI3_FUNCTION_SCSI_IO (0x20) +#define MPI3_FUNCTION_SCSI_TASK_MGMT (0x21) +#define MPI3_FUNCTION_SMP_PASSTHROUGH (0x22) +#define MPI3_FUNCTION_NVME_ENCAPSULATED (0x24) +#define MPI3_FUNCTION_TARGET_ASSIST (0x30) +#define MPI3_FUNCTION_TARGET_STATUS_SEND (0x31) +#define MPI3_FUNCTION_TARGET_MODE_ABORT (0x32) +#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_BASE (0x33) +#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_LIST (0x34) +#define MPI3_FUNCTION_CREATE_REQUEST_QUEUE (0x70) +#define MPI3_FUNCTION_DELETE_REQUEST_QUEUE (0x71) +#define MPI3_FUNCTION_CREATE_REPLY_QUEUE (0x72) +#define MPI3_FUNCTION_DELETE_REPLY_QUEUE (0x73) +#define MPI3_FUNCTION_TOOLBOX (0x80) +#define MPI3_FUNCTION_DIAG_BUFFER_POST (0x81) +#define MPI3_FUNCTION_DIAG_BUFFER_MANAGE (0x82) +#define MPI3_FUNCTION_DIAG_BUFFER_UPLOAD (0x83) +#define MPI3_FUNCTION_MIN_IOC_USE_ONLY (0xc0) +#define MPI3_FUNCTION_MAX_IOC_USE_ONLY (0xef) +#define MPI3_FUNCTION_MIN_PRODUCT_SPECIFIC (0xf0) +#define MPI3_FUNCTION_MAX_PRODUCT_SPECIFIC (0xff) +#define MPI3_IOCSTATUS_LOG_INFO_AVAIL_MASK (0x8000) +#define MPI3_IOCSTATUS_LOG_INFO_AVAILABLE (0x8000) +#define MPI3_IOCSTATUS_STATUS_MASK (0x7fff) +#define MPI3_IOCSTATUS_SUCCESS (0x0000) +#define MPI3_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI3_IOCSTATUS_BUSY (0x0002) +#define MPI3_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI3_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI3_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI3_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI3_IOCSTATUS_INSUFFICIENT_POWER (0x000a) +#define MPI3_IOCSTATUS_INVALID_CHANGE_COUNT (0x000b) +#define MPI3_IOCSTATUS_FAILURE (0x001f) +#define MPI3_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI3_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI3_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI3_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI3_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI3_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) +#define MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI3_IOCSTATUS_SCSI_TM_NOT_SUPPORTED (0x0041) +#define MPI3_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) +#define MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI3_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI3_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004a) +#define MPI3_IOCSTATUS_SCSI_IOC_TERMINATED (0x004b) +#define MPI3_IOCSTATUS_SCSI_EXT_TERMINATED (0x004c) +#define MPI3_IOCSTATUS_EEDP_GUARD_ERROR (0x004d) +#define MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004e) +#define MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004f) +#define MPI3_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) +#define MPI3_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI3_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI3_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI3_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006a) +#define MPI3_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006d) +#define MPI3_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006e) +#define MPI3_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006f) +#define MPI3_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) +#define MPI3_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) +#define MPI3_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) +#define MPI3_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) +#define MPI3_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00a0) +#define MPI3_IOCSTATUS_CI_UNSUPPORTED (0x00b0) +#define MPI3_IOCSTATUS_CI_UPDATE_SEQUENCE (0x00b1) +#define MPI3_IOCSTATUS_CI_VALIDATION_FAILED (0x00b2) +#define MPI3_IOCSTATUS_CI_UPDATE_PENDING (0x00b3) +#define MPI3_IOCSTATUS_SECURITY_KEY_REQUIRED (0x00c0) +#define MPI3_IOCSTATUS_INVALID_QUEUE_ID (0x0f00) +#define MPI3_IOCSTATUS_INVALID_QUEUE_SIZE (0x0f01) +#define MPI3_IOCSTATUS_INVALID_MSIX_VECTOR (0x0f02) +#define MPI3_IOCSTATUS_INVALID_REPLY_QUEUE_ID (0x0f03) +#define MPI3_IOCSTATUS_INVALID_QUEUE_DELETION (0x0f04) +#define MPI3_IOCLOGINFO_TYPE_MASK (0xf0000000) +#define MPI3_IOCLOGINFO_TYPE_SHIFT (28) +#define MPI3_IOCLOGINFO_TYPE_NONE (0x0) +#define MPI3_IOCLOGINFO_TYPE_SAS (0x3) +#define MPI3_IOCLOGINFO_LOG_DATA_MASK (0x0fffffff) +#endif diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h new file mode 100644 index 000000000000..5d5529167350 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -0,0 +1,900 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Broadcom MPI3 Storage Controllers + * + * Copyright (C) 2017-2021 Broadcom Inc. + * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com) + * + */ + +#ifndef MPI3MR_H_INCLUDED +#define MPI3MR_H_INCLUDED + +#include <linux/blkdev.h> +#include <linux/blk-mq.h> +#include <linux/blk-mq-pci.h> +#include <linux/delay.h> +#include <linux/dmapool.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/utsname.h> +#include <linux/version.h> +#include <linux/workqueue.h> +#include <asm/unaligned.h> +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> + +#include "mpi/mpi30_transport.h" +#include "mpi/mpi30_cnfg.h" +#include "mpi/mpi30_image.h" +#include "mpi/mpi30_init.h" +#include "mpi/mpi30_ioc.h" +#include "mpi/mpi30_sas.h" +#include "mpi3mr_debug.h" + +/* Global list and lock for storing multiple adapters managed by the driver */ +extern spinlock_t mrioc_list_lock; +extern struct list_head mrioc_list; + +#define MPI3MR_DRIVER_VERSION "00.255.45.01" +#define MPI3MR_DRIVER_RELDATE "12-December-2020" + +#define MPI3MR_DRIVER_NAME "mpi3mr" +#define MPI3MR_DRIVER_LICENSE "GPL" +#define MPI3MR_DRIVER_AUTHOR "Broadcom Inc. <mpi3mr-linuxdrv.pdl@broadcom.com>" +#define MPI3MR_DRIVER_DESC "MPI3 Storage Controller Device Driver" + +#define MPI3MR_NAME_LENGTH 32 +#define IOCNAME "%s: " + +/* Definitions for internal SGL and Chain SGL buffers */ +#define MPI3MR_PAGE_SIZE_4K 4096 +#define MPI3MR_SG_DEPTH (MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common)) + +/* Definitions for MAX values for shost */ +#define MPI3MR_MAX_CMDS_LUN 7 +#define MPI3MR_MAX_CDB_LENGTH 32 + +/* Admin queue management definitions */ +#define MPI3MR_ADMIN_REQ_Q_SIZE (2 * MPI3MR_PAGE_SIZE_4K) +#define MPI3MR_ADMIN_REPLY_Q_SIZE (4 * MPI3MR_PAGE_SIZE_4K) +#define MPI3MR_ADMIN_REQ_FRAME_SZ 128 +#define MPI3MR_ADMIN_REPLY_FRAME_SZ 16 + +/* Operational queue management definitions */ +#define MPI3MR_OP_REQ_Q_QD 512 +#define MPI3MR_OP_REP_Q_QD 4096 +#define MPI3MR_OP_REQ_Q_SEG_SIZE 4096 +#define MPI3MR_OP_REP_Q_SEG_SIZE 4096 +#define MPI3MR_MAX_SEG_LIST_SIZE 4096 + +/* Reserved Host Tag definitions */ +#define MPI3MR_HOSTTAG_INVALID 0xFFFF +#define MPI3MR_HOSTTAG_INITCMDS 1 +#define MPI3MR_HOSTTAG_IOCTLCMDS 2 +#define MPI3MR_HOSTTAG_BLK_TMS 5 + +#define MPI3MR_NUM_DEVRMCMD 1 +#define MPI3MR_HOSTTAG_DEVRMCMD_MIN (MPI3MR_HOSTTAG_BLK_TMS + 1) +#define MPI3MR_HOSTTAG_DEVRMCMD_MAX (MPI3MR_HOSTTAG_DEVRMCMD_MIN + \ + MPI3MR_NUM_DEVRMCMD - 1) + +#define MPI3MR_INTERNAL_CMDS_RESVD MPI3MR_HOSTTAG_DEVRMCMD_MAX + +/* Reduced resource count definition for crash kernel */ +#define MPI3MR_HOST_IOS_KDUMP 128 + +/* command/controller interaction timeout definitions in seconds */ +#define MPI3MR_INTADMCMD_TIMEOUT 10 +#define MPI3MR_PORTENABLE_TIMEOUT 300 +#define MPI3MR_ABORTTM_TIMEOUT 30 +#define MPI3MR_RESETTM_TIMEOUT 30 +#define MPI3MR_RESET_HOST_IOWAIT_TIMEOUT 5 +#define MPI3MR_TSUPDATE_INTERVAL 900 +#define MPI3MR_DEFAULT_SHUTDOWN_TIME 120 +#define MPI3MR_RAID_ERRREC_RESET_TIMEOUT 180 + +#define MPI3MR_WATCHDOG_INTERVAL 1000 /* in milli seconds */ + +/* Internal admin command state definitions*/ +#define MPI3MR_CMD_NOTUSED 0x8000 +#define MPI3MR_CMD_COMPLETE 0x0001 +#define MPI3MR_CMD_PENDING 0x0002 +#define MPI3MR_CMD_REPLY_VALID 0x0004 +#define MPI3MR_CMD_RESET 0x0008 + +/* Definitions for Event replies and sense buffer allocated per controller */ +#define MPI3MR_NUM_EVT_REPLIES 64 +#define MPI3MR_SENSEBUF_SZ 256 +#define MPI3MR_SENSEBUF_FACTOR 3 +#define MPI3MR_CHAINBUF_FACTOR 3 +#define MPI3MR_CHAINBUFDIX_FACTOR 2 + +/* Invalid target device handle */ +#define MPI3MR_INVALID_DEV_HANDLE 0xFFFF + +/* Controller Reset related definitions */ +#define MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT 5 +#define MPI3MR_MAX_RESET_RETRY_COUNT 3 + +/* ResponseCode definitions */ +#define MPI3MR_RI_MASK_RESPCODE (0x000000FF) +#define MPI3MR_RSP_TM_COMPLETE 0x00 +#define MPI3MR_RSP_INVALID_FRAME 0x02 +#define MPI3MR_RSP_TM_NOT_SUPPORTED 0x04 +#define MPI3MR_RSP_TM_FAILED 0x05 +#define MPI3MR_RSP_TM_SUCCEEDED 0x08 +#define MPI3MR_RSP_TM_INVALID_LUN 0x09 +#define MPI3MR_RSP_TM_OVERLAPPED_TAG 0x0A +#define MPI3MR_RSP_IO_QUEUED_ON_IOC \ + MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC + +#define MPI3MR_DEFAULT_MDTS (128 * 1024) +/* Command retry count definitions */ +#define MPI3MR_DEV_RMHS_RETRY_COUNT 3 + +/* Default target device queue depth */ +#define MPI3MR_DEFAULT_SDEV_QD 32 + +/* Definitions for Threaded IRQ poll*/ +#define MPI3MR_IRQ_POLL_SLEEP 2 +#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8 + +/* Definitions for the controller security status*/ +#define MPI3MR_CTLR_SECURITY_STATUS_MASK 0x0C +#define MPI3MR_CTLR_SECURE_DBG_STATUS_MASK 0x02 + +#define MPI3MR_INVALID_DEVICE 0x00 +#define MPI3MR_CONFIG_SECURE_DEVICE 0x04 +#define MPI3MR_HARD_SECURE_DEVICE 0x08 +#define MPI3MR_TAMPERED_DEVICE 0x0C + +/* SGE Flag definition */ +#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \ + (MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \ + MPI3_SGE_FLAGS_END_OF_LIST) + +/* MSI Index from Reply Queue Index */ +#define REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, offset) (qidx + offset) + +/* IOC State definitions */ +enum mpi3mr_iocstate { + MRIOC_STATE_READY = 1, + MRIOC_STATE_RESET, + MRIOC_STATE_FAULT, + MRIOC_STATE_BECOMING_READY, + MRIOC_STATE_RESET_REQUESTED, + MRIOC_STATE_UNRECOVERABLE, +}; + +/* Reset reason code definitions*/ +enum mpi3mr_reset_reason { + MPI3MR_RESET_FROM_BRINGUP = 1, + MPI3MR_RESET_FROM_FAULT_WATCH = 2, + MPI3MR_RESET_FROM_IOCTL = 3, + MPI3MR_RESET_FROM_EH_HOS = 4, + MPI3MR_RESET_FROM_TM_TIMEOUT = 5, + MPI3MR_RESET_FROM_IOCTL_TIMEOUT = 6, + MPI3MR_RESET_FROM_MUR_FAILURE = 7, + MPI3MR_RESET_FROM_CTLR_CLEANUP = 8, + MPI3MR_RESET_FROM_CIACTIV_FAULT = 9, + MPI3MR_RESET_FROM_PE_TIMEOUT = 10, + MPI3MR_RESET_FROM_TSU_TIMEOUT = 11, + MPI3MR_RESET_FROM_DELREQQ_TIMEOUT = 12, + MPI3MR_RESET_FROM_DELREPQ_TIMEOUT = 13, + MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT = 14, + MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT = 15, + MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT = 16, + MPI3MR_RESET_FROM_IOCINIT_TIMEOUT = 17, + MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT = 18, + MPI3MR_RESET_FROM_EVTACK_TIMEOUT = 19, + MPI3MR_RESET_FROM_CIACTVRST_TIMER = 20, + MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT = 21, + MPI3MR_RESET_FROM_PELABORT_TIMEOUT = 22, + MPI3MR_RESET_FROM_SYSFS = 23, + MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24 +}; + +/** + * struct mpi3mr_compimg_ver - replica of component image + * version defined in mpi30_image.h in host endianness + * + */ +struct mpi3mr_compimg_ver { + u16 build_num; + u16 cust_id; + u8 ph_minor; + u8 ph_major; + u8 gen_minor; + u8 gen_major; +}; + +/** + * struct mpi3mr_ioc_facs - replica of component image version + * defined in mpi30_ioc.h in host endianness + * + */ +struct mpi3mr_ioc_facts { + u32 ioc_capabilities; + struct mpi3mr_compimg_ver fw_ver; + u32 mpi_version; + u16 max_reqs; + u16 product_id; + u16 op_req_sz; + u16 reply_sz; + u16 exceptions; + u16 max_perids; + u16 max_pds; + u16 max_sasexpanders; + u16 max_sasinitiators; + u16 max_enclosures; + u16 max_pcie_switches; + u16 max_nvme; + u16 max_vds; + u16 max_hpds; + u16 max_advhpds; + u16 max_raidpds; + u16 min_devhandle; + u16 max_devhandle; + u16 max_op_req_q; + u16 max_op_reply_q; + u16 shutdown_timeout; + u8 ioc_num; + u8 who_init; + u16 max_msix_vectors; + u8 personality; + u8 dma_mask; + u8 protocol_flags; + u8 sge_mod_mask; + u8 sge_mod_value; + u8 sge_mod_shift; +}; + +/** + * struct segments - memory descriptor structure to store + * virtual and dma addresses for operational queue segments. + * + * @segment: virtual address + * @segment_dma: dma address + */ +struct segments { + void *segment; + dma_addr_t segment_dma; +}; + +/** + * struct op_req_qinfo - Operational Request Queue Information + * + * @ci: consumer index + * @pi: producer index + * @num_request: Maximum number of entries in the queue + * @qid: Queue Id starting from 1 + * @reply_qid: Associated reply queue Id + * @num_segments: Number of discontiguous memory segments + * @segment_qd: Depth of each segments + * @q_lock: Concurrent queue access lock + * @q_segments: Segment descriptor pointer + * @q_segment_list: Segment list base virtual address + * @q_segment_list_dma: Segment list base DMA address + */ +struct op_req_qinfo { + u16 ci; + u16 pi; + u16 num_requests; + u16 qid; + u16 reply_qid; + u16 num_segments; + u16 segment_qd; + spinlock_t q_lock; + struct segments *q_segments; + void *q_segment_list; + dma_addr_t q_segment_list_dma; +}; + +/** + * struct op_reply_qinfo - Operational Reply Queue Information + * + * @ci: consumer index + * @qid: Queue Id starting from 1 + * @num_replies: Maximum number of entries in the queue + * @num_segments: Number of discontiguous memory segments + * @segment_qd: Depth of each segments + * @q_segments: Segment descriptor pointer + * @q_segment_list: Segment list base virtual address + * @q_segment_list_dma: Segment list base DMA address + * @ephase: Expected phased identifier for the reply queue + * @pend_ios: Number of IOs pending in HW for this queue + * @enable_irq_poll: Flag to indicate polling is enabled + * @in_use: Queue is handled by poll/ISR + */ +struct op_reply_qinfo { + u16 ci; + u16 qid; + u16 num_replies; + u16 num_segments; + u16 segment_qd; + struct segments *q_segments; + void *q_segment_list; + dma_addr_t q_segment_list_dma; + u8 ephase; + atomic_t pend_ios; + bool enable_irq_poll; + atomic_t in_use; +}; + +/** + * struct mpi3mr_intr_info - Interrupt cookie information + * + * @mrioc: Adapter instance reference + * @msix_index: MSIx index + * @op_reply_q: Associated operational reply queue + * @name: Dev name for the irq claiming device + */ +struct mpi3mr_intr_info { + struct mpi3mr_ioc *mrioc; + u16 msix_index; + struct op_reply_qinfo *op_reply_q; + char name[MPI3MR_NAME_LENGTH]; +}; + +/** + * struct tgt_dev_sas_sata - SAS/SATA device specific + * information cached from firmware given data + * + * @sas_address: World wide unique SAS address + * @dev_info: Device information bits + */ +struct tgt_dev_sas_sata { + u64 sas_address; + u16 dev_info; +}; + +/** + * struct tgt_dev_pcie - PCIe device specific information cached + * from firmware given data + * + * @mdts: Maximum data transfer size + * @capb: Device capabilities + * @pgsz: Device page size + * @abort_to: Timeout for abort TM + * @reset_to: Timeout for Target/LUN reset TM + */ +struct tgt_dev_pcie { + u32 mdts; + u16 capb; + u8 pgsz; + u8 abort_to; + u8 reset_to; +}; + +/** + * struct tgt_dev_volume - virtual device specific information + * cached from firmware given data + * + * @state: State of the VD + */ +struct tgt_dev_volume { + u8 state; +}; + +/** + * union _form_spec_inf - union of device specific information + */ +union _form_spec_inf { + struct tgt_dev_sas_sata sas_sata_inf; + struct tgt_dev_pcie pcie_inf; + struct tgt_dev_volume vol_inf; +}; + + + +/** + * struct mpi3mr_tgt_dev - target device data structure + * + * @list: List pointer + * @starget: Scsi_target pointer + * @dev_handle: FW device handle + * @parent_handle: FW parent device handle + * @slot: Slot number + * @encl_handle: FW enclosure handle + * @perst_id: FW assigned Persistent ID + * @dev_type: SAS/SATA/PCIE device type + * @is_hidden: Should be exposed to upper layers or not + * @host_exposed: Already exposed to host or not + * @q_depth: Device specific Queue Depth + * @wwid: World wide ID + * @dev_spec: Device type specific information + * @ref_count: Reference count + */ +struct mpi3mr_tgt_dev { + struct list_head list; + struct scsi_target *starget; + u16 dev_handle; + u16 parent_handle; + u16 slot; + u16 encl_handle; + u16 perst_id; + u8 dev_type; + u8 is_hidden; + u8 host_exposed; + u16 q_depth; + u64 wwid; + union _form_spec_inf dev_spec; + struct kref ref_count; +}; + +/** + * mpi3mr_tgtdev_get - k reference incrementor + * @s: Target device reference + * + * Increment target device reference count. + */ +static inline void mpi3mr_tgtdev_get(struct mpi3mr_tgt_dev *s) +{ + kref_get(&s->ref_count); +} + +/** + * mpi3mr_free_tgtdev - target device memory dealloctor + * @r: k reference pointer of the target device + * + * Free target device memory when no reference. + */ +static inline void mpi3mr_free_tgtdev(struct kref *r) +{ + kfree(container_of(r, struct mpi3mr_tgt_dev, ref_count)); +} + +/** + * mpi3mr_tgtdev_put - k reference decrementor + * @s: Target device reference + * + * Decrement target device reference count. + */ +static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s) +{ + kref_put(&s->ref_count, mpi3mr_free_tgtdev); +} + + +/** + * struct mpi3mr_stgt_priv_data - SCSI target private structure + * + * @starget: Scsi_target pointer + * @dev_handle: FW device handle + * @perst_id: FW assigned Persistent ID + * @num_luns: Number of Logical Units + * @block_io: I/O blocked to the device or not + * @dev_removed: Device removed in the Firmware + * @dev_removedelay: Device is waiting to be removed in FW + * @dev_type: Device type + * @tgt_dev: Internal target device pointer + */ +struct mpi3mr_stgt_priv_data { + struct scsi_target *starget; + u16 dev_handle; + u16 perst_id; + u32 num_luns; + atomic_t block_io; + u8 dev_removed; + u8 dev_removedelay; + u8 dev_type; + struct mpi3mr_tgt_dev *tgt_dev; +}; + +/** + * struct mpi3mr_stgt_priv_data - SCSI device private structure + * + * @tgt_priv_data: Scsi_target private data pointer + * @lun_id: LUN ID of the device + * @ncq_prio_enable: NCQ priority enable for SATA device + */ +struct mpi3mr_sdev_priv_data { + struct mpi3mr_stgt_priv_data *tgt_priv_data; + u32 lun_id; + u8 ncq_prio_enable; +}; + +/** + * struct mpi3mr_drv_cmd - Internal command tracker + * + * @mutex: Command mutex + * @done: Completeor for wakeup + * @reply: Firmware reply for internal commands + * @sensebuf: Sensebuf for SCSI IO commands + * @iou_rc: IO Unit control reason code + * @state: Command State + * @dev_handle: Firmware handle for device specific commands + * @ioc_status: IOC status from the firmware + * @ioc_loginfo:IOC log info from the firmware + * @is_waiting: Is the command issued in block mode + * @retry_count: Retry count for retriable commands + * @host_tag: Host tag used by the command + * @callback: Callback for non blocking commands + */ +struct mpi3mr_drv_cmd { + struct mutex mutex; + struct completion done; + void *reply; + u8 *sensebuf; + u8 iou_rc; + u16 state; + u16 dev_handle; + u16 ioc_status; + u32 ioc_loginfo; + u8 is_waiting; + u8 retry_count; + u16 host_tag; + + void (*callback)(struct mpi3mr_ioc *mrioc, + struct mpi3mr_drv_cmd *drv_cmd); +}; + + +/** + * struct chain_element - memory descriptor structure to store + * virtual and dma addresses for chain elements. + * + * @addr: virtual address + * @dma_addr: dma address + */ +struct chain_element { + void *addr; + dma_addr_t dma_addr; +}; + +/** + * struct scmd_priv - SCSI command private data + * + * @host_tag: Host tag specific to operational queue + * @in_lld_scope: Command in LLD scope or not + * @meta_sg_valid: DIX command with meta data SGL or not + * @scmd: SCSI Command pointer + * @req_q_idx: Operational request queue index + * @chain_idx: Chain frame index + * @meta_chain_idx: Chain frame index of meta data SGL + * @mpi3mr_scsiio_req: MPI SCSI IO request + */ +struct scmd_priv { + u16 host_tag; + u8 in_lld_scope; + u8 meta_sg_valid; + struct scsi_cmnd *scmd; + u16 req_q_idx; + int chain_idx; + int meta_chain_idx; + u8 mpi3mr_scsiio_req[MPI3MR_ADMIN_REQ_FRAME_SZ]; +}; + +/** + * struct mpi3mr_ioc - Adapter anchor structure stored in shost + * private data + * + * @list: List pointer + * @pdev: PCI device pointer + * @shost: Scsi_Host pointer + * @id: Controller ID + * @cpu_count: Number of online CPUs + * @irqpoll_sleep: usleep unit used in threaded isr irqpoll + * @name: Controller ASCII name + * @driver_name: Driver ASCII name + * @sysif_regs: System interface registers virtual address + * @sysif_regs_phys: System interface registers physical address + * @bars: PCI BARS + * @dma_mask: DMA mask + * @msix_count: Number of MSIX vectors used + * @intr_enabled: Is interrupts enabled + * @num_admin_req: Number of admin requests + * @admin_req_q_sz: Admin request queue size + * @admin_req_pi: Admin request queue producer index + * @admin_req_ci: Admin request queue consumer index + * @admin_req_base: Admin request queue base virtual address + * @admin_req_dma: Admin request queue base dma address + * @admin_req_lock: Admin queue access lock + * @num_admin_replies: Number of admin replies + * @admin_reply_q_sz: Admin reply queue size + * @admin_reply_ci: Admin reply queue consumer index + * @admin_reply_ephase:Admin reply queue expected phase + * @admin_reply_base: Admin reply queue base virtual address + * @admin_reply_dma: Admin reply queue base dma address + * @ready_timeout: Controller ready timeout + * @intr_info: Interrupt cookie pointer + * @intr_info_count: Number of interrupt cookies + * @num_queues: Number of operational queues + * @num_op_req_q: Number of operational request queues + * @req_qinfo: Operational request queue info pointer + * @num_op_reply_q: Number of operational reply queues + * @op_reply_qinfo: Operational reply queue info pointer + * @init_cmds: Command tracker for initialization commands + * @facts: Cached IOC facts data + * @op_reply_desc_sz: Operational reply descriptor size + * @num_reply_bufs: Number of reply buffers allocated + * @reply_buf_pool: Reply buffer pool + * @reply_buf: Reply buffer base virtual address + * @reply_buf_dma: Reply buffer DMA address + * @reply_buf_dma_max_address: Reply DMA address max limit + * @reply_free_qsz: Reply free queue size + * @reply_free_q_pool: Reply free queue pool + * @reply_free_q: Reply free queue base virtual address + * @reply_free_q_dma: Reply free queue base DMA address + * @reply_free_queue_lock: Reply free queue lock + * @reply_free_queue_host_index: Reply free queue host index + * @num_sense_bufs: Number of sense buffers + * @sense_buf_pool: Sense buffer pool + * @sense_buf: Sense buffer base virtual address + * @sense_buf_dma: Sense buffer base DMA address + * @sense_buf_q_sz: Sense buffer queue size + * @sense_buf_q_pool: Sense buffer queue pool + * @sense_buf_q: Sense buffer queue virtual address + * @sense_buf_q_dma: Sense buffer queue DMA address + * @sbq_lock: Sense buffer queue lock + * @sbq_host_index: Sense buffer queuehost index + * @event_masks: Event mask bitmap + * @fwevt_worker_name: Firmware event worker thread name + * @fwevt_worker_thread: Firmware event worker thread + * @fwevt_lock: Firmware event lock + * @fwevt_list: Firmware event list + * @watchdog_work_q_name: Fault watchdog worker thread name + * @watchdog_work_q: Fault watchdog worker thread + * @watchdog_work: Fault watchdog work + * @watchdog_lock: Fault watchdog lock + * @is_driver_loading: Is driver still loading + * @scan_started: Async scan started + * @scan_failed: Asycn scan failed + * @stop_drv_processing: Stop all command processing + * @max_host_ios: Maximum host I/O count + * @chain_buf_count: Chain buffer count + * @chain_buf_pool: Chain buffer pool + * @chain_sgl_list: Chain SGL list + * @chain_bitmap_sz: Chain buffer allocator bitmap size + * @chain_bitmap: Chain buffer allocator bitmap + * @chain_buf_lock: Chain buffer list lock + * @host_tm_cmds: Command tracker for task management commands + * @dev_rmhs_cmds: Command tracker for device removal commands + * @devrem_bitmap_sz: Device removal bitmap size + * @devrem_bitmap: Device removal bitmap + * @dev_handle_bitmap_sz: Device handle bitmap size + * @removepend_bitmap: Remove pending bitmap + * @delayed_rmhs_list: Delayed device removal list + * @ts_update_counter: Timestamp update counter + * @fault_dbg: Fault debug flag + * @reset_in_progress: Reset in progress flag + * @unrecoverable: Controller unrecoverable flag + * @reset_mutex: Controller reset mutex + * @reset_waitq: Controller reset wait queue + * @diagsave_timeout: Diagnostic information save timeout + * @logging_level: Controller debug logging level + * @flush_io_count: I/O count to flush after reset + * @current_event: Firmware event currently in process + * @driver_info: Driver, Kernel, OS information to firmware + * @change_count: Topology change count + * @op_reply_q_offset: Operational reply queue offset with MSIx + */ +struct mpi3mr_ioc { + struct list_head list; + struct pci_dev *pdev; + struct Scsi_Host *shost; + u8 id; + int cpu_count; + bool enable_segqueue; + u32 irqpoll_sleep; + + char name[MPI3MR_NAME_LENGTH]; + char driver_name[MPI3MR_NAME_LENGTH]; + + volatile struct mpi3_sysif_registers __iomem *sysif_regs; + resource_size_t sysif_regs_phys; + int bars; + u64 dma_mask; + + u16 msix_count; + u8 intr_enabled; + + u16 num_admin_req; + u32 admin_req_q_sz; + u16 admin_req_pi; + u16 admin_req_ci; + void *admin_req_base; + dma_addr_t admin_req_dma; + spinlock_t admin_req_lock; + + u16 num_admin_replies; + u32 admin_reply_q_sz; + u16 admin_reply_ci; + u8 admin_reply_ephase; + void *admin_reply_base; + dma_addr_t admin_reply_dma; + + u32 ready_timeout; + + struct mpi3mr_intr_info *intr_info; + u16 intr_info_count; + + u16 num_queues; + u16 num_op_req_q; + struct op_req_qinfo *req_qinfo; + + u16 num_op_reply_q; + struct op_reply_qinfo *op_reply_qinfo; + + struct mpi3mr_drv_cmd init_cmds; + struct mpi3mr_ioc_facts facts; + u16 op_reply_desc_sz; + + u32 num_reply_bufs; + struct dma_pool *reply_buf_pool; + u8 *reply_buf; + dma_addr_t reply_buf_dma; + dma_addr_t reply_buf_dma_max_address; + + u16 reply_free_qsz; + struct dma_pool *reply_free_q_pool; + __le64 *reply_free_q; + dma_addr_t reply_free_q_dma; + spinlock_t reply_free_queue_lock; + u32 reply_free_queue_host_index; + + u32 num_sense_bufs; + struct dma_pool *sense_buf_pool; + u8 *sense_buf; + dma_addr_t sense_buf_dma; + + u16 sense_buf_q_sz; + struct dma_pool *sense_buf_q_pool; + __le64 *sense_buf_q; + dma_addr_t sense_buf_q_dma; + spinlock_t sbq_lock; + u32 sbq_host_index; + u32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS]; + + char fwevt_worker_name[MPI3MR_NAME_LENGTH]; + struct workqueue_struct *fwevt_worker_thread; + spinlock_t fwevt_lock; + struct list_head fwevt_list; + + char watchdog_work_q_name[20]; + struct workqueue_struct *watchdog_work_q; + struct delayed_work watchdog_work; + spinlock_t watchdog_lock; + + u8 is_driver_loading; + u8 scan_started; + u16 scan_failed; + u8 stop_drv_processing; + + u16 max_host_ios; + spinlock_t tgtdev_lock; + struct list_head tgtdev_list; + + u32 chain_buf_count; + struct dma_pool *chain_buf_pool; + struct chain_element *chain_sgl_list; + u16 chain_bitmap_sz; + void *chain_bitmap; + spinlock_t chain_buf_lock; + + struct mpi3mr_drv_cmd host_tm_cmds; + struct mpi3mr_drv_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD]; + u16 devrem_bitmap_sz; + void *devrem_bitmap; + u16 dev_handle_bitmap_sz; + void *removepend_bitmap; + struct list_head delayed_rmhs_list; + + u32 ts_update_counter; + u8 fault_dbg; + u8 reset_in_progress; + u8 unrecoverable; + struct mutex reset_mutex; + wait_queue_head_t reset_waitq; + + u16 diagsave_timeout; + int logging_level; + u16 flush_io_count; + + struct mpi3mr_fwevt *current_event; + struct mpi3_driver_info_layout driver_info; + u16 change_count; + u16 op_reply_q_offset; +}; + +/** + * struct mpi3mr_fwevt - Firmware event structure. + * + * @list: list head + * @work: Work structure + * @mrioc: Adapter instance reference + * @event_id: MPI3 firmware event ID + * @send_ack: Event acknowledgment required or not + * @process_evt: Bottomhalf processing required or not + * @evt_ctx: Event context to send in Ack + * @ref_count: kref count + * @event_data: Actual MPI3 event data + */ +struct mpi3mr_fwevt { + struct list_head list; + struct work_struct work; + struct mpi3mr_ioc *mrioc; + u16 event_id; + bool send_ack; + bool process_evt; + u32 evt_ctx; + struct kref ref_count; + char event_data[0] __aligned(4); +}; + + +/** + * struct delayed_dev_rmhs_node - Delayed device removal node + * + * @list: list head + * @handle: Device handle + * @iou_rc: IO Unit Control Reason Code + */ +struct delayed_dev_rmhs_node { + struct list_head list; + u16 handle; + u8 iou_rc; +}; + +int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc); +void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc); +int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc, u8 re_init); +void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc, u8 re_init); +int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async); +int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req, +u16 admin_req_sz, u8 ignore_reset); +int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, + struct op_req_qinfo *opreqq, u8 *req); +void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length, + dma_addr_t dma_addr); +void mpi3mr_build_zero_len_sge(void *paddr); +void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc, + dma_addr_t phys_addr); +void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc, + dma_addr_t phys_addr); +void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc, + u64 sense_buf_dma); + +void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply); +void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, + struct mpi3_default_reply_descriptor *reply_desc, + u64 *reply_dma, u16 qidx); +void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc); +void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc); + +int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc, + u32 reset_reason, u8 snapdump); +int mpi3mr_diagfault_reset_handler(struct mpi3mr_ioc *mrioc, + u32 reset_reason); +void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc); +void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc); + +enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc); +int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, + u32 event_ctx); + +void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout); +void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc); +void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc); +void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc); +void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc); +void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc); + +#endif /*MPI3MR_H_INCLUDED*/ diff --git a/drivers/scsi/mpi3mr/mpi3mr_debug.h b/drivers/scsi/mpi3mr/mpi3mr_debug.h new file mode 100644 index 000000000000..c085bb048d41 --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi3mr_debug.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Driver for Broadcom MPI3 Storage Controllers + * + * Copyright (C) 2017-2021 Broadcom Inc. + * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com) + * + */ + +#ifndef MPI3SAS_DEBUG_H_INCLUDED + +#define MPI3SAS_DEBUG_H_INCLUDED + +/* + * debug levels + */ +#define MPI3_DEBUG 0x00000001 +#define MPI3_DEBUG_MSG_FRAME 0x00000002 +#define MPI3_DEBUG_SG 0x00000004 +#define MPI3_DEBUG_EVENTS 0x00000008 +#define MPI3_DEBUG_EVENT_WORK_TASK 0x00000010 +#define MPI3_DEBUG_INIT 0x00000020 +#define MPI3_DEBUG_EXIT 0x00000040 +#define MPI3_DEBUG_FAIL 0x00000080 +#define MPI3_DEBUG_TM 0x00000100 +#define MPI3_DEBUG_REPLY 0x00000200 +#define MPI3_DEBUG_HANDSHAKE 0x00000400 +#define MPI3_DEBUG_CONFIG 0x00000800 +#define MPI3_DEBUG_DL 0x00001000 +#define MPI3_DEBUG_RESET 0x00002000 +#define MPI3_DEBUG_SCSI 0x00004000 +#define MPI3_DEBUG_IOCTL 0x00008000 +#define MPI3_DEBUG_CSMISAS 0x00010000 +#define MPI3_DEBUG_SAS 0x00020000 +#define MPI3_DEBUG_TRANSPORT 0x00040000 +#define MPI3_DEBUG_TASK_SET_FULL 0x00080000 +#define MPI3_DEBUG_TRIGGER_DIAG 0x00200000 + + +/* + * debug macros + */ + +#define ioc_err(ioc, fmt, ...) \ + pr_err("%s: " fmt, (ioc)->name, ##__VA_ARGS__) +#define ioc_notice(ioc, fmt, ...) \ + pr_notice("%s: " fmt, (ioc)->name, ##__VA_ARGS__) +#define ioc_warn(ioc, fmt, ...) \ + pr_warn("%s: " fmt, (ioc)->name, ##__VA_ARGS__) +#define ioc_info(ioc, fmt, ...) \ + pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__) + + +#define dbgprint(IOC, FMT, ...) \ + do { \ + if (IOC->logging_level & MPI3_DEBUG) \ + pr_info("%s: " FMT, (IOC)->name, ##__VA_ARGS__); \ + } while (0) + +#endif /* MPT3SAS_DEBUG_H_INCLUDED */ diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c new file mode 100644 index 000000000000..acb2be62080a --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -0,0 +1,3956 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Broadcom MPI3 Storage Controllers + * + * Copyright (C) 2017-2021 Broadcom Inc. + * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com) + * + */ + +#include "mpi3mr.h" +#include <linux/io-64-nonatomic-lo-hi.h> +extern int prot_mask; + +#if defined(writeq) && defined(CONFIG_64BIT) +static inline void mpi3mr_writeq(__u64 b, volatile void __iomem *addr) +{ + writeq(b, addr); +} +#else +static inline void mpi3mr_writeq(__u64 b, volatile void __iomem *addr) +{ + __u64 data_out = b; + + writel((u32)(data_out), addr); + writel((u32)(data_out >> 32), (addr + 4)); +} +#endif + +static inline bool +mpi3mr_check_req_qfull(struct op_req_qinfo *op_req_q) +{ + u16 pi, ci, max_entries; + bool is_qfull = false; + + pi = op_req_q->pi; + ci = READ_ONCE(op_req_q->ci); + max_entries = op_req_q->num_requests; + + if ((ci == (pi + 1)) || ((!ci) && (pi == (max_entries - 1)))) + is_qfull = true; + + return is_qfull; +} + +static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc) +{ + u16 i, max_vectors; + + max_vectors = mrioc->intr_info_count; + + for (i = 0; i < max_vectors; i++) + synchronize_irq(pci_irq_vector(mrioc->pdev, i)); +} + +void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc) +{ + mrioc->intr_enabled = 0; + mpi3mr_sync_irqs(mrioc); +} + +void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc) +{ + mrioc->intr_enabled = 1; +} + +static void mpi3mr_cleanup_isr(struct mpi3mr_ioc *mrioc) +{ + u16 i; + + mpi3mr_ioc_disable_intr(mrioc); + + if (!mrioc->intr_info) + return; + + for (i = 0; i < mrioc->intr_info_count; i++) + free_irq(pci_irq_vector(mrioc->pdev, i), + (mrioc->intr_info + i)); + + kfree(mrioc->intr_info); + mrioc->intr_info = NULL; + mrioc->intr_info_count = 0; + pci_free_irq_vectors(mrioc->pdev); +} + +void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length, + dma_addr_t dma_addr) +{ + struct mpi3_sge_common *sgel = paddr; + + sgel->flags = flags; + sgel->length = cpu_to_le32(length); + sgel->address = cpu_to_le64(dma_addr); +} + +void mpi3mr_build_zero_len_sge(void *paddr) +{ + u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST; + + mpi3mr_add_sg_single(paddr, sgl_flags, 0, -1); +} + +void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc, + dma_addr_t phys_addr) +{ + if (!phys_addr) + return NULL; + + if ((phys_addr < mrioc->reply_buf_dma) || + (phys_addr > mrioc->reply_buf_dma_max_address)) + return NULL; + + return mrioc->reply_buf + (phys_addr - mrioc->reply_buf_dma); +} + +void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc, + dma_addr_t phys_addr) +{ + if (!phys_addr) + return NULL; + + return mrioc->sense_buf + (phys_addr - mrioc->sense_buf_dma); +} + +static void mpi3mr_repost_reply_buf(struct mpi3mr_ioc *mrioc, + u64 reply_dma) +{ + u32 old_idx = 0; + + spin_lock(&mrioc->reply_free_queue_lock); + old_idx = mrioc->reply_free_queue_host_index; + mrioc->reply_free_queue_host_index = ( + (mrioc->reply_free_queue_host_index == + (mrioc->reply_free_qsz - 1)) ? 0 : + (mrioc->reply_free_queue_host_index + 1)); + mrioc->reply_free_q[old_idx] = cpu_to_le64(reply_dma); + writel(mrioc->reply_free_queue_host_index, + &mrioc->sysif_regs->reply_free_host_index); + spin_unlock(&mrioc->reply_free_queue_lock); +} + +void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc, + u64 sense_buf_dma) +{ + u32 old_idx = 0; + + spin_lock(&mrioc->sbq_lock); + old_idx = mrioc->sbq_host_index; + mrioc->sbq_host_index = ((mrioc->sbq_host_index == + (mrioc->sense_buf_q_sz - 1)) ? 0 : + (mrioc->sbq_host_index + 1)); + mrioc->sense_buf_q[old_idx] = cpu_to_le64(sense_buf_dma); + writel(mrioc->sbq_host_index, + &mrioc->sysif_regs->sense_buffer_free_host_index); + spin_unlock(&mrioc->sbq_lock); +} + +static void mpi3mr_print_event_data(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + char *desc = NULL; + u16 event; + + event = event_reply->event; + + switch (event) { + case MPI3_EVENT_LOG_DATA: + desc = "Log Data"; + break; + case MPI3_EVENT_CHANGE: + desc = "Event Change"; + break; + case MPI3_EVENT_GPIO_INTERRUPT: + desc = "GPIO Interrupt"; + break; + case MPI3_EVENT_TEMP_THRESHOLD: + desc = "Temperature Threshold"; + break; + case MPI3_EVENT_CABLE_MGMT: + desc = "Cable Management"; + break; + case MPI3_EVENT_ENERGY_PACK_CHANGE: + desc = "Energy Pack Change"; + break; + case MPI3_EVENT_DEVICE_ADDED: + { + struct mpi3_device_page0 *event_data = + (struct mpi3_device_page0 *)event_reply->event_data; + ioc_info(mrioc, "Device Added: dev=0x%04x Form=0x%x\n", + event_data->dev_handle, event_data->device_form); + return; + } + case MPI3_EVENT_DEVICE_INFO_CHANGED: + { + struct mpi3_device_page0 *event_data = + (struct mpi3_device_page0 *)event_reply->event_data; + ioc_info(mrioc, "Device Info Changed: dev=0x%04x Form=0x%x\n", + event_data->dev_handle, event_data->device_form); + return; + } + case MPI3_EVENT_DEVICE_STATUS_CHANGE: + { + struct mpi3_event_data_device_status_change *event_data = + (struct mpi3_event_data_device_status_change *)event_reply->event_data; + ioc_info(mrioc, "Device status Change: dev=0x%04x RC=0x%x\n", + event_data->dev_handle, event_data->reason_code); + return; + } + case MPI3_EVENT_SAS_DISCOVERY: + { + struct mpi3_event_data_sas_discovery *event_data = + (struct mpi3_event_data_sas_discovery *)event_reply->event_data; + ioc_info(mrioc, "SAS Discovery: (%s) status (0x%08x)\n", + (event_data->reason_code == MPI3_EVENT_SAS_DISC_RC_STARTED) ? + "start" : "stop", + le32_to_cpu(event_data->discovery_status)); + return; + } + case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE: + desc = "SAS Broadcast Primitive"; + break; + case MPI3_EVENT_SAS_NOTIFY_PRIMITIVE: + desc = "SAS Notify Primitive"; + break; + case MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: + desc = "SAS Init Device Status Change"; + break; + case MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW: + desc = "SAS Init Table Overflow"; + break; + case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + desc = "SAS Topology Change List"; + break; + case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE: + desc = "Enclosure Device Status Change"; + break; + case MPI3_EVENT_HARD_RESET_RECEIVED: + desc = "Hard Reset Received"; + break; + case MPI3_EVENT_SAS_PHY_COUNTER: + desc = "SAS PHY Counter"; + break; + case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR: + desc = "SAS Device Discovery Error"; + break; + case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + desc = "PCIE Topology Change List"; + break; + case MPI3_EVENT_PCIE_ENUMERATION: + { + struct mpi3_event_data_pcie_enumeration *event_data = + (struct mpi3_event_data_pcie_enumeration *)event_reply->event_data; + ioc_info(mrioc, "PCIE Enumeration: (%s)", + (event_data->reason_code == + MPI3_EVENT_PCIE_ENUM_RC_STARTED) ? "start" : "stop"); + if (event_data->enumeration_status) + ioc_info(mrioc, "enumeration_status(0x%08x)\n", + le32_to_cpu(event_data->enumeration_status)); + return; + } + case MPI3_EVENT_PREPARE_FOR_RESET: + desc = "Prepare For Reset"; + break; + } + + if (!desc) + return; + + ioc_info(mrioc, "%s\n", desc); +} + +static void mpi3mr_handle_events(struct mpi3mr_ioc *mrioc, + struct mpi3_default_reply *def_reply) +{ + struct mpi3_event_notification_reply *event_reply = + (struct mpi3_event_notification_reply *)def_reply; + + mrioc->change_count = le16_to_cpu(event_reply->ioc_change_count); + mpi3mr_print_event_data(mrioc, event_reply); + mpi3mr_os_handle_events(mrioc, event_reply); +} + +static struct mpi3mr_drv_cmd * +mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag, + struct mpi3_default_reply *def_reply) +{ + u16 idx; + + switch (host_tag) { + case MPI3MR_HOSTTAG_INITCMDS: + return &mrioc->init_cmds; + case MPI3MR_HOSTTAG_BLK_TMS: + return &mrioc->host_tm_cmds; + case MPI3MR_HOSTTAG_INVALID: + if (def_reply && def_reply->function == + MPI3_FUNCTION_EVENT_NOTIFICATION) + mpi3mr_handle_events(mrioc, def_reply); + return NULL; + default: + break; + } + if (host_tag >= MPI3MR_HOSTTAG_DEVRMCMD_MIN && + host_tag <= MPI3MR_HOSTTAG_DEVRMCMD_MAX) { + idx = host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN; + return &mrioc->dev_rmhs_cmds[idx]; + } + + return NULL; +} + +static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc, + struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma) +{ + u16 reply_desc_type, host_tag = 0; + u16 ioc_status = MPI3_IOCSTATUS_SUCCESS; + u32 ioc_loginfo = 0; + struct mpi3_status_reply_descriptor *status_desc; + struct mpi3_address_reply_descriptor *addr_desc; + struct mpi3_success_reply_descriptor *success_desc; + struct mpi3_default_reply *def_reply = NULL; + struct mpi3mr_drv_cmd *cmdptr = NULL; + struct mpi3_scsi_io_reply *scsi_reply; + u8 *sense_buf = NULL; + + *reply_dma = 0; + reply_desc_type = le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK; + switch (reply_desc_type) { + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS: + status_desc = (struct mpi3_status_reply_descriptor *)reply_desc; + host_tag = le16_to_cpu(status_desc->host_tag); + ioc_status = le16_to_cpu(status_desc->ioc_status); + if (ioc_status & + MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) + ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info); + ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; + break; + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY: + addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc; + *reply_dma = le64_to_cpu(addr_desc->reply_frame_address); + def_reply = mpi3mr_get_reply_virt_addr(mrioc, *reply_dma); + if (!def_reply) + goto out; + host_tag = le16_to_cpu(def_reply->host_tag); + ioc_status = le16_to_cpu(def_reply->ioc_status); + if (ioc_status & + MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) + ioc_loginfo = le32_to_cpu(def_reply->ioc_log_info); + ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; + if (def_reply->function == MPI3_FUNCTION_SCSI_IO) { + scsi_reply = (struct mpi3_scsi_io_reply *)def_reply; + sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc, + le64_to_cpu(scsi_reply->sense_data_buffer_address)); + } + break; + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS: + success_desc = (struct mpi3_success_reply_descriptor *)reply_desc; + host_tag = le16_to_cpu(success_desc->host_tag); + break; + default: + break; + } + + cmdptr = mpi3mr_get_drv_cmd(mrioc, host_tag, def_reply); + if (cmdptr) { + if (cmdptr->state & MPI3MR_CMD_PENDING) { + cmdptr->state |= MPI3MR_CMD_COMPLETE; + cmdptr->ioc_loginfo = ioc_loginfo; + cmdptr->ioc_status = ioc_status; + cmdptr->state &= ~MPI3MR_CMD_PENDING; + if (def_reply) { + cmdptr->state |= MPI3MR_CMD_REPLY_VALID; + memcpy((u8 *)cmdptr->reply, (u8 *)def_reply, + mrioc->facts.reply_sz); + } + if (cmdptr->is_waiting) { + complete(&cmdptr->done); + cmdptr->is_waiting = 0; + } else if (cmdptr->callback) + cmdptr->callback(mrioc, cmdptr); + } + } +out: + if (sense_buf) + mpi3mr_repost_sense_buf(mrioc, + le64_to_cpu(scsi_reply->sense_data_buffer_address)); +} + +static int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc) +{ + u32 exp_phase = mrioc->admin_reply_ephase; + u32 admin_reply_ci = mrioc->admin_reply_ci; + u32 num_admin_replies = 0; + u64 reply_dma = 0; + struct mpi3_default_reply_descriptor *reply_desc; + + reply_desc = (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base + + admin_reply_ci; + + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) + return 0; + + do { + mrioc->admin_req_ci = le16_to_cpu(reply_desc->request_queue_ci); + mpi3mr_process_admin_reply_desc(mrioc, reply_desc, &reply_dma); + if (reply_dma) + mpi3mr_repost_reply_buf(mrioc, reply_dma); + num_admin_replies++; + if (++admin_reply_ci == mrioc->num_admin_replies) { + admin_reply_ci = 0; + exp_phase ^= 1; + } + reply_desc = + (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base + + admin_reply_ci; + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) + break; + } while (1); + + writel(admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci); + mrioc->admin_reply_ci = admin_reply_ci; + mrioc->admin_reply_ephase = exp_phase; + + return num_admin_replies; +} + +/** + * mpi3mr_get_reply_desc - get reply descriptor frame corresponding to + * queue's consumer index from operational reply descriptor queue. + * @op_reply_q: op_reply_qinfo object + * @reply_ci: operational reply descriptor's queue consumer index + * + * Returns reply descriptor frame address + */ +static inline struct mpi3_default_reply_descriptor * +mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci) +{ + void *segment_base_addr; + struct segments *segments = op_reply_q->q_segments; + struct mpi3_default_reply_descriptor *reply_desc = NULL; + + segment_base_addr = + segments[reply_ci / op_reply_q->segment_qd].segment; + reply_desc = (struct mpi3_default_reply_descriptor *)segment_base_addr + + (reply_ci % op_reply_q->segment_qd); + return reply_desc; +} + +static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, + struct mpi3mr_intr_info *intr_info) +{ + struct op_reply_qinfo *op_reply_q = intr_info->op_reply_q; + struct op_req_qinfo *op_req_q; + u32 exp_phase; + u32 reply_ci; + u32 num_op_reply = 0; + u64 reply_dma = 0; + struct mpi3_default_reply_descriptor *reply_desc; + u16 req_q_idx = 0, reply_qidx; + + reply_qidx = op_reply_q->qid - 1; + + if (!atomic_add_unless(&op_reply_q->in_use, 1, 1)) + return 0; + + exp_phase = op_reply_q->ephase; + reply_ci = op_reply_q->ci; + + reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) { + atomic_dec(&op_reply_q->in_use); + return 0; + } + + do { + req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1; + op_req_q = &mrioc->req_qinfo[req_q_idx]; + + WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci)); + mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma, + reply_qidx); + atomic_dec(&op_reply_q->pend_ios); + if (reply_dma) + mpi3mr_repost_reply_buf(mrioc, reply_dma); + num_op_reply++; + + if (++reply_ci == op_reply_q->num_replies) { + reply_ci = 0; + exp_phase ^= 1; + } + + reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); + + if ((le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) + break; + /* + * Exit completion loop to avoid CPU lockup + * Ensure remaining completion happens from threaded ISR. + */ + if (num_op_reply > mrioc->max_host_ios) { + intr_info->op_reply_q->enable_irq_poll = true; + break; + } + + } while (1); + + writel(reply_ci, + &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index); + op_reply_q->ci = reply_ci; + op_reply_q->ephase = exp_phase; + + atomic_dec(&op_reply_q->in_use); + return num_op_reply; +} + +static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) +{ + struct mpi3mr_intr_info *intr_info = privdata; + struct mpi3mr_ioc *mrioc; + u16 midx; + u32 num_admin_replies = 0, num_op_reply = 0; + + if (!intr_info) + return IRQ_NONE; + + mrioc = intr_info->mrioc; + + if (!mrioc->intr_enabled) + return IRQ_NONE; + + midx = intr_info->msix_index; + + if (!midx) + num_admin_replies = mpi3mr_process_admin_reply_q(mrioc); + if (intr_info->op_reply_q) + num_op_reply = mpi3mr_process_op_reply_q(mrioc, intr_info); + + if (num_admin_replies || num_op_reply) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static irqreturn_t mpi3mr_isr(int irq, void *privdata) +{ + struct mpi3mr_intr_info *intr_info = privdata; + struct mpi3mr_ioc *mrioc; + u16 midx; + int ret; + + if (!intr_info) + return IRQ_NONE; + + mrioc = intr_info->mrioc; + midx = intr_info->msix_index; + /* Call primary ISR routine */ + ret = mpi3mr_isr_primary(irq, privdata); + + /* + * If more IOs are expected, schedule IRQ polling thread. + * Otherwise exit from ISR. + */ + if (!intr_info->op_reply_q) + return ret; + + if (!intr_info->op_reply_q->enable_irq_poll || + !atomic_read(&intr_info->op_reply_q->pend_ios)) + return ret; + + disable_irq_nosync(pci_irq_vector(mrioc->pdev, midx)); + + return IRQ_WAKE_THREAD; +} + +/** + * mpi3mr_isr_poll - Reply queue polling routine + * @irq: IRQ + * @privdata: Interrupt info + * + * poll for pending I/O completions in a loop until pending I/Os + * present or controller queue depth I/Os are processed. + * + * Return: IRQ_NONE or IRQ_HANDLED + */ +static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata) +{ + struct mpi3mr_intr_info *intr_info = privdata; + struct mpi3mr_ioc *mrioc; + u16 midx; + u32 num_op_reply = 0; + + if (!intr_info || !intr_info->op_reply_q) + return IRQ_NONE; + + mrioc = intr_info->mrioc; + midx = intr_info->msix_index; + + /* Poll for pending IOs completions */ + do { + if (!mrioc->intr_enabled) + break; + + if (!midx) + mpi3mr_process_admin_reply_q(mrioc); + if (intr_info->op_reply_q) + num_op_reply += + mpi3mr_process_op_reply_q(mrioc, intr_info); + + usleep_range(mrioc->irqpoll_sleep, 10 * mrioc->irqpoll_sleep); + + } while (atomic_read(&intr_info->op_reply_q->pend_ios) && + (num_op_reply < mrioc->max_host_ios)); + + intr_info->op_reply_q->enable_irq_poll = false; + enable_irq(pci_irq_vector(mrioc->pdev, midx)); + + return IRQ_HANDLED; +} + +/** + * mpi3mr_request_irq - Request IRQ and register ISR + * @mrioc: Adapter instance reference + * @index: IRQ vector index + * + * Request threaded ISR with primary ISR and secondary + * + * Return: 0 on success and non zero on failures. + */ +static inline int mpi3mr_request_irq(struct mpi3mr_ioc *mrioc, u16 index) +{ + struct pci_dev *pdev = mrioc->pdev; + struct mpi3mr_intr_info *intr_info = mrioc->intr_info + index; + int retval = 0; + + intr_info->mrioc = mrioc; + intr_info->msix_index = index; + intr_info->op_reply_q = NULL; + + snprintf(intr_info->name, MPI3MR_NAME_LENGTH, "%s%d-msix%d", + mrioc->driver_name, mrioc->id, index); + + retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr, + mpi3mr_isr_poll, IRQF_SHARED, intr_info->name, intr_info); + if (retval) { + ioc_err(mrioc, "%s: Unable to allocate interrupt %d!\n", + intr_info->name, pci_irq_vector(pdev, index)); + return retval; + } + + return retval; +} + +/** + * mpi3mr_setup_isr - Setup ISR for the controller + * @mrioc: Adapter instance reference + * @setup_one: Request one IRQ or more + * + * Allocate IRQ vectors and call mpi3mr_request_irq to setup ISR + * + * Return: 0 on success and non zero on failures. + */ +static int mpi3mr_setup_isr(struct mpi3mr_ioc *mrioc, u8 setup_one) +{ + unsigned int irq_flags = PCI_IRQ_MSIX; + u16 max_vectors = 0, i; + int retval = 0; + struct irq_affinity desc = { .pre_vectors = 1}; + + mpi3mr_cleanup_isr(mrioc); + + if (setup_one || reset_devices) + max_vectors = 1; + else { + max_vectors = + min_t(int, mrioc->cpu_count + 1, mrioc->msix_count); + + ioc_info(mrioc, + "MSI-X vectors supported: %d, no of cores: %d,", + mrioc->msix_count, mrioc->cpu_count); + ioc_info(mrioc, + "MSI-x vectors requested: %d\n", max_vectors); + } + + irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES; + + mrioc->op_reply_q_offset = (max_vectors > 1) ? 1 : 0; + i = pci_alloc_irq_vectors_affinity(mrioc->pdev, + 1, max_vectors, irq_flags, &desc); + if (i <= 0) { + ioc_err(mrioc, "Cannot alloc irq vectors\n"); + goto out_failed; + } + if (i != max_vectors) { + ioc_info(mrioc, + "allocated vectors (%d) are less than configured (%d)\n", + i, max_vectors); + /* + * If only one MSI-x is allocated, then MSI-x 0 will be shared + * between Admin queue and operational queue + */ + if (i == 1) + mrioc->op_reply_q_offset = 0; + + max_vectors = i; + } + mrioc->intr_info = kzalloc(sizeof(struct mpi3mr_intr_info) * max_vectors, + GFP_KERNEL); + if (!mrioc->intr_info) { + retval = -1; + pci_free_irq_vectors(mrioc->pdev); + goto out_failed; + } + for (i = 0; i < max_vectors; i++) { + retval = mpi3mr_request_irq(mrioc, i); + if (retval) { + mrioc->intr_info_count = i; + goto out_failed; + } + } + mrioc->intr_info_count = max_vectors; + mpi3mr_ioc_enable_intr(mrioc); + return retval; +out_failed: + mpi3mr_cleanup_isr(mrioc); + + return retval; +} + +static const struct { + enum mpi3mr_iocstate value; + char *name; +} mrioc_states[] = { + { MRIOC_STATE_READY, "ready" }, + { MRIOC_STATE_FAULT, "fault" }, + { MRIOC_STATE_RESET, "reset" }, + { MRIOC_STATE_BECOMING_READY, "becoming ready" }, + { MRIOC_STATE_RESET_REQUESTED, "reset requested" }, + { MRIOC_STATE_UNRECOVERABLE, "unrecoverable error" }, +}; + +static const char *mpi3mr_iocstate_name(enum mpi3mr_iocstate mrioc_state) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(mrioc_states); i++) { + if (mrioc_states[i].value == mrioc_state) { + name = mrioc_states[i].name; + break; + } + } + return name; +} + +/* Reset reason to name mapper structure*/ +static const struct { + enum mpi3mr_reset_reason value; + char *name; +} mpi3mr_reset_reason_codes[] = { + { MPI3MR_RESET_FROM_BRINGUP, "timeout in bringup" }, + { MPI3MR_RESET_FROM_FAULT_WATCH, "fault" }, + { MPI3MR_RESET_FROM_IOCTL, "application invocation" }, + { MPI3MR_RESET_FROM_EH_HOS, "error handling" }, + { MPI3MR_RESET_FROM_TM_TIMEOUT, "TM timeout" }, + { MPI3MR_RESET_FROM_IOCTL_TIMEOUT, "IOCTL timeout" }, + { MPI3MR_RESET_FROM_MUR_FAILURE, "MUR failure" }, + { MPI3MR_RESET_FROM_CTLR_CLEANUP, "timeout in controller cleanup" }, + { MPI3MR_RESET_FROM_CIACTIV_FAULT, "component image activation fault" }, + { MPI3MR_RESET_FROM_PE_TIMEOUT, "port enable timeout" }, + { MPI3MR_RESET_FROM_TSU_TIMEOUT, "time stamp update timeout" }, + { MPI3MR_RESET_FROM_DELREQQ_TIMEOUT, "delete request queue timeout" }, + { MPI3MR_RESET_FROM_DELREPQ_TIMEOUT, "delete reply queue timeout" }, + { + MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT, + "create request queue timeout" + }, + { + MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT, + "create reply queue timeout" + }, + { MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT, "IOC facts timeout" }, + { MPI3MR_RESET_FROM_IOCINIT_TIMEOUT, "IOC init timeout" }, + { MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT, "event notify timeout" }, + { MPI3MR_RESET_FROM_EVTACK_TIMEOUT, "event acknowledgment timeout" }, + { + MPI3MR_RESET_FROM_CIACTVRST_TIMER, + "component image activation timeout" + }, + { + MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT, + "get package version timeout" + }, + { MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" }, + { MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" }, +}; + +/** + * mpi3mr_reset_rc_name - get reset reason code name + * @reason_code: reset reason code value + * + * Map reset reason to an NULL terminated ASCII string + * + * Return: name corresponding to reset reason value or NULL. + */ +static const char *mpi3mr_reset_rc_name(enum mpi3mr_reset_reason reason_code) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_reason_codes); i++) { + if (mpi3mr_reset_reason_codes[i].value == reason_code) { + name = mpi3mr_reset_reason_codes[i].name; + break; + } + } + return name; +} + +/* Reset type to name mapper structure*/ +static const struct { + u16 reset_type; + char *name; +} mpi3mr_reset_types[] = { + { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, "soft" }, + { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, "diag fault" }, +}; + +/** + * mpi3mr_reset_type_name - get reset type name + * @reset_type: reset type value + * + * Map reset type to an NULL terminated ASCII string + * + * Return: name corresponding to reset type value or NULL. + */ +static const char *mpi3mr_reset_type_name(u16 reset_type) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_types); i++) { + if (mpi3mr_reset_types[i].reset_type == reset_type) { + name = mpi3mr_reset_types[i].name; + break; + } + } + return name; +} + +/** + * mpi3mr_print_fault_info - Display fault information + * @mrioc: Adapter instance reference + * + * Display the controller fault information if there is a + * controller fault. + * + * Return: Nothing. + */ +static void mpi3mr_print_fault_info(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_status, code, code1, code2, code3; + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + + if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) { + code = readl(&mrioc->sysif_regs->fault); + code1 = readl(&mrioc->sysif_regs->fault_info[0]); + code2 = readl(&mrioc->sysif_regs->fault_info[1]); + code3 = readl(&mrioc->sysif_regs->fault_info[2]); + + ioc_info(mrioc, + "fault code(0x%08X): Additional code: (0x%08X:0x%08X:0x%08X)\n", + code, code1, code2, code3); + } +} + +/** + * mpi3mr_get_iocstate - Get IOC State + * @mrioc: Adapter instance reference + * + * Return a proper IOC state enum based on the IOC status and + * IOC configuration and unrcoverable state of the controller. + * + * Return: Current IOC state. + */ +enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_status, ioc_config; + u8 ready, enabled; + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + + if (mrioc->unrecoverable) + return MRIOC_STATE_UNRECOVERABLE; + if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) + return MRIOC_STATE_FAULT; + + ready = (ioc_status & MPI3_SYSIF_IOC_STATUS_READY); + enabled = (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC); + + if (ready && enabled) + return MRIOC_STATE_READY; + if ((!ready) && (!enabled)) + return MRIOC_STATE_RESET; + if ((!ready) && (enabled)) + return MRIOC_STATE_BECOMING_READY; + + return MRIOC_STATE_RESET_REQUESTED; +} + +/** + * mpi3mr_clear_reset_history - clear reset history + * @mrioc: Adapter instance reference + * + * Write the reset history bit in IOC status to clear the bit, + * if it is already set. + * + * Return: Nothing. + */ +static inline void mpi3mr_clear_reset_history(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_status; + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) + writel(ioc_status, &mrioc->sysif_regs->ioc_status); +} + +/** + * mpi3mr_issue_and_process_mur - Message unit Reset handler + * @mrioc: Adapter instance reference + * @reset_reason: Reset reason code + * + * Issue Message unit Reset to the controller and wait for it to + * be complete. + * + * Return: 0 on success, -1 on failure. + */ +static int mpi3mr_issue_and_process_mur(struct mpi3mr_ioc *mrioc, + u32 reset_reason) +{ + u32 ioc_config, timeout, ioc_status; + int retval = -1; + + ioc_info(mrioc, "Issuing Message unit Reset(MUR)\n"); + if (mrioc->unrecoverable) { + ioc_info(mrioc, "IOC is unrecoverable MUR not issued\n"); + return retval; + } + mpi3mr_clear_reset_history(mrioc); + writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + ioc_config &= ~MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC; + writel(ioc_config, &mrioc->sysif_regs->ioc_configuration); + + timeout = mrioc->ready_timeout * 10; + do { + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)) { + mpi3mr_clear_reset_history(mrioc); + ioc_config = + readl(&mrioc->sysif_regs->ioc_configuration); + if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) || + (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) || + (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC))) { + retval = 0; + break; + } + } + msleep(100); + } while (--timeout); + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + + ioc_info(mrioc, "Base IOC Sts/Config after %s MUR is (0x%x)/(0x%x)\n", + (!retval) ? "successful" : "failed", ioc_status, ioc_config); + return retval; +} + +/** + * mpi3mr_bring_ioc_ready - Bring controller to ready state + * @mrioc: Adapter instance reference + * + * Set Enable IOC bit in IOC configuration register and wait for + * the controller to become ready. + * + * Return: 0 on success, -1 on failure. + */ +static int mpi3mr_bring_ioc_ready(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_config, timeout; + enum mpi3mr_iocstate current_state; + + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + ioc_config |= MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC; + writel(ioc_config, &mrioc->sysif_regs->ioc_configuration); + + timeout = mrioc->ready_timeout * 10; + do { + current_state = mpi3mr_get_iocstate(mrioc); + if (current_state == MRIOC_STATE_READY) + return 0; + msleep(100); + } while (--timeout); + + return -1; +} + +/** + * mpi3mr_soft_reset_success - Check softreset is success or not + * @ioc_status: IOC status register value + * @ioc_config: IOC config register value + * + * Check whether the soft reset is successful or not based on + * IOC status and IOC config register values. + * + * Return: True when the soft reset is success, false otherwise. + */ +static inline bool +mpi3mr_soft_reset_success(u32 ioc_status, u32 ioc_config) +{ + if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) || + (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) || + (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC))) + return true; + return false; +} + +/** + * mpi3mr_diagfault_success - Check diag fault is success or not + * @mrioc: Adapter reference + * @ioc_status: IOC status register value + * + * Check whether the controller hit diag reset fault code. + * + * Return: True when there is diag fault, false otherwise. + */ +static inline bool mpi3mr_diagfault_success(struct mpi3mr_ioc *mrioc, + u32 ioc_status) +{ + u32 fault; + + if (!(ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) + return false; + fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK; + if (fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET) + return true; + return false; +} + +/** + * mpi3mr_set_diagsave - Set diag save bit for snapdump + * @mrioc: Adapter reference + * + * Set diag save bit in IOC configuration register to enable + * snapdump. + * + * Return: Nothing. + */ +static inline void mpi3mr_set_diagsave(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_config; + + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + ioc_config |= MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE; + writel(ioc_config, &mrioc->sysif_regs->ioc_configuration); +} + +/** + * mpi3mr_issue_reset - Issue reset to the controller + * @mrioc: Adapter reference + * @reset_type: Reset type + * @reset_reason: Reset reason code + * + * Unlock the host diagnostic registers and write the specific + * reset type to that, wait for reset acknowledgment from the + * controller, if the reset is not successful retry for the + * predefined number of times. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, + u32 reset_reason) +{ + int retval = -1; + u8 unlock_retry_count, reset_retry_count = 0; + u32 host_diagnostic, timeout, ioc_status, ioc_config; + + pci_cfg_access_lock(mrioc->pdev); + if ((reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) && + (reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT)) + goto out; + if (mrioc->unrecoverable) + goto out; +retry_reset: + unlock_retry_count = 0; + mpi3mr_clear_reset_history(mrioc); + do { + ioc_info(mrioc, + "Write magic sequence to unlock host diag register (retry=%d)\n", + ++unlock_retry_count); + if (unlock_retry_count >= MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT) { + writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]); + mrioc->unrecoverable = 1; + goto out; + } + + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH, + &mrioc->sysif_regs->write_sequence); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH, + &mrioc->sysif_regs->write_sequence); + usleep_range(1000, 1100); + host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic); + ioc_info(mrioc, + "wrote magic sequence: retry_count(%d), host_diagnostic(0x%08x)\n", + unlock_retry_count, host_diagnostic); + } while (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE)); + + writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]); + ioc_info(mrioc, "%s reset due to %s(0x%x)\n", + mpi3mr_reset_type_name(reset_type), + mpi3mr_reset_rc_name(reset_reason), reset_reason); + writel(host_diagnostic | reset_type, + &mrioc->sysif_regs->host_diagnostic); + timeout = mrioc->ready_timeout * 10; + if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) { + do { + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if (ioc_status & + MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) { + mpi3mr_clear_reset_history(mrioc); + ioc_config = + readl(&mrioc->sysif_regs->ioc_configuration); + if (mpi3mr_soft_reset_success(ioc_status, + ioc_config)) { + retval = 0; + break; + } + } + msleep(100); + } while (--timeout); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND, + &mrioc->sysif_regs->write_sequence); + } else if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT) { + do { + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if (mpi3mr_diagfault_success(mrioc, ioc_status)) { + retval = 0; + break; + } + msleep(100); + } while (--timeout); + mpi3mr_clear_reset_history(mrioc); + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND, + &mrioc->sysif_regs->write_sequence); + } + if (retval && ((++reset_retry_count) < MPI3MR_MAX_RESET_RETRY_COUNT)) { + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + ioc_info(mrioc, + "Base IOC Sts/Config after reset try %d is (0x%x)/(0x%x)\n", + reset_retry_count, ioc_status, ioc_config); + goto retry_reset; + } + +out: + pci_cfg_access_unlock(mrioc->pdev); + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + + ioc_info(mrioc, + "Base IOC Sts/Config after %s reset is (0x%x)/(0x%x)\n", + (!retval) ? "successful" : "failed", ioc_status, + ioc_config); + return retval; +} + +/** + * mpi3mr_admin_request_post - Post request to admin queue + * @mrioc: Adapter reference + * @admin_req: MPI3 request + * @admin_req_sz: Request size + * @ignore_reset: Ignore reset in process + * + * Post the MPI3 request into admin request queue and + * inform the controller, if the queue is full return + * appropriate error. + * + * Return: 0 on success, non-zero on failure. + */ +int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req, + u16 admin_req_sz, u8 ignore_reset) +{ + u16 areq_pi = 0, areq_ci = 0, max_entries = 0; + int retval = 0; + unsigned long flags; + u8 *areq_entry; + + if (mrioc->unrecoverable) { + ioc_err(mrioc, "%s : Unrecoverable controller\n", __func__); + return -EFAULT; + } + + spin_lock_irqsave(&mrioc->admin_req_lock, flags); + areq_pi = mrioc->admin_req_pi; + areq_ci = mrioc->admin_req_ci; + max_entries = mrioc->num_admin_req; + if ((areq_ci == (areq_pi + 1)) || ((!areq_ci) && + (areq_pi == (max_entries - 1)))) { + ioc_err(mrioc, "AdminReqQ full condition detected\n"); + retval = -EAGAIN; + goto out; + } + if (!ignore_reset && mrioc->reset_in_progress) { + ioc_err(mrioc, "AdminReqQ submit reset in progress\n"); + retval = -EAGAIN; + goto out; + } + areq_entry = (u8 *)mrioc->admin_req_base + + (areq_pi * MPI3MR_ADMIN_REQ_FRAME_SZ); + memset(areq_entry, 0, MPI3MR_ADMIN_REQ_FRAME_SZ); + memcpy(areq_entry, (u8 *)admin_req, admin_req_sz); + + if (++areq_pi == max_entries) + areq_pi = 0; + mrioc->admin_req_pi = areq_pi; + + writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi); + +out: + spin_unlock_irqrestore(&mrioc->admin_req_lock, flags); + + return retval; +} + +/** + * mpi3mr_free_op_req_q_segments - free request memory segments + * @mrioc: Adapter instance reference + * @q_idx: operational request queue index + * + * Free memory segments allocated for operational request queue + * + * Return: Nothing. + */ +static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) +{ + u16 j; + int size; + struct segments *segments; + + segments = mrioc->req_qinfo[q_idx].q_segments; + if (!segments) + return; + + if (mrioc->enable_segqueue) { + size = MPI3MR_OP_REQ_Q_SEG_SIZE; + if (mrioc->req_qinfo[q_idx].q_segment_list) { + dma_free_coherent(&mrioc->pdev->dev, + MPI3MR_MAX_SEG_LIST_SIZE, + mrioc->req_qinfo[q_idx].q_segment_list, + mrioc->req_qinfo[q_idx].q_segment_list_dma); + mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL; + } + } else + size = mrioc->req_qinfo[q_idx].num_requests * + mrioc->facts.op_req_sz; + + for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) { + if (!segments[j].segment) + continue; + dma_free_coherent(&mrioc->pdev->dev, + size, segments[j].segment, segments[j].segment_dma); + segments[j].segment = NULL; + } + kfree(mrioc->req_qinfo[q_idx].q_segments); + mrioc->req_qinfo[q_idx].q_segments = NULL; + mrioc->req_qinfo[q_idx].qid = 0; +} + +/** + * mpi3mr_free_op_reply_q_segments - free reply memory segments + * @mrioc: Adapter instance reference + * @q_idx: operational reply queue index + * + * Free memory segments allocated for operational reply queue + * + * Return: Nothing. + */ +static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx) +{ + u16 j; + int size; + struct segments *segments; + + segments = mrioc->op_reply_qinfo[q_idx].q_segments; + if (!segments) + return; + + if (mrioc->enable_segqueue) { + size = MPI3MR_OP_REP_Q_SEG_SIZE; + if (mrioc->op_reply_qinfo[q_idx].q_segment_list) { + dma_free_coherent(&mrioc->pdev->dev, + MPI3MR_MAX_SEG_LIST_SIZE, + mrioc->op_reply_qinfo[q_idx].q_segment_list, + mrioc->op_reply_qinfo[q_idx].q_segment_list_dma); + mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL; + } + } else + size = mrioc->op_reply_qinfo[q_idx].segment_qd * + mrioc->op_reply_desc_sz; + + for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) { + if (!segments[j].segment) + continue; + dma_free_coherent(&mrioc->pdev->dev, + size, segments[j].segment, segments[j].segment_dma); + segments[j].segment = NULL; + } + + kfree(mrioc->op_reply_qinfo[q_idx].q_segments); + mrioc->op_reply_qinfo[q_idx].q_segments = NULL; + mrioc->op_reply_qinfo[q_idx].qid = 0; +} + +/** + * mpi3mr_delete_op_reply_q - delete operational reply queue + * @mrioc: Adapter instance reference + * @qidx: operational reply queue index + * + * Delete operatinal reply queue by issuing MPI request + * through admin queue. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_delete_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct mpi3_delete_reply_queue_request delq_req; + int retval = 0; + u16 reply_qid = 0, midx; + + reply_qid = mrioc->op_reply_qinfo[qidx].qid; + + midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset); + + if (!reply_qid) { + retval = -1; + ioc_err(mrioc, "Issue DelRepQ: called with invalid ReqQID\n"); + goto out; + } + + memset(&delq_req, 0, sizeof(delq_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue DelRepQ: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + delq_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + delq_req.function = MPI3_FUNCTION_DELETE_REPLY_QUEUE; + delq_req.queue_id = cpu_to_le16(reply_qid); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &delq_req, sizeof(delq_req), + 1); + if (retval) { + ioc_err(mrioc, "Issue DelRepQ: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue DelRepQ: command timed out\n"); + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_DELREPQ_TIMEOUT); + mrioc->unrecoverable = 1; + + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Issue DelRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + mrioc->intr_info[midx].op_reply_q = NULL; + + mpi3mr_free_op_reply_q_segments(mrioc, qidx); +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); +out: + + return retval; +} + +/** + * mpi3mr_alloc_op_reply_q_segments -Alloc segmented reply pool + * @mrioc: Adapter instance reference + * @qidx: request queue index + * + * Allocate segmented memory pools for operational reply + * queue. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_alloc_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx; + int i, size; + u64 *q_segment_list_entry = NULL; + struct segments *segments; + + if (mrioc->enable_segqueue) { + op_reply_q->segment_qd = + MPI3MR_OP_REP_Q_SEG_SIZE / mrioc->op_reply_desc_sz; + + size = MPI3MR_OP_REP_Q_SEG_SIZE; + + op_reply_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev, + MPI3MR_MAX_SEG_LIST_SIZE, &op_reply_q->q_segment_list_dma, + GFP_KERNEL); + if (!op_reply_q->q_segment_list) + return -ENOMEM; + q_segment_list_entry = (u64 *)op_reply_q->q_segment_list; + } else { + op_reply_q->segment_qd = op_reply_q->num_replies; + size = op_reply_q->num_replies * mrioc->op_reply_desc_sz; + } + + op_reply_q->num_segments = DIV_ROUND_UP(op_reply_q->num_replies, + op_reply_q->segment_qd); + + op_reply_q->q_segments = kcalloc(op_reply_q->num_segments, + sizeof(struct segments), GFP_KERNEL); + if (!op_reply_q->q_segments) + return -ENOMEM; + + segments = op_reply_q->q_segments; + for (i = 0; i < op_reply_q->num_segments; i++) { + segments[i].segment = + dma_alloc_coherent(&mrioc->pdev->dev, + size, &segments[i].segment_dma, GFP_KERNEL); + if (!segments[i].segment) + return -ENOMEM; + if (mrioc->enable_segqueue) + q_segment_list_entry[i] = + (unsigned long)segments[i].segment_dma; + } + + return 0; +} + +/** + * mpi3mr_alloc_op_req_q_segments - Alloc segmented req pool. + * @mrioc: Adapter instance reference + * @qidx: request queue index + * + * Allocate segmented memory pools for operational request + * queue. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_alloc_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx; + int i, size; + u64 *q_segment_list_entry = NULL; + struct segments *segments; + + if (mrioc->enable_segqueue) { + op_req_q->segment_qd = + MPI3MR_OP_REQ_Q_SEG_SIZE / mrioc->facts.op_req_sz; + + size = MPI3MR_OP_REQ_Q_SEG_SIZE; + + op_req_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev, + MPI3MR_MAX_SEG_LIST_SIZE, &op_req_q->q_segment_list_dma, + GFP_KERNEL); + if (!op_req_q->q_segment_list) + return -ENOMEM; + q_segment_list_entry = (u64 *)op_req_q->q_segment_list; + + } else { + op_req_q->segment_qd = op_req_q->num_requests; + size = op_req_q->num_requests * mrioc->facts.op_req_sz; + } + + op_req_q->num_segments = DIV_ROUND_UP(op_req_q->num_requests, + op_req_q->segment_qd); + + op_req_q->q_segments = kcalloc(op_req_q->num_segments, + sizeof(struct segments), GFP_KERNEL); + if (!op_req_q->q_segments) + return -ENOMEM; + + segments = op_req_q->q_segments; + for (i = 0; i < op_req_q->num_segments; i++) { + segments[i].segment = + dma_alloc_coherent(&mrioc->pdev->dev, + size, &segments[i].segment_dma, GFP_KERNEL); + if (!segments[i].segment) + return -ENOMEM; + if (mrioc->enable_segqueue) + q_segment_list_entry[i] = + (unsigned long)segments[i].segment_dma; + } + + return 0; +} + +/** + * mpi3mr_create_op_reply_q - create operational reply queue + * @mrioc: Adapter instance reference + * @qidx: operational reply queue index + * + * Create operatinal reply queue by issuing MPI request + * through admin queue. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_create_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct mpi3_create_reply_queue_request create_req; + struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx; + int retval = 0; + u16 reply_qid = 0, midx; + + reply_qid = op_reply_q->qid; + + midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset); + + if (reply_qid) { + retval = -1; + ioc_err(mrioc, "CreateRepQ: called for duplicate qid %d\n", + reply_qid); + + return retval; + } + + reply_qid = qidx + 1; + op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD; + op_reply_q->ci = 0; + op_reply_q->ephase = 1; + atomic_set(&op_reply_q->pend_ios, 0); + atomic_set(&op_reply_q->in_use, 0); + op_reply_q->enable_irq_poll = false; + + if (!op_reply_q->q_segments) { + retval = mpi3mr_alloc_op_reply_q_segments(mrioc, qidx); + if (retval) { + mpi3mr_free_op_reply_q_segments(mrioc, qidx); + goto out; + } + } + + memset(&create_req, 0, sizeof(create_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "CreateRepQ: Init command is in use\n"); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + create_req.function = MPI3_FUNCTION_CREATE_REPLY_QUEUE; + create_req.queue_id = cpu_to_le16(reply_qid); + create_req.flags = MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE; + create_req.msix_index = cpu_to_le16(mrioc->intr_info[midx].msix_index); + if (mrioc->enable_segqueue) { + create_req.flags |= + MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED; + create_req.base_address = cpu_to_le64( + op_reply_q->q_segment_list_dma); + } else + create_req.base_address = cpu_to_le64( + op_reply_q->q_segments[0].segment_dma); + + create_req.size = cpu_to_le16(op_reply_q->num_replies); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &create_req, + sizeof(create_req), 1); + if (retval) { + ioc_err(mrioc, "CreateRepQ: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "CreateRepQ: command timed out\n"); + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT); + mrioc->unrecoverable = 1; + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "CreateRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + op_reply_q->qid = reply_qid; + mrioc->intr_info[midx].op_reply_q = op_reply_q; + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); +out: + + return retval; +} + +/** + * mpi3mr_create_op_req_q - create operational request queue + * @mrioc: Adapter instance reference + * @idx: operational request queue index + * @reply_qid: Reply queue ID + * + * Create operatinal request queue by issuing MPI request + * through admin queue. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx, + u16 reply_qid) +{ + struct mpi3_create_request_queue_request create_req; + struct op_req_qinfo *op_req_q = mrioc->req_qinfo + idx; + int retval = 0; + u16 req_qid = 0; + + req_qid = op_req_q->qid; + + if (req_qid) { + retval = -1; + ioc_err(mrioc, "CreateReqQ: called for duplicate qid %d\n", + req_qid); + + return retval; + } + req_qid = idx + 1; + + op_req_q->num_requests = MPI3MR_OP_REQ_Q_QD; + op_req_q->ci = 0; + op_req_q->pi = 0; + op_req_q->reply_qid = reply_qid; + spin_lock_init(&op_req_q->q_lock); + + if (!op_req_q->q_segments) { + retval = mpi3mr_alloc_op_req_q_segments(mrioc, idx); + if (retval) { + mpi3mr_free_op_req_q_segments(mrioc, idx); + goto out; + } + } + + memset(&create_req, 0, sizeof(create_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "CreateReqQ: Init command is in use\n"); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + create_req.function = MPI3_FUNCTION_CREATE_REQUEST_QUEUE; + create_req.queue_id = cpu_to_le16(req_qid); + if (mrioc->enable_segqueue) { + create_req.flags = + MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED; + create_req.base_address = cpu_to_le64( + op_req_q->q_segment_list_dma); + } else + create_req.base_address = cpu_to_le64( + op_req_q->q_segments[0].segment_dma); + create_req.reply_queue_id = cpu_to_le16(reply_qid); + create_req.size = cpu_to_le16(op_req_q->num_requests); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &create_req, + sizeof(create_req), 1); + if (retval) { + ioc_err(mrioc, "CreateReqQ: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "CreateReqQ: command timed out\n"); + mpi3mr_set_diagsave(mrioc); + if (mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT)) + mrioc->unrecoverable = 1; + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "CreateReqQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + op_req_q->qid = req_qid; + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); +out: + + return retval; +} + +/** + * mpi3mr_create_op_queues - create operational queue pairs + * @mrioc: Adapter instance reference + * + * Allocate memory for operational queue meta data and call + * create request and reply queue functions. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc) +{ + int retval = 0; + u16 num_queues = 0, i = 0, msix_count_op_q = 1; + + num_queues = min_t(int, mrioc->facts.max_op_reply_q, + mrioc->facts.max_op_req_q); + + msix_count_op_q = + mrioc->intr_info_count - mrioc->op_reply_q_offset; + if (!mrioc->num_queues) + mrioc->num_queues = min_t(int, num_queues, msix_count_op_q); + num_queues = mrioc->num_queues; + ioc_info(mrioc, "Trying to create %d Operational Q pairs\n", + num_queues); + + if (!mrioc->req_qinfo) { + mrioc->req_qinfo = kcalloc(num_queues, + sizeof(struct op_req_qinfo), GFP_KERNEL); + if (!mrioc->req_qinfo) { + retval = -1; + goto out_failed; + } + + mrioc->op_reply_qinfo = kzalloc(sizeof(struct op_reply_qinfo) * + num_queues, GFP_KERNEL); + if (!mrioc->op_reply_qinfo) { + retval = -1; + goto out_failed; + } + } + + if (mrioc->enable_segqueue) + ioc_info(mrioc, + "allocating operational queues through segmented queues\n"); + + for (i = 0; i < num_queues; i++) { + if (mpi3mr_create_op_reply_q(mrioc, i)) { + ioc_err(mrioc, "Cannot create OP RepQ %d\n", i); + break; + } + if (mpi3mr_create_op_req_q(mrioc, i, + mrioc->op_reply_qinfo[i].qid)) { + ioc_err(mrioc, "Cannot create OP ReqQ %d\n", i); + mpi3mr_delete_op_reply_q(mrioc, i); + break; + } + } + + if (i == 0) { + /* Not even one queue is created successfully*/ + retval = -1; + goto out_failed; + } + mrioc->num_op_reply_q = mrioc->num_op_req_q = i; + ioc_info(mrioc, "Successfully created %d Operational Q pairs\n", + mrioc->num_op_reply_q); + + return retval; +out_failed: + kfree(mrioc->req_qinfo); + mrioc->req_qinfo = NULL; + + kfree(mrioc->op_reply_qinfo); + mrioc->op_reply_qinfo = NULL; + + return retval; +} + +/** + * mpi3mr_op_request_post - Post request to operational queue + * @mrioc: Adapter reference + * @op_req_q: Operational request queue info + * @req: MPI3 request + * + * Post the MPI3 request into operational request queue and + * inform the controller, if the queue is full return + * appropriate error. + * + * Return: 0 on success, non-zero on failure. + */ +int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, + struct op_req_qinfo *op_req_q, u8 *req) +{ + u16 pi = 0, max_entries, reply_qidx = 0, midx; + int retval = 0; + unsigned long flags; + u8 *req_entry; + void *segment_base_addr; + u16 req_sz = mrioc->facts.op_req_sz; + struct segments *segments = op_req_q->q_segments; + + reply_qidx = op_req_q->reply_qid - 1; + + if (mrioc->unrecoverable) + return -EFAULT; + + spin_lock_irqsave(&op_req_q->q_lock, flags); + pi = op_req_q->pi; + max_entries = op_req_q->num_requests; + + if (mpi3mr_check_req_qfull(op_req_q)) { + midx = REPLY_QUEUE_IDX_TO_MSIX_IDX( + reply_qidx, mrioc->op_reply_q_offset); + mpi3mr_process_op_reply_q(mrioc, &mrioc->intr_info[midx]); + + if (mpi3mr_check_req_qfull(op_req_q)) { + retval = -EAGAIN; + goto out; + } + } + + if (mrioc->reset_in_progress) { + ioc_err(mrioc, "OpReqQ submit reset in progress\n"); + retval = -EAGAIN; + goto out; + } + + segment_base_addr = segments[pi / op_req_q->segment_qd].segment; + req_entry = (u8 *)segment_base_addr + + ((pi % op_req_q->segment_qd) * req_sz); + + memset(req_entry, 0, req_sz); + memcpy(req_entry, req, MPI3MR_ADMIN_REQ_FRAME_SZ); + + if (++pi == max_entries) + pi = 0; + op_req_q->pi = pi; + + if (atomic_inc_return(&mrioc->op_reply_qinfo[reply_qidx].pend_ios) + > MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT) + mrioc->op_reply_qinfo[reply_qidx].enable_irq_poll = true; + + writel(op_req_q->pi, + &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].producer_index); + +out: + spin_unlock_irqrestore(&op_req_q->q_lock, flags); + return retval; +} + +/** + * mpi3mr_sync_timestamp - Issue time stamp sync request + * @mrioc: Adapter reference + * + * Issue IO unit control MPI request to synchornize firmware + * timestamp with host time. + * + * Return: 0 on success, non-zero on failure. + */ +static int mpi3mr_sync_timestamp(struct mpi3mr_ioc *mrioc) +{ + ktime_t current_time; + struct mpi3_iounit_control_request iou_ctrl; + int retval = 0; + + memset(&iou_ctrl, 0, sizeof(iou_ctrl)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue IOUCTL time_stamp: command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + iou_ctrl.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL; + iou_ctrl.operation = MPI3_CTRL_OP_UPDATE_TIMESTAMP; + current_time = ktime_get_real(); + iou_ctrl.param64[0] = cpu_to_le64(ktime_to_ms(current_time)); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl, + sizeof(iou_ctrl), 0); + if (retval) { + ioc_err(mrioc, "Issue IOUCTL time_stamp: Admin Post failed\n"); + goto out_unlock; + } + + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue IOUCTL time_stamp: command timed out\n"); + mrioc->init_cmds.is_waiting = 0; + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_TSU_TIMEOUT, 1); + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Issue IOUCTL time_stamp: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); + +out: + return retval; +} + +/** + * mpi3mr_watchdog_work - watchdog thread to monitor faults + * @work: work struct + * + * Watch dog work periodically executed (1 second interval) to + * monitor firmware fault and to issue periodic timer sync to + * the firmware. + * + * Return: Nothing. + */ +static void mpi3mr_watchdog_work(struct work_struct *work) +{ + struct mpi3mr_ioc *mrioc = + container_of(work, struct mpi3mr_ioc, watchdog_work.work); + unsigned long flags; + enum mpi3mr_iocstate ioc_state; + u32 fault, host_diagnostic; + + if (mrioc->ts_update_counter++ >= MPI3MR_TSUPDATE_INTERVAL) { + mrioc->ts_update_counter = 0; + mpi3mr_sync_timestamp(mrioc); + } + + /*Check for fault state every one second and issue Soft reset*/ + ioc_state = mpi3mr_get_iocstate(mrioc); + if (ioc_state == MRIOC_STATE_FAULT) { + fault = readl(&mrioc->sysif_regs->fault) & + MPI3_SYSIF_FAULT_CODE_MASK; + host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic); + if (host_diagnostic & MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS) { + if (!mrioc->diagsave_timeout) { + mpi3mr_print_fault_info(mrioc); + ioc_warn(mrioc, "Diag save in progress\n"); + } + if ((mrioc->diagsave_timeout++) <= + MPI3_SYSIF_DIAG_SAVE_TIMEOUT) + goto schedule_work; + } else + mpi3mr_print_fault_info(mrioc); + mrioc->diagsave_timeout = 0; + + if (fault == MPI3_SYSIF_FAULT_CODE_FACTORY_RESET) { + ioc_info(mrioc, + "Factory Reset fault occurred marking controller as unrecoverable" + ); + mrioc->unrecoverable = 1; + goto out; + } + + if ((fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET) || + (fault == MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS) || + (mrioc->reset_in_progress)) + goto out; + if (fault == MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET) + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_CIACTIV_FAULT, 0); + else + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_FAULT_WATCH, 0); + } + +schedule_work: + spin_lock_irqsave(&mrioc->watchdog_lock, flags); + if (mrioc->watchdog_work_q) + queue_delayed_work(mrioc->watchdog_work_q, + &mrioc->watchdog_work, + msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL)); + spin_unlock_irqrestore(&mrioc->watchdog_lock, flags); +out: + return; +} + +/** + * mpi3mr_start_watchdog - Start watchdog + * @mrioc: Adapter instance reference + * + * Create and start the watchdog thread to monitor controller + * faults. + * + * Return: Nothing. + */ +void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc) +{ + if (mrioc->watchdog_work_q) + return; + + INIT_DELAYED_WORK(&mrioc->watchdog_work, mpi3mr_watchdog_work); + snprintf(mrioc->watchdog_work_q_name, + sizeof(mrioc->watchdog_work_q_name), "watchdog_%s%d", mrioc->name, + mrioc->id); + mrioc->watchdog_work_q = + create_singlethread_workqueue(mrioc->watchdog_work_q_name); + if (!mrioc->watchdog_work_q) { + ioc_err(mrioc, "%s: failed (line=%d)\n", __func__, __LINE__); + return; + } + + if (mrioc->watchdog_work_q) + queue_delayed_work(mrioc->watchdog_work_q, + &mrioc->watchdog_work, + msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL)); +} + +/** + * mpi3mr_stop_watchdog - Stop watchdog + * @mrioc: Adapter instance reference + * + * Stop the watchdog thread created to monitor controller + * faults. + * + * Return: Nothing. + */ +void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc) +{ + unsigned long flags; + struct workqueue_struct *wq; + + spin_lock_irqsave(&mrioc->watchdog_lock, flags); + wq = mrioc->watchdog_work_q; + mrioc->watchdog_work_q = NULL; + spin_unlock_irqrestore(&mrioc->watchdog_lock, flags); + if (wq) { + if (!cancel_delayed_work_sync(&mrioc->watchdog_work)) + flush_workqueue(wq); + destroy_workqueue(wq); + } +} + +/** + * mpi3mr_kill_ioc - Kill the controller + * @mrioc: Adapter instance reference + * @reason: reason for the failure. + * + * If fault debug is enabled, display the fault info else issue + * diag fault and freeze the system for controller debug + * purpose. + * + * Return: Nothing. + */ +static void mpi3mr_kill_ioc(struct mpi3mr_ioc *mrioc, u32 reason) +{ + enum mpi3mr_iocstate ioc_state; + + if (!mrioc->fault_dbg) + return; + + dump_stack(); + + ioc_state = mpi3mr_get_iocstate(mrioc); + if (ioc_state == MRIOC_STATE_FAULT) + mpi3mr_print_fault_info(mrioc); + else { + ioc_err(mrioc, "Firmware is halted due to the reason %d\n", + reason); + mpi3mr_diagfault_reset_handler(mrioc, reason); + } + if (mrioc->fault_dbg == 2) + for (;;) + ; + else + panic("panic in %s\n", __func__); +} + +/** + * mpi3mr_setup_admin_qpair - Setup admin queue pair + * @mrioc: Adapter instance reference + * + * Allocate memory for admin queue pair if required and register + * the admin queue with the controller. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc) +{ + int retval = 0; + u32 num_admin_entries = 0; + + mrioc->admin_req_q_sz = MPI3MR_ADMIN_REQ_Q_SIZE; + mrioc->num_admin_req = mrioc->admin_req_q_sz / + MPI3MR_ADMIN_REQ_FRAME_SZ; + mrioc->admin_req_ci = mrioc->admin_req_pi = 0; + mrioc->admin_req_base = NULL; + + mrioc->admin_reply_q_sz = MPI3MR_ADMIN_REPLY_Q_SIZE; + mrioc->num_admin_replies = mrioc->admin_reply_q_sz / + MPI3MR_ADMIN_REPLY_FRAME_SZ; + mrioc->admin_reply_ci = 0; + mrioc->admin_reply_ephase = 1; + mrioc->admin_reply_base = NULL; + + if (!mrioc->admin_req_base) { + mrioc->admin_req_base = dma_alloc_coherent(&mrioc->pdev->dev, + mrioc->admin_req_q_sz, &mrioc->admin_req_dma, GFP_KERNEL); + + if (!mrioc->admin_req_base) { + retval = -1; + goto out_failed; + } + + mrioc->admin_reply_base = dma_alloc_coherent(&mrioc->pdev->dev, + mrioc->admin_reply_q_sz, &mrioc->admin_reply_dma, + GFP_KERNEL); + + if (!mrioc->admin_reply_base) { + retval = -1; + goto out_failed; + } + } + + num_admin_entries = (mrioc->num_admin_replies << 16) | + (mrioc->num_admin_req); + writel(num_admin_entries, &mrioc->sysif_regs->admin_queue_num_entries); + mpi3mr_writeq(mrioc->admin_req_dma, + &mrioc->sysif_regs->admin_request_queue_address); + mpi3mr_writeq(mrioc->admin_reply_dma, + &mrioc->sysif_regs->admin_reply_queue_address); + writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi); + writel(mrioc->admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci); + return retval; + +out_failed: + + if (mrioc->admin_reply_base) { + dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz, + mrioc->admin_reply_base, mrioc->admin_reply_dma); + mrioc->admin_reply_base = NULL; + } + if (mrioc->admin_req_base) { + dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz, + mrioc->admin_req_base, mrioc->admin_req_dma); + mrioc->admin_req_base = NULL; + } + return retval; +} + +/** + * mpi3mr_issue_iocfacts - Send IOC Facts + * @mrioc: Adapter instance reference + * @facts_data: Cached IOC facts data + * + * Issue IOC Facts MPI request through admin queue and wait for + * the completion of it or time out. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_issue_iocfacts(struct mpi3mr_ioc *mrioc, + struct mpi3_ioc_facts_data *facts_data) +{ + struct mpi3_ioc_facts_request iocfacts_req; + void *data = NULL; + dma_addr_t data_dma; + u32 data_len = sizeof(*facts_data); + int retval = 0; + u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST; + + data = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma, + GFP_KERNEL); + + if (!data) { + retval = -1; + goto out; + } + + memset(&iocfacts_req, 0, sizeof(iocfacts_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue IOCFacts: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + iocfacts_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + iocfacts_req.function = MPI3_FUNCTION_IOC_FACTS; + + mpi3mr_add_sg_single(&iocfacts_req.sgl, sgl_flags, data_len, + data_dma); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &iocfacts_req, + sizeof(iocfacts_req), 1); + if (retval) { + ioc_err(mrioc, "Issue IOCFacts: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue IOCFacts: command timed out\n"); + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT); + mrioc->unrecoverable = 1; + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Issue IOCFacts: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + memcpy(facts_data, (u8 *)data, data_len); +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); + +out: + if (data) + dma_free_coherent(&mrioc->pdev->dev, data_len, data, data_dma); + + return retval; +} + +/** + * mpi3mr_check_reset_dma_mask - Process IOC facts data + * @mrioc: Adapter instance reference + * + * Check whether the new DMA mask requested through IOCFacts by + * firmware needs to be set, if so set it . + * + * Return: 0 on success, non-zero on failure. + */ +static inline int mpi3mr_check_reset_dma_mask(struct mpi3mr_ioc *mrioc) +{ + struct pci_dev *pdev = mrioc->pdev; + int r; + u64 facts_dma_mask = DMA_BIT_MASK(mrioc->facts.dma_mask); + + if (!mrioc->facts.dma_mask || (mrioc->dma_mask <= facts_dma_mask)) + return 0; + + ioc_info(mrioc, "Changing DMA mask from 0x%016llx to 0x%016llx\n", + mrioc->dma_mask, facts_dma_mask); + + r = dma_set_mask_and_coherent(&pdev->dev, facts_dma_mask); + if (r) { + ioc_err(mrioc, "Setting DMA mask to 0x%016llx failed: %d\n", + facts_dma_mask, r); + return r; + } + mrioc->dma_mask = facts_dma_mask; + return r; +} + +/** + * mpi3mr_process_factsdata - Process IOC facts data + * @mrioc: Adapter instance reference + * @facts_data: Cached IOC facts data + * + * Convert IOC facts data into cpu endianness and cache it in + * the driver . + * + * Return: Nothing. + */ +static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, + struct mpi3_ioc_facts_data *facts_data) +{ + u32 ioc_config, req_sz, facts_flags; + + if ((le16_to_cpu(facts_data->ioc_facts_data_length)) != + (sizeof(*facts_data) / 4)) { + ioc_warn(mrioc, + "IOCFactsdata length mismatch driver_sz(%zu) firmware_sz(%d)\n", + sizeof(*facts_data), + le16_to_cpu(facts_data->ioc_facts_data_length) * 4); + } + + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + req_sz = 1 << ((ioc_config & MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ) >> + MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT); + if (le16_to_cpu(facts_data->ioc_request_frame_size) != (req_sz / 4)) { + ioc_err(mrioc, + "IOCFacts data reqFrameSize mismatch hw_size(%d) firmware_sz(%d)\n", + req_sz / 4, le16_to_cpu(facts_data->ioc_request_frame_size)); + } + + memset(&mrioc->facts, 0, sizeof(mrioc->facts)); + + facts_flags = le32_to_cpu(facts_data->flags); + mrioc->facts.op_req_sz = req_sz; + mrioc->op_reply_desc_sz = 1 << ((ioc_config & + MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ) >> + MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT); + + mrioc->facts.ioc_num = facts_data->ioc_number; + mrioc->facts.who_init = facts_data->who_init; + mrioc->facts.max_msix_vectors = le16_to_cpu(facts_data->max_msix_vectors); + mrioc->facts.personality = (facts_flags & + MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK); + mrioc->facts.dma_mask = (facts_flags & + MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK) >> + MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT; + mrioc->facts.protocol_flags = facts_data->protocol_flags; + mrioc->facts.mpi_version = le32_to_cpu(facts_data->mpi_version.word); + mrioc->facts.max_reqs = le16_to_cpu(facts_data->max_outstanding_request); + mrioc->facts.product_id = le16_to_cpu(facts_data->product_id); + mrioc->facts.reply_sz = le16_to_cpu(facts_data->reply_frame_size) * 4; + mrioc->facts.exceptions = le16_to_cpu(facts_data->ioc_exceptions); + mrioc->facts.max_perids = le16_to_cpu(facts_data->max_persistent_id); + mrioc->facts.max_pds = le16_to_cpu(facts_data->max_pds); + mrioc->facts.max_vds = le16_to_cpu(facts_data->max_vds); + mrioc->facts.max_hpds = le16_to_cpu(facts_data->max_host_pds); + mrioc->facts.max_advhpds = le16_to_cpu(facts_data->max_advanced_host_pds); + mrioc->facts.max_raidpds = le16_to_cpu(facts_data->max_raid_pds); + mrioc->facts.max_nvme = le16_to_cpu(facts_data->max_nvme); + mrioc->facts.max_pcie_switches = + le16_to_cpu(facts_data->max_pc_ie_switches); + mrioc->facts.max_sasexpanders = + le16_to_cpu(facts_data->max_sas_expanders); + mrioc->facts.max_sasinitiators = + le16_to_cpu(facts_data->max_sas_initiators); + mrioc->facts.max_enclosures = le16_to_cpu(facts_data->max_enclosures); + mrioc->facts.min_devhandle = le16_to_cpu(facts_data->min_dev_handle); + mrioc->facts.max_devhandle = le16_to_cpu(facts_data->max_dev_handle); + mrioc->facts.max_op_req_q = + le16_to_cpu(facts_data->max_operational_request_queues); + mrioc->facts.max_op_reply_q = + le16_to_cpu(facts_data->max_operational_reply_queues); + mrioc->facts.ioc_capabilities = + le32_to_cpu(facts_data->ioc_capabilities); + mrioc->facts.fw_ver.build_num = + le16_to_cpu(facts_data->fw_version.build_num); + mrioc->facts.fw_ver.cust_id = + le16_to_cpu(facts_data->fw_version.customer_id); + mrioc->facts.fw_ver.ph_minor = facts_data->fw_version.phase_minor; + mrioc->facts.fw_ver.ph_major = facts_data->fw_version.phase_major; + mrioc->facts.fw_ver.gen_minor = facts_data->fw_version.gen_minor; + mrioc->facts.fw_ver.gen_major = facts_data->fw_version.gen_major; + mrioc->msix_count = min_t(int, mrioc->msix_count, + mrioc->facts.max_msix_vectors); + mrioc->facts.sge_mod_mask = facts_data->sge_modifier_mask; + mrioc->facts.sge_mod_value = facts_data->sge_modifier_value; + mrioc->facts.sge_mod_shift = facts_data->sge_modifier_shift; + mrioc->facts.shutdown_timeout = + le16_to_cpu(facts_data->shutdown_timeout); + + ioc_info(mrioc, "ioc_num(%d), maxopQ(%d), maxopRepQ(%d), maxdh(%d),", + mrioc->facts.ioc_num, mrioc->facts.max_op_req_q, + mrioc->facts.max_op_reply_q, mrioc->facts.max_devhandle); + ioc_info(mrioc, + "maxreqs(%d), mindh(%d) maxPDs(%d) maxvectors(%d) maxperids(%d)\n", + mrioc->facts.max_reqs, mrioc->facts.min_devhandle, + mrioc->facts.max_pds, mrioc->facts.max_msix_vectors, + mrioc->facts.max_perids); + ioc_info(mrioc, "SGEModMask 0x%x SGEModVal 0x%x SGEModShift 0x%x ", + mrioc->facts.sge_mod_mask, mrioc->facts.sge_mod_value, + mrioc->facts.sge_mod_shift); + ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x\n", + mrioc->facts.dma_mask, (facts_flags & + MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK)); + + mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD; + + if (reset_devices) + mrioc->max_host_ios = min_t(int, mrioc->max_host_ios, + MPI3MR_HOST_IOS_KDUMP); +} + +/** + * mpi3mr_alloc_reply_sense_bufs - Send IOC Init + * @mrioc: Adapter instance reference + * + * Allocate and initialize the reply free buffers, sense + * buffers, reply free queue and sense buffer queue. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_alloc_reply_sense_bufs(struct mpi3mr_ioc *mrioc) +{ + int retval = 0; + u32 sz, i; + dma_addr_t phy_addr; + + if (mrioc->init_cmds.reply) + goto post_reply_sbuf; + + mrioc->init_cmds.reply = kzalloc(mrioc->facts.reply_sz, GFP_KERNEL); + if (!mrioc->init_cmds.reply) + goto out_failed; + + for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) { + mrioc->dev_rmhs_cmds[i].reply = kzalloc(mrioc->facts.reply_sz, + GFP_KERNEL); + if (!mrioc->dev_rmhs_cmds[i].reply) + goto out_failed; + } + + mrioc->host_tm_cmds.reply = kzalloc(mrioc->facts.reply_sz, GFP_KERNEL); + if (!mrioc->host_tm_cmds.reply) + goto out_failed; + + mrioc->dev_handle_bitmap_sz = mrioc->facts.max_devhandle / 8; + if (mrioc->facts.max_devhandle % 8) + mrioc->dev_handle_bitmap_sz++; + mrioc->removepend_bitmap = kzalloc(mrioc->dev_handle_bitmap_sz, + GFP_KERNEL); + if (!mrioc->removepend_bitmap) + goto out_failed; + + mrioc->devrem_bitmap_sz = MPI3MR_NUM_DEVRMCMD / 8; + if (MPI3MR_NUM_DEVRMCMD % 8) + mrioc->devrem_bitmap_sz++; + mrioc->devrem_bitmap = kzalloc(mrioc->devrem_bitmap_sz, + GFP_KERNEL); + if (!mrioc->devrem_bitmap) + goto out_failed; + + mrioc->num_reply_bufs = mrioc->facts.max_reqs + MPI3MR_NUM_EVT_REPLIES; + mrioc->reply_free_qsz = mrioc->num_reply_bufs + 1; + mrioc->num_sense_bufs = mrioc->facts.max_reqs / MPI3MR_SENSEBUF_FACTOR; + mrioc->sense_buf_q_sz = mrioc->num_sense_bufs + 1; + + /* reply buffer pool, 16 byte align */ + sz = mrioc->num_reply_bufs * mrioc->facts.reply_sz; + mrioc->reply_buf_pool = dma_pool_create("reply_buf pool", + &mrioc->pdev->dev, sz, 16, 0); + if (!mrioc->reply_buf_pool) { + ioc_err(mrioc, "reply buf pool: dma_pool_create failed\n"); + goto out_failed; + } + + mrioc->reply_buf = dma_pool_zalloc(mrioc->reply_buf_pool, GFP_KERNEL, + &mrioc->reply_buf_dma); + if (!mrioc->reply_buf) + goto out_failed; + + mrioc->reply_buf_dma_max_address = mrioc->reply_buf_dma + sz; + + /* reply free queue, 8 byte align */ + sz = mrioc->reply_free_qsz * 8; + mrioc->reply_free_q_pool = dma_pool_create("reply_free_q pool", + &mrioc->pdev->dev, sz, 8, 0); + if (!mrioc->reply_free_q_pool) { + ioc_err(mrioc, "reply_free_q pool: dma_pool_create failed\n"); + goto out_failed; + } + mrioc->reply_free_q = dma_pool_zalloc(mrioc->reply_free_q_pool, + GFP_KERNEL, &mrioc->reply_free_q_dma); + if (!mrioc->reply_free_q) + goto out_failed; + + /* sense buffer pool, 4 byte align */ + sz = mrioc->num_sense_bufs * MPI3MR_SENSEBUF_SZ; + mrioc->sense_buf_pool = dma_pool_create("sense_buf pool", + &mrioc->pdev->dev, sz, 4, 0); + if (!mrioc->sense_buf_pool) { + ioc_err(mrioc, "sense_buf pool: dma_pool_create failed\n"); + goto out_failed; + } + mrioc->sense_buf = dma_pool_zalloc(mrioc->sense_buf_pool, GFP_KERNEL, + &mrioc->sense_buf_dma); + if (!mrioc->sense_buf) + goto out_failed; + + /* sense buffer queue, 8 byte align */ + sz = mrioc->sense_buf_q_sz * 8; + mrioc->sense_buf_q_pool = dma_pool_create("sense_buf_q pool", + &mrioc->pdev->dev, sz, 8, 0); + if (!mrioc->sense_buf_q_pool) { + ioc_err(mrioc, "sense_buf_q pool: dma_pool_create failed\n"); + goto out_failed; + } + mrioc->sense_buf_q = dma_pool_zalloc(mrioc->sense_buf_q_pool, + GFP_KERNEL, &mrioc->sense_buf_q_dma); + if (!mrioc->sense_buf_q) + goto out_failed; + +post_reply_sbuf: + sz = mrioc->num_reply_bufs * mrioc->facts.reply_sz; + ioc_info(mrioc, + "reply buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n", + mrioc->reply_buf, mrioc->num_reply_bufs, mrioc->facts.reply_sz, + (sz / 1024), (unsigned long long)mrioc->reply_buf_dma); + sz = mrioc->reply_free_qsz * 8; + ioc_info(mrioc, + "reply_free_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n", + mrioc->reply_free_q, mrioc->reply_free_qsz, 8, (sz / 1024), + (unsigned long long)mrioc->reply_free_q_dma); + sz = mrioc->num_sense_bufs * MPI3MR_SENSEBUF_SZ; + ioc_info(mrioc, + "sense_buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n", + mrioc->sense_buf, mrioc->num_sense_bufs, MPI3MR_SENSEBUF_SZ, + (sz / 1024), (unsigned long long)mrioc->sense_buf_dma); + sz = mrioc->sense_buf_q_sz * 8; + ioc_info(mrioc, + "sense_buf_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n", + mrioc->sense_buf_q, mrioc->sense_buf_q_sz, 8, (sz / 1024), + (unsigned long long)mrioc->sense_buf_q_dma); + + /* initialize Reply buffer Queue */ + for (i = 0, phy_addr = mrioc->reply_buf_dma; + i < mrioc->num_reply_bufs; i++, phy_addr += mrioc->facts.reply_sz) + mrioc->reply_free_q[i] = cpu_to_le64(phy_addr); + mrioc->reply_free_q[i] = cpu_to_le64(0); + + /* initialize Sense Buffer Queue */ + for (i = 0, phy_addr = mrioc->sense_buf_dma; + i < mrioc->num_sense_bufs; i++, phy_addr += MPI3MR_SENSEBUF_SZ) + mrioc->sense_buf_q[i] = cpu_to_le64(phy_addr); + mrioc->sense_buf_q[i] = cpu_to_le64(0); + return retval; + +out_failed: + retval = -1; + return retval; +} + +/** + * mpi3mr_issue_iocinit - Send IOC Init + * @mrioc: Adapter instance reference + * + * Issue IOC Init MPI request through admin queue and wait for + * the completion of it or time out. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_issue_iocinit(struct mpi3mr_ioc *mrioc) +{ + struct mpi3_ioc_init_request iocinit_req; + struct mpi3_driver_info_layout *drv_info; + dma_addr_t data_dma; + u32 data_len = sizeof(*drv_info); + int retval = 0; + ktime_t current_time; + + drv_info = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma, + GFP_KERNEL); + if (!drv_info) { + retval = -1; + goto out; + } + drv_info->information_length = cpu_to_le32(data_len); + strncpy(drv_info->driver_signature, "Broadcom", sizeof(drv_info->driver_signature)); + strncpy(drv_info->os_name, utsname()->sysname, sizeof(drv_info->os_name)); + drv_info->os_name[sizeof(drv_info->os_name) - 1] = 0; + strncpy(drv_info->os_version, utsname()->release, sizeof(drv_info->os_version)); + drv_info->os_version[sizeof(drv_info->os_version) - 1] = 0; + strncpy(drv_info->driver_name, MPI3MR_DRIVER_NAME, sizeof(drv_info->driver_name)); + strncpy(drv_info->driver_version, MPI3MR_DRIVER_VERSION, sizeof(drv_info->driver_version)); + strncpy(drv_info->driver_release_date, MPI3MR_DRIVER_RELDATE, sizeof(drv_info->driver_release_date)); + drv_info->driver_capabilities = 0; + memcpy((u8 *)&mrioc->driver_info, (u8 *)drv_info, + sizeof(mrioc->driver_info)); + + memset(&iocinit_req, 0, sizeof(iocinit_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue IOCInit: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + iocinit_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + iocinit_req.function = MPI3_FUNCTION_IOC_INIT; + iocinit_req.mpi_version.mpi3_version.dev = MPI3_VERSION_DEV; + iocinit_req.mpi_version.mpi3_version.unit = MPI3_VERSION_UNIT; + iocinit_req.mpi_version.mpi3_version.major = MPI3_VERSION_MAJOR; + iocinit_req.mpi_version.mpi3_version.minor = MPI3_VERSION_MINOR; + iocinit_req.who_init = MPI3_WHOINIT_HOST_DRIVER; + iocinit_req.reply_free_queue_depth = cpu_to_le16(mrioc->reply_free_qsz); + iocinit_req.reply_free_queue_address = + cpu_to_le64(mrioc->reply_free_q_dma); + iocinit_req.sense_buffer_length = cpu_to_le16(MPI3MR_SENSEBUF_SZ); + iocinit_req.sense_buffer_free_queue_depth = + cpu_to_le16(mrioc->sense_buf_q_sz); + iocinit_req.sense_buffer_free_queue_address = + cpu_to_le64(mrioc->sense_buf_q_dma); + iocinit_req.driver_information_address = cpu_to_le64(data_dma); + + current_time = ktime_get_real(); + iocinit_req.time_stamp = cpu_to_le64(ktime_to_ms(current_time)); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &iocinit_req, + sizeof(iocinit_req), 1); + if (retval) { + ioc_err(mrioc, "Issue IOCInit: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_IOCINIT_TIMEOUT); + mrioc->unrecoverable = 1; + ioc_err(mrioc, "Issue IOCInit: command timed out\n"); + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Issue IOCInit: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); + +out: + if (drv_info) + dma_free_coherent(&mrioc->pdev->dev, data_len, drv_info, + data_dma); + + return retval; +} + +/** + * mpi3mr_unmask_events - Unmask events in event mask bitmap + * @mrioc: Adapter instance reference + * @event: MPI event ID + * + * Un mask the specific event by resetting the event_mask + * bitmap. + * + * Return: 0 on success, non-zero on failures. + */ +static void mpi3mr_unmask_events(struct mpi3mr_ioc *mrioc, u16 event) +{ + u32 desired_event; + u8 word; + + if (event >= 128) + return; + + desired_event = (1 << (event % 32)); + word = event / 32; + + mrioc->event_masks[word] &= ~desired_event; +} + +/** + * mpi3mr_issue_event_notification - Send event notification + * @mrioc: Adapter instance reference + * + * Issue event notification MPI request through admin queue and + * wait for the completion of it or time out. + * + * Return: 0 on success, non-zero on failures. + */ +static int mpi3mr_issue_event_notification(struct mpi3mr_ioc *mrioc) +{ + struct mpi3_event_notification_request evtnotify_req; + int retval = 0; + u8 i; + + memset(&evtnotify_req, 0, sizeof(evtnotify_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue EvtNotify: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + evtnotify_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + evtnotify_req.function = MPI3_FUNCTION_EVENT_NOTIFICATION; + for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++) + evtnotify_req.event_masks[i] = + cpu_to_le32(mrioc->event_masks[i]); + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &evtnotify_req, + sizeof(evtnotify_req), 1); + if (retval) { + ioc_err(mrioc, "Issue EvtNotify: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue EvtNotify: command timed out\n"); + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT); + mrioc->unrecoverable = 1; + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Issue EvtNotify: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); +out: + return retval; +} + +/** + * mpi3mr_send_event_ack - Send event acknowledgment + * @mrioc: Adapter instance reference + * @event: MPI3 event ID + * @event_ctx: Event context + * + * Send event acknowledgment through admin queue and wait for + * it to complete. + * + * Return: 0 on success, non-zero on failures. + */ +int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, + u32 event_ctx) +{ + struct mpi3_event_ack_request evtack_req; + int retval = 0; + + memset(&evtack_req, 0, sizeof(evtack_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Send EvtAck: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + evtack_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + evtack_req.function = MPI3_FUNCTION_EVENT_ACK; + evtack_req.event = event; + evtack_req.event_context = cpu_to_le32(event_ctx); + + init_completion(&mrioc->init_cmds.done); + retval = mpi3mr_admin_request_post(mrioc, &evtack_req, + sizeof(evtack_req), 1); + if (retval) { + ioc_err(mrioc, "Send EvtAck: Admin Post failed\n"); + goto out_unlock; + } + wait_for_completion_timeout(&mrioc->init_cmds.done, + (MPI3MR_INTADMCMD_TIMEOUT * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue EvtNotify: command timed out\n"); + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_EVTACK_TIMEOUT, 1); + retval = -1; + goto out_unlock; + } + if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK) + != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "Send EvtAck: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK), + mrioc->init_cmds.ioc_loginfo); + retval = -1; + goto out_unlock; + } + +out_unlock: + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&mrioc->init_cmds.mutex); +out: + return retval; +} + +/** + * mpi3mr_alloc_chain_bufs - Allocate chain buffers + * @mrioc: Adapter instance reference + * + * Allocate chain buffers and set a bitmap to indicate free + * chain buffers. Chain buffers are used to pass the SGE + * information along with MPI3 SCSI IO requests for host I/O. + * + * Return: 0 on success, non-zero on failure + */ +static int mpi3mr_alloc_chain_bufs(struct mpi3mr_ioc *mrioc) +{ + int retval = 0; + u32 sz, i; + u16 num_chains; + + num_chains = mrioc->max_host_ios / MPI3MR_CHAINBUF_FACTOR; + + if (prot_mask & (SHOST_DIX_TYPE0_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE2_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION)) + num_chains += (num_chains / MPI3MR_CHAINBUFDIX_FACTOR); + + mrioc->chain_buf_count = num_chains; + sz = sizeof(struct chain_element) * num_chains; + mrioc->chain_sgl_list = kzalloc(sz, GFP_KERNEL); + if (!mrioc->chain_sgl_list) + goto out_failed; + + sz = MPI3MR_PAGE_SIZE_4K; + mrioc->chain_buf_pool = dma_pool_create("chain_buf pool", + &mrioc->pdev->dev, sz, 16, 0); + if (!mrioc->chain_buf_pool) { + ioc_err(mrioc, "chain buf pool: dma_pool_create failed\n"); + goto out_failed; + } + + for (i = 0; i < num_chains; i++) { + mrioc->chain_sgl_list[i].addr = + dma_pool_zalloc(mrioc->chain_buf_pool, GFP_KERNEL, + &mrioc->chain_sgl_list[i].dma_addr); + + if (!mrioc->chain_sgl_list[i].addr) + goto out_failed; + } + mrioc->chain_bitmap_sz = num_chains / 8; + if (num_chains % 8) + mrioc->chain_bitmap_sz++; + mrioc->chain_bitmap = kzalloc(mrioc->chain_bitmap_sz, GFP_KERNEL); + if (!mrioc->chain_bitmap) + goto out_failed; + return retval; +out_failed: + retval = -1; + return retval; +} + +/** + * mpi3mr_port_enable_complete - Mark port enable complete + * @mrioc: Adapter instance reference + * @drv_cmd: Internal command tracker + * + * Call back for asynchronous port enable request sets the + * driver command to indicate port enable request is complete. + * + * Return: Nothing + */ +static void mpi3mr_port_enable_complete(struct mpi3mr_ioc *mrioc, + struct mpi3mr_drv_cmd *drv_cmd) +{ + drv_cmd->state = MPI3MR_CMD_NOTUSED; + drv_cmd->callback = NULL; + mrioc->scan_failed = drv_cmd->ioc_status; + mrioc->scan_started = 0; +} + +/** + * mpi3mr_issue_port_enable - Issue Port Enable + * @mrioc: Adapter instance reference + * @async: Flag to wait for completion or not + * + * Issue Port Enable MPI request through admin queue and if the + * async flag is not set wait for the completion of the port + * enable or time out. + * + * Return: 0 on success, non-zero on failures. + */ +int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async) +{ + struct mpi3_port_enable_request pe_req; + int retval = 0; + u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT; + + memset(&pe_req, 0, sizeof(pe_req)); + mutex_lock(&mrioc->init_cmds.mutex); + if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "Issue PortEnable: Init command is in use\n"); + mutex_unlock(&mrioc->init_cmds.mutex); + goto out; + } + mrioc->init_cmds.state = MPI3MR_CMD_PENDING; + if (async) { + mrioc->init_cmds.is_waiting = 0; + mrioc->init_cmds.callback = mpi3mr_port_enable_complete; + } else { + mrioc->init_cmds.is_waiting = 1; + mrioc->init_cmds.callback = NULL; + init_completion(&mrioc->init_cmds.done); + } + pe_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); + pe_req.function = MPI3_FUNCTION_PORT_ENABLE; + + retval = mpi3mr_admin_request_post(mrioc, &pe_req, sizeof(pe_req), 1); + if (retval) { + ioc_err(mrioc, "Issue PortEnable: Admin Post failed\n"); + goto out_unlock; + } + if (!async) { + wait_for_completion_timeout(&mrioc->init_cmds.done, + (pe_timeout * HZ)); + if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "Issue PortEnable: command timed out\n"); + retval = -1; + mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR; + mpi3mr_set_diagsave(mrioc); + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, + MPI3MR_RESET_FROM_PE_TIMEOUT); + mrioc->unrecoverable = 1; + goto out_unlock; + } + mpi3mr_port_enable_complete(mrioc, &mrioc->init_cmds); + } +out_unlock: + mutex_unlock(&mrioc->init_cmds.mutex); +out: + return retval; +} + +/* Protocol type to name mapper structure*/ +static const struct { + u8 protocol; + char *name; +} mpi3mr_protocols[] = { + { MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR, "Initiator" }, + { MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET, "Target" }, + { MPI3_IOCFACTS_PROTOCOL_NVME, "NVMe attachment" }, +}; + +/* Capability to name mapper structure*/ +static const struct { + u32 capability; + char *name; +} mpi3mr_capabilities[] = { + { MPI3_IOCFACTS_CAPABILITY_RAID_CAPABLE, "RAID" }, +}; + +/** + * mpi3mr_print_ioc_info - Display controller information + * @mrioc: Adapter instance reference + * + * Display controller personalit, capability, supported + * protocols etc. + * + * Return: Nothing + */ +static void +mpi3mr_print_ioc_info(struct mpi3mr_ioc *mrioc) +{ + int i = 0, bytes_wrote = 0; + char personality[16]; + char protocol[50] = {0}; + char capabilities[100] = {0}; + bool is_string_nonempty = false; + struct mpi3mr_compimg_ver *fwver = &mrioc->facts.fw_ver; + + switch (mrioc->facts.personality) { + case MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA: + strncpy(personality, "Enhanced HBA", sizeof(personality)); + break; + case MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR: + strncpy(personality, "RAID", sizeof(personality)); + break; + default: + strncpy(personality, "Unknown", sizeof(personality)); + break; + } + + ioc_info(mrioc, "Running in %s Personality", personality); + + ioc_info(mrioc, "FW version(%d.%d.%d.%d.%d.%d)\n", + fwver->gen_major, fwver->gen_minor, fwver->ph_major, + fwver->ph_minor, fwver->cust_id, fwver->build_num); + + for (i = 0; i < ARRAY_SIZE(mpi3mr_protocols); i++) { + if (mrioc->facts.protocol_flags & + mpi3mr_protocols[i].protocol) { + if (is_string_nonempty && + (bytes_wrote < sizeof(protocol))) + bytes_wrote += snprintf(protocol + bytes_wrote, + (sizeof(protocol) - bytes_wrote), ","); + + if (bytes_wrote < sizeof(protocol)) + bytes_wrote += snprintf(protocol + bytes_wrote, + (sizeof(protocol) - bytes_wrote), "%s", + mpi3mr_protocols[i].name); + is_string_nonempty = true; + } + } + + bytes_wrote = 0; + is_string_nonempty = false; + for (i = 0; i < ARRAY_SIZE(mpi3mr_capabilities); i++) { + if (mrioc->facts.protocol_flags & + mpi3mr_capabilities[i].capability) { + if (is_string_nonempty && + (bytes_wrote < sizeof(capabilities))) + bytes_wrote += snprintf(capabilities + bytes_wrote, + (sizeof(capabilities) - bytes_wrote), ","); + + if (bytes_wrote < sizeof(capabilities)) + bytes_wrote += snprintf(capabilities + bytes_wrote, + (sizeof(capabilities) - bytes_wrote), "%s", + mpi3mr_capabilities[i].name); + is_string_nonempty = true; + } + } + + ioc_info(mrioc, "Protocol=(%s), Capabilities=(%s)\n", + protocol, capabilities); +} + +/** + * mpi3mr_cleanup_resources - Free PCI resources + * @mrioc: Adapter instance reference + * + * Unmap PCI device memory and disable PCI device. + * + * Return: 0 on success and non-zero on failure. + */ +void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc) +{ + struct pci_dev *pdev = mrioc->pdev; + + mpi3mr_cleanup_isr(mrioc); + + if (mrioc->sysif_regs) { + iounmap((void __iomem *)mrioc->sysif_regs); + mrioc->sysif_regs = NULL; + } + + if (pci_is_enabled(pdev)) { + if (mrioc->bars) + pci_release_selected_regions(pdev, mrioc->bars); + pci_disable_device(pdev); + } +} + +/** + * mpi3mr_setup_resources - Enable PCI resources + * @mrioc: Adapter instance reference + * + * Enable PCI device memory, MSI-x registers and set DMA mask. + * + * Return: 0 on success and non-zero on failure. + */ +int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc) +{ + struct pci_dev *pdev = mrioc->pdev; + u32 memap_sz = 0; + int i, retval = 0, capb = 0; + u16 message_control; + u64 dma_mask = mrioc->dma_mask ? mrioc->dma_mask : + (((dma_get_required_mask(&pdev->dev) > DMA_BIT_MASK(32)) && + (sizeof(dma_addr_t) > 4)) ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); + + if (pci_enable_device_mem(pdev)) { + ioc_err(mrioc, "pci_enable_device_mem: failed\n"); + retval = -ENODEV; + goto out_failed; + } + + capb = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (!capb) { + ioc_err(mrioc, "Unable to find MSI-X Capabilities\n"); + retval = -ENODEV; + goto out_failed; + } + mrioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); + + if (pci_request_selected_regions(pdev, mrioc->bars, + mrioc->driver_name)) { + ioc_err(mrioc, "pci_request_selected_regions: failed\n"); + retval = -ENODEV; + goto out_failed; + } + + for (i = 0; (i < DEVICE_COUNT_RESOURCE); i++) { + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + mrioc->sysif_regs_phys = pci_resource_start(pdev, i); + memap_sz = pci_resource_len(pdev, i); + mrioc->sysif_regs = + ioremap(mrioc->sysif_regs_phys, memap_sz); + break; + } + } + + pci_set_master(pdev); + + retval = dma_set_mask_and_coherent(&pdev->dev, dma_mask); + if (retval) { + if (dma_mask != DMA_BIT_MASK(32)) { + ioc_warn(mrioc, "Setting 64 bit DMA mask failed\n"); + dma_mask = DMA_BIT_MASK(32); + retval = dma_set_mask_and_coherent(&pdev->dev, + dma_mask); + } + if (retval) { + mrioc->dma_mask = 0; + ioc_err(mrioc, "Setting 32 bit DMA mask also failed\n"); + goto out_failed; + } + } + mrioc->dma_mask = dma_mask; + + if (!mrioc->sysif_regs) { + ioc_err(mrioc, + "Unable to map adapter memory or resource not found\n"); + retval = -EINVAL; + goto out_failed; + } + + pci_read_config_word(pdev, capb + 2, &message_control); + mrioc->msix_count = (message_control & 0x3FF) + 1; + + pci_save_state(pdev); + + pci_set_drvdata(pdev, mrioc->shost); + + mpi3mr_ioc_disable_intr(mrioc); + + ioc_info(mrioc, "iomem(0x%016llx), mapped(0x%p), size(%d)\n", + (unsigned long long)mrioc->sysif_regs_phys, + mrioc->sysif_regs, memap_sz); + ioc_info(mrioc, "Number of MSI-X vectors found in capabilities: (%d)\n", + mrioc->msix_count); + return retval; + +out_failed: + mpi3mr_cleanup_resources(mrioc); + return retval; +} + +/** + * mpi3mr_init_ioc - Initialize the controller + * @mrioc: Adapter instance reference + * @re_init: Flag to indicate is this fresh init or re-init + * + * This the controller initialization routine, executed either + * after soft reset or from pci probe callback. + * Setup the required resources, memory map the controller + * registers, create admin and operational reply queue pairs, + * allocate required memory for reply pool, sense buffer pool, + * issue IOC init request to the firmware, unmask the events and + * issue port enable to discover SAS/SATA/NVMe devies and RAID + * volumes. + * + * Return: 0 on success and non-zero on failure. + */ +int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc, u8 re_init) +{ + int retval = 0; + enum mpi3mr_iocstate ioc_state; + u64 base_info; + u32 timeout; + u32 ioc_status, ioc_config, i; + struct mpi3_ioc_facts_data facts_data; + + mrioc->irqpoll_sleep = MPI3MR_IRQ_POLL_SLEEP; + mrioc->change_count = 0; + if (!re_init) { + mrioc->cpu_count = num_online_cpus(); + retval = mpi3mr_setup_resources(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to setup resources:error %d\n", + retval); + goto out_nocleanup; + } + } + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + + ioc_info(mrioc, "SOD status %x configuration %x\n", + ioc_status, ioc_config); + + base_info = lo_hi_readq(&mrioc->sysif_regs->ioc_information); + ioc_info(mrioc, "SOD base_info %llx\n", base_info); + + /*The timeout value is in 2sec unit, changing it to seconds*/ + mrioc->ready_timeout = + ((base_info & MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK) >> + MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT) * 2; + + ioc_info(mrioc, "IOC ready timeout %d\n", mrioc->ready_timeout); + + ioc_state = mpi3mr_get_iocstate(mrioc); + ioc_info(mrioc, "IOC in %s state during detection\n", + mpi3mr_iocstate_name(ioc_state)); + + if (ioc_state == MRIOC_STATE_BECOMING_READY || + ioc_state == MRIOC_STATE_RESET_REQUESTED) { + timeout = mrioc->ready_timeout * 10; + do { + msleep(100); + } while (--timeout); + + ioc_state = mpi3mr_get_iocstate(mrioc); + ioc_info(mrioc, + "IOC in %s state after waiting for reset time\n", + mpi3mr_iocstate_name(ioc_state)); + } + + if (ioc_state == MRIOC_STATE_READY) { + retval = mpi3mr_issue_and_process_mur(mrioc, + MPI3MR_RESET_FROM_BRINGUP); + if (retval) { + ioc_err(mrioc, "Failed to MU reset IOC error %d\n", + retval); + } + ioc_state = mpi3mr_get_iocstate(mrioc); + } + if (ioc_state != MRIOC_STATE_RESET) { + mpi3mr_print_fault_info(mrioc); + retval = mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, + MPI3MR_RESET_FROM_BRINGUP); + if (retval) { + ioc_err(mrioc, + "%s :Failed to soft reset IOC error %d\n", + __func__, retval); + goto out_failed; + } + } + ioc_state = mpi3mr_get_iocstate(mrioc); + if (ioc_state != MRIOC_STATE_RESET) { + ioc_err(mrioc, "Cannot bring IOC to reset state\n"); + goto out_failed; + } + + retval = mpi3mr_setup_admin_qpair(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to setup admin Qs: error %d\n", + retval); + goto out_failed; + } + + retval = mpi3mr_bring_ioc_ready(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to bring ioc ready: error %d\n", + retval); + goto out_failed; + } + + if (!re_init) { + retval = mpi3mr_setup_isr(mrioc, 1); + if (retval) { + ioc_err(mrioc, "Failed to setup ISR error %d\n", + retval); + goto out_failed; + } + } else + mpi3mr_ioc_enable_intr(mrioc); + + retval = mpi3mr_issue_iocfacts(mrioc, &facts_data); + if (retval) { + ioc_err(mrioc, "Failed to Issue IOC Facts %d\n", + retval); + goto out_failed; + } + + mpi3mr_process_factsdata(mrioc, &facts_data); + if (!re_init) { + retval = mpi3mr_check_reset_dma_mask(mrioc); + if (retval) { + ioc_err(mrioc, "Resetting dma mask failed %d\n", + retval); + goto out_failed; + } + } + + mpi3mr_print_ioc_info(mrioc); + + retval = mpi3mr_alloc_reply_sense_bufs(mrioc); + if (retval) { + ioc_err(mrioc, + "%s :Failed to allocated reply sense buffers %d\n", + __func__, retval); + goto out_failed; + } + + if (!re_init) { + retval = mpi3mr_alloc_chain_bufs(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to allocated chain buffers %d\n", + retval); + goto out_failed; + } + } + + retval = mpi3mr_issue_iocinit(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to Issue IOC Init %d\n", + retval); + goto out_failed; + } + mrioc->reply_free_queue_host_index = mrioc->num_reply_bufs; + writel(mrioc->reply_free_queue_host_index, + &mrioc->sysif_regs->reply_free_host_index); + + mrioc->sbq_host_index = mrioc->num_sense_bufs; + writel(mrioc->sbq_host_index, + &mrioc->sysif_regs->sense_buffer_free_host_index); + + if (!re_init) { + retval = mpi3mr_setup_isr(mrioc, 0); + if (retval) { + ioc_err(mrioc, "Failed to re-setup ISR, error %d\n", + retval); + goto out_failed; + } + } + + retval = mpi3mr_create_op_queues(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to create OpQueues error %d\n", + retval); + goto out_failed; + } + + if (re_init && + (mrioc->shost->nr_hw_queues > mrioc->num_op_reply_q)) { + ioc_err(mrioc, + "Cannot create minimum number of OpQueues expected:%d created:%d\n", + mrioc->shost->nr_hw_queues, mrioc->num_op_reply_q); + goto out_failed; + } + + for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++) + mrioc->event_masks[i] = -1; + + mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_ADDED); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_INFO_CHANGED); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_STATUS_CHANGE); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DISCOVERY); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_BROADCAST_PRIMITIVE); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_ENUMERATION); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_CABLE_MGMT); + mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENERGY_PACK_CHANGE); + + retval = mpi3mr_issue_event_notification(mrioc); + if (retval) { + ioc_err(mrioc, "Failed to issue event notification %d\n", + retval); + goto out_failed; + } + + if (re_init) { + ioc_info(mrioc, "Issuing Port Enable\n"); + retval = mpi3mr_issue_port_enable(mrioc, 0); + if (retval) { + ioc_err(mrioc, "Failed to issue port enable %d\n", + retval); + goto out_failed; + } + } + return retval; + +out_failed: + mpi3mr_cleanup_ioc(mrioc, re_init); +out_nocleanup: + return retval; +} + +/** + * mpi3mr_memset_op_reply_q_buffers - memset the operational reply queue's + * segments + * @mrioc: Adapter instance reference + * @qidx: Operational reply queue index + * + * Return: Nothing. + */ +static void mpi3mr_memset_op_reply_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx; + struct segments *segments; + int i, size; + + if (!op_reply_q->q_segments) + return; + + size = op_reply_q->segment_qd * mrioc->op_reply_desc_sz; + segments = op_reply_q->q_segments; + for (i = 0; i < op_reply_q->num_segments; i++) + memset(segments[i].segment, 0, size); +} + +/** + * mpi3mr_memset_op_req_q_buffers - memset the operational request queue's + * segments + * @mrioc: Adapter instance reference + * @qidx: Operational request queue index + * + * Return: Nothing. + */ +static void mpi3mr_memset_op_req_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx) +{ + struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx; + struct segments *segments; + int i, size; + + if (!op_req_q->q_segments) + return; + + size = op_req_q->segment_qd * mrioc->facts.op_req_sz; + segments = op_req_q->q_segments; + for (i = 0; i < op_req_q->num_segments; i++) + memset(segments[i].segment, 0, size); +} + +/** + * mpi3mr_memset_buffers - memset memory for a controller + * @mrioc: Adapter instance reference + * + * clear all the memory allocated for a controller, typically + * called post reset to reuse the memory allocated during the + * controller init. + * + * Return: Nothing. + */ +static void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) +{ + u16 i; + + memset(mrioc->admin_req_base, 0, mrioc->admin_req_q_sz); + memset(mrioc->admin_reply_base, 0, mrioc->admin_reply_q_sz); + + memset(mrioc->init_cmds.reply, 0, sizeof(*mrioc->init_cmds.reply)); + memset(mrioc->host_tm_cmds.reply, 0, + sizeof(*mrioc->host_tm_cmds.reply)); + for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) + memset(mrioc->dev_rmhs_cmds[i].reply, 0, + sizeof(*mrioc->dev_rmhs_cmds[i].reply)); + memset(mrioc->removepend_bitmap, 0, mrioc->dev_handle_bitmap_sz); + memset(mrioc->devrem_bitmap, 0, mrioc->devrem_bitmap_sz); + + for (i = 0; i < mrioc->num_queues; i++) { + mrioc->op_reply_qinfo[i].qid = 0; + mrioc->op_reply_qinfo[i].ci = 0; + mrioc->op_reply_qinfo[i].num_replies = 0; + mrioc->op_reply_qinfo[i].ephase = 0; + atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0); + atomic_set(&mrioc->op_reply_qinfo[i].in_use, 0); + mpi3mr_memset_op_reply_q_buffers(mrioc, i); + + mrioc->req_qinfo[i].ci = 0; + mrioc->req_qinfo[i].pi = 0; + mrioc->req_qinfo[i].num_requests = 0; + mrioc->req_qinfo[i].qid = 0; + mrioc->req_qinfo[i].reply_qid = 0; + spin_lock_init(&mrioc->req_qinfo[i].q_lock); + mpi3mr_memset_op_req_q_buffers(mrioc, i); + } +} + +/** + * mpi3mr_free_mem - Free memory allocated for a controller + * @mrioc: Adapter instance reference + * + * Free all the memory allocated for a controller. + * + * Return: Nothing. + */ +static void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc) +{ + u16 i; + struct mpi3mr_intr_info *intr_info; + + if (mrioc->sense_buf_pool) { + if (mrioc->sense_buf) + dma_pool_free(mrioc->sense_buf_pool, mrioc->sense_buf, + mrioc->sense_buf_dma); + dma_pool_destroy(mrioc->sense_buf_pool); + mrioc->sense_buf = NULL; + mrioc->sense_buf_pool = NULL; + } + if (mrioc->sense_buf_q_pool) { + if (mrioc->sense_buf_q) + dma_pool_free(mrioc->sense_buf_q_pool, + mrioc->sense_buf_q, mrioc->sense_buf_q_dma); + dma_pool_destroy(mrioc->sense_buf_q_pool); + mrioc->sense_buf_q = NULL; + mrioc->sense_buf_q_pool = NULL; + } + + if (mrioc->reply_buf_pool) { + if (mrioc->reply_buf) + dma_pool_free(mrioc->reply_buf_pool, mrioc->reply_buf, + mrioc->reply_buf_dma); + dma_pool_destroy(mrioc->reply_buf_pool); + mrioc->reply_buf = NULL; + mrioc->reply_buf_pool = NULL; + } + if (mrioc->reply_free_q_pool) { + if (mrioc->reply_free_q) + dma_pool_free(mrioc->reply_free_q_pool, + mrioc->reply_free_q, mrioc->reply_free_q_dma); + dma_pool_destroy(mrioc->reply_free_q_pool); + mrioc->reply_free_q = NULL; + mrioc->reply_free_q_pool = NULL; + } + + for (i = 0; i < mrioc->num_op_req_q; i++) + mpi3mr_free_op_req_q_segments(mrioc, i); + + for (i = 0; i < mrioc->num_op_reply_q; i++) + mpi3mr_free_op_reply_q_segments(mrioc, i); + + for (i = 0; i < mrioc->intr_info_count; i++) { + intr_info = mrioc->intr_info + i; + if (intr_info) + intr_info->op_reply_q = NULL; + } + + kfree(mrioc->req_qinfo); + mrioc->req_qinfo = NULL; + mrioc->num_op_req_q = 0; + + kfree(mrioc->op_reply_qinfo); + mrioc->op_reply_qinfo = NULL; + mrioc->num_op_reply_q = 0; + + kfree(mrioc->init_cmds.reply); + mrioc->init_cmds.reply = NULL; + + kfree(mrioc->host_tm_cmds.reply); + mrioc->host_tm_cmds.reply = NULL; + + kfree(mrioc->removepend_bitmap); + mrioc->removepend_bitmap = NULL; + + kfree(mrioc->devrem_bitmap); + mrioc->devrem_bitmap = NULL; + + kfree(mrioc->chain_bitmap); + mrioc->chain_bitmap = NULL; + + for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) { + kfree(mrioc->dev_rmhs_cmds[i].reply); + mrioc->dev_rmhs_cmds[i].reply = NULL; + } + + if (mrioc->chain_buf_pool) { + for (i = 0; i < mrioc->chain_buf_count; i++) { + if (mrioc->chain_sgl_list[i].addr) { + dma_pool_free(mrioc->chain_buf_pool, + mrioc->chain_sgl_list[i].addr, + mrioc->chain_sgl_list[i].dma_addr); + mrioc->chain_sgl_list[i].addr = NULL; + } + } + dma_pool_destroy(mrioc->chain_buf_pool); + mrioc->chain_buf_pool = NULL; + } + + kfree(mrioc->chain_sgl_list); + mrioc->chain_sgl_list = NULL; + + if (mrioc->admin_reply_base) { + dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz, + mrioc->admin_reply_base, mrioc->admin_reply_dma); + mrioc->admin_reply_base = NULL; + } + if (mrioc->admin_req_base) { + dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz, + mrioc->admin_req_base, mrioc->admin_req_dma); + mrioc->admin_req_base = NULL; + } +} + +/** + * mpi3mr_issue_ioc_shutdown - shutdown controller + * @mrioc: Adapter instance reference + * + * Send shutodwn notification to the controller and wait for the + * shutdown_timeout for it to be completed. + * + * Return: Nothing. + */ +static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc) +{ + u32 ioc_config, ioc_status; + u8 retval = 1; + u32 timeout = MPI3MR_DEFAULT_SHUTDOWN_TIME * 10; + + ioc_info(mrioc, "Issuing shutdown Notification\n"); + if (mrioc->unrecoverable) { + ioc_warn(mrioc, + "IOC is unrecoverable shutdown is not issued\n"); + return; + } + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK) + == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS) { + ioc_info(mrioc, "shutdown already in progress\n"); + return; + } + + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + ioc_config |= MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL; + ioc_config |= MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN; + + writel(ioc_config, &mrioc->sysif_regs->ioc_configuration); + + if (mrioc->facts.shutdown_timeout) + timeout = mrioc->facts.shutdown_timeout * 10; + + do { + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK) + == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE) { + retval = 0; + break; + } + msleep(100); + } while (--timeout); + + ioc_status = readl(&mrioc->sysif_regs->ioc_status); + ioc_config = readl(&mrioc->sysif_regs->ioc_configuration); + + if (retval) { + if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK) + == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS) + ioc_warn(mrioc, + "shutdown still in progress after timeout\n"); + } + + ioc_info(mrioc, + "Base IOC Sts/Config after %s shutdown is (0x%x)/(0x%x)\n", + (!retval) ? "successful" : "failed", ioc_status, + ioc_config); +} + +/** + * mpi3mr_cleanup_ioc - Cleanup controller + * @mrioc: Adapter instance reference + * @re_init: Cleanup due to a reinit or not + * + * controller cleanup handler, Message unit reset or soft reset + * and shutdown notification is issued to the controller and the + * associated memory resources are freed. + * + * Return: Nothing. + */ +void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc, u8 re_init) +{ + enum mpi3mr_iocstate ioc_state; + + if (!re_init) + mpi3mr_stop_watchdog(mrioc); + + mpi3mr_ioc_disable_intr(mrioc); + + ioc_state = mpi3mr_get_iocstate(mrioc); + + if ((!mrioc->unrecoverable) && (!mrioc->reset_in_progress) && + (ioc_state == MRIOC_STATE_READY)) { + if (mpi3mr_issue_and_process_mur(mrioc, + MPI3MR_RESET_FROM_CTLR_CLEANUP)) + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, + MPI3MR_RESET_FROM_MUR_FAILURE); + + if (!re_init) + mpi3mr_issue_ioc_shutdown(mrioc); + } + + if (!re_init) { + mpi3mr_free_mem(mrioc); + mpi3mr_cleanup_resources(mrioc); + } +} + +/** + * mpi3mr_drv_cmd_comp_reset - Flush a internal driver command + * @mrioc: Adapter instance reference + * @cmdptr: Internal command tracker + * + * Complete an internal driver commands with state indicating it + * is completed due to reset. + * + * Return: Nothing. + */ +static inline void mpi3mr_drv_cmd_comp_reset(struct mpi3mr_ioc *mrioc, + struct mpi3mr_drv_cmd *cmdptr) +{ + if (cmdptr->state & MPI3MR_CMD_PENDING) { + cmdptr->state |= MPI3MR_CMD_RESET; + cmdptr->state &= ~MPI3MR_CMD_PENDING; + if (cmdptr->is_waiting) { + complete(&cmdptr->done); + cmdptr->is_waiting = 0; + } else if (cmdptr->callback) + cmdptr->callback(mrioc, cmdptr); + } +} + +/** + * mpi3mr_flush_drv_cmds - Flush internaldriver commands + * @mrioc: Adapter instance reference + * + * Flush all internal driver commands post reset + * + * Return: Nothing. + */ +static void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc) +{ + struct mpi3mr_drv_cmd *cmdptr; + u8 i; + + cmdptr = &mrioc->init_cmds; + mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr); + cmdptr = &mrioc->host_tm_cmds; + mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr); + + for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) { + cmdptr = &mrioc->dev_rmhs_cmds[i]; + mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr); + } +} + +/** + * mpi3mr_diagfault_reset_handler - Diag fault reset handler + * @mrioc: Adapter instance reference + * @reset_reason: Reset reason code + * + * This is an handler for issuing diag fault reset from the + * applications through IOCTL path to stop the execution of the + * controller + * + * Return: 0 on success, non-zero on failure. + */ +int mpi3mr_diagfault_reset_handler(struct mpi3mr_ioc *mrioc, + u32 reset_reason) +{ + int retval = 0; + + ioc_info(mrioc, "Entry: reason code: %s\n", + mpi3mr_reset_rc_name(reset_reason)); + mrioc->reset_in_progress = 1; + + mpi3mr_ioc_disable_intr(mrioc); + + retval = mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason); + + if (retval) { + ioc_err(mrioc, "The diag fault reset failed: reason %d\n", + reset_reason); + mpi3mr_ioc_enable_intr(mrioc); + } + ioc_info(mrioc, "%s\n", ((retval == 0) ? "SUCCESS" : "FAILED")); + mrioc->reset_in_progress = 0; + return retval; +} + +/** + * mpi3mr_soft_reset_handler - Reset the controller + * @mrioc: Adapter instance reference + * @reset_reason: Reset reason code + * @snapdump: Flag to generate snapdump in firmware or not + * + * This is an handler for recovering controller by issuing soft + * reset are diag fault reset. This is a blocking function and + * when one reset is executed if any other resets they will be + * blocked. All IOCTLs/IO will be blocked during the reset. If + * controller reset is successful then the controller will be + * reinitalized, otherwise the controller will be marked as not + * recoverable + * + * In snapdump bit is set, the controller is issued with diag + * fault reset so that the firmware can create a snap dump and + * post that the firmware will result in F000 fault and the + * driver will issue soft reset to recover from that. + * + * Return: 0 on success, non-zero on failure. + */ +int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc, + u32 reset_reason, u8 snapdump) +{ + int retval = 0, i; + unsigned long flags; + u32 host_diagnostic, timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10; + + if (mrioc->fault_dbg) { + if (snapdump) + mpi3mr_set_diagsave(mrioc); + mpi3mr_kill_ioc(mrioc, reset_reason); + } + + /* + * Block new resets until the currently executing one is finished and + * return the status of the existing reset for all blocked resets + */ + if (!mutex_trylock(&mrioc->reset_mutex)) { + ioc_info(mrioc, "Another reset in progress\n"); + return -1; + } + mrioc->reset_in_progress = 1; + + if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) && + (reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) { + for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++) + mrioc->event_masks[i] = -1; + + retval = mpi3mr_issue_event_notification(mrioc); + + if (retval) { + ioc_err(mrioc, + "Failed to turn off events prior to reset %d\n", + retval); + } + } + + mpi3mr_wait_for_host_io(mrioc, MPI3MR_RESET_HOST_IOWAIT_TIMEOUT); + + mpi3mr_ioc_disable_intr(mrioc); + + if (snapdump) { + mpi3mr_set_diagsave(mrioc); + retval = mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason); + if (!retval) { + do { + host_diagnostic = + readl(&mrioc->sysif_regs->host_diagnostic); + if (!(host_diagnostic & + MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS)) + break; + msleep(100); + } while (--timeout); + } + } + + retval = mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, reset_reason); + if (retval) { + ioc_err(mrioc, "Failed to issue soft reset to the ioc\n"); + goto out; + } + + mpi3mr_flush_delayed_rmhs_list(mrioc); + mpi3mr_flush_drv_cmds(mrioc); + memset(mrioc->devrem_bitmap, 0, mrioc->devrem_bitmap_sz); + memset(mrioc->removepend_bitmap, 0, mrioc->dev_handle_bitmap_sz); + mpi3mr_cleanup_fwevt_list(mrioc); + mpi3mr_flush_host_io(mrioc); + mpi3mr_invalidate_devhandles(mrioc); + mpi3mr_memset_buffers(mrioc); + retval = mpi3mr_init_ioc(mrioc, 1); + if (retval) { + pr_err(IOCNAME "reinit after soft reset failed: reason %d\n", + mrioc->name, reset_reason); + goto out; + } + ssleep(10); + +out: + if (!retval) { + mrioc->reset_in_progress = 0; + scsi_unblock_requests(mrioc->shost); + mpi3mr_rfresh_tgtdevs(mrioc); + mrioc->ts_update_counter = 0; + spin_lock_irqsave(&mrioc->watchdog_lock, flags); + if (mrioc->watchdog_work_q) + queue_delayed_work(mrioc->watchdog_work_q, + &mrioc->watchdog_work, + msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL)); + spin_unlock_irqrestore(&mrioc->watchdog_lock, flags); + } else { + mpi3mr_issue_reset(mrioc, + MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason); + mrioc->unrecoverable = 1; + mrioc->reset_in_progress = 0; + retval = -1; + } + + mutex_unlock(&mrioc->reset_mutex); + ioc_info(mrioc, "%s\n", ((retval == 0) ? "SUCCESS" : "FAILED")); + return retval; +} diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c new file mode 100644 index 000000000000..a54aa009ec5a --- /dev/null +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -0,0 +1,4049 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Broadcom MPI3 Storage Controllers + * + * Copyright (C) 2017-2021 Broadcom Inc. + * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com) + * + */ + +#include "mpi3mr.h" + +/* global driver scop variables */ +LIST_HEAD(mrioc_list); +DEFINE_SPINLOCK(mrioc_list_lock); +static int mrioc_ids; +static int warn_non_secure_ctlr; + +MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR); +MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC); +MODULE_LICENSE(MPI3MR_DRIVER_LICENSE); +MODULE_VERSION(MPI3MR_DRIVER_VERSION); + +/* Module parameters*/ +int prot_mask = -1; +module_param(prot_mask, int, 0); +MODULE_PARM_DESC(prot_mask, "Host protection capabilities mask, def=0x07"); + +int prot_guard_mask = 3; +module_param(prot_guard_mask, int, 0); +MODULE_PARM_DESC(prot_guard_mask, " Host protection guard mask, def=3"); +int logging_level; +module_param(logging_level, int, 0); +MODULE_PARM_DESC(logging_level, + " bits for enabling additional logging info (default=0)"); + +/* Forward declarations*/ +/** + * mpi3mr_host_tag_for_scmd - Get host tag for a scmd + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * + * Calculate the host tag based on block tag for a given scmd. + * + * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID. + */ +static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd) +{ + struct scmd_priv *priv = NULL; + u32 unique_tag; + u16 host_tag, hw_queue; + + unique_tag = blk_mq_unique_tag(scmd->request); + + hw_queue = blk_mq_unique_tag_to_hwq(unique_tag); + if (hw_queue >= mrioc->num_op_reply_q) + return MPI3MR_HOSTTAG_INVALID; + host_tag = blk_mq_unique_tag_to_tag(unique_tag); + + if (WARN_ON(host_tag >= mrioc->max_host_ios)) + return MPI3MR_HOSTTAG_INVALID; + + priv = scsi_cmd_priv(scmd); + /*host_tag 0 is invalid hence incrementing by 1*/ + priv->host_tag = host_tag + 1; + priv->scmd = scmd; + priv->in_lld_scope = 1; + priv->req_q_idx = hw_queue; + priv->meta_chain_idx = -1; + priv->chain_idx = -1; + priv->meta_sg_valid = 0; + return priv->host_tag; +} + +/** + * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag + * @mrioc: Adapter instance reference + * @host_tag: Host tag + * @qidx: Operational queue index + * + * Identify the block tag from the host tag and queue index and + * retrieve associated scsi command using scsi_host_find_tag(). + * + * Return: SCSI command reference or NULL. + */ +static struct scsi_cmnd *mpi3mr_scmd_from_host_tag( + struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx) +{ + struct scsi_cmnd *scmd = NULL; + struct scmd_priv *priv = NULL; + u32 unique_tag = host_tag - 1; + + if (WARN_ON(host_tag > mrioc->max_host_ios)) + goto out; + + unique_tag |= (qidx << BLK_MQ_UNIQUE_TAG_BITS); + + scmd = scsi_host_find_tag(mrioc->shost, unique_tag); + if (scmd) { + priv = scsi_cmd_priv(scmd); + if (!priv->in_lld_scope) + scmd = NULL; + } +out: + return scmd; +} + +/** + * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * + * Invalidate the SCSI command private data to mark the command + * is not in LLD scope anymore. + * + * Return: Nothing. + */ +static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd) +{ + struct scmd_priv *priv = NULL; + + priv = scsi_cmd_priv(scmd); + + if (WARN_ON(priv->in_lld_scope == 0)) + return; + priv->host_tag = MPI3MR_HOSTTAG_INVALID; + priv->req_q_idx = 0xFFFF; + priv->scmd = NULL; + priv->in_lld_scope = 0; + priv->meta_sg_valid = 0; + if (priv->chain_idx >= 0) { + clear_bit(priv->chain_idx, mrioc->chain_bitmap); + priv->chain_idx = -1; + } + if (priv->meta_chain_idx >= 0) { + clear_bit(priv->meta_chain_idx, mrioc->chain_bitmap); + priv->meta_chain_idx = -1; + } +} + +static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle, + struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc); +static void mpi3mr_fwevt_worker(struct work_struct *work); + +/** + * mpi3mr_fwevt_free - firmware event memory dealloctor + * @r: k reference pointer of the firmware event + * + * Free firmware event memory when no reference. + */ +static void mpi3mr_fwevt_free(struct kref *r) +{ + kfree(container_of(r, struct mpi3mr_fwevt, ref_count)); +} + +/** + * mpi3mr_fwevt_get - k reference incrementor + * @fwevt: Firmware event reference + * + * Increment firmware event reference count. + */ +static void mpi3mr_fwevt_get(struct mpi3mr_fwevt *fwevt) +{ + kref_get(&fwevt->ref_count); +} + +/** + * mpi3mr_fwevt_put - k reference decrementor + * @fwevt: Firmware event reference + * + * decrement firmware event reference count. + */ +static void mpi3mr_fwevt_put(struct mpi3mr_fwevt *fwevt) +{ + kref_put(&fwevt->ref_count, mpi3mr_fwevt_free); +} + +/** + * mpi3mr_alloc_fwevt - Allocate firmware event + * @len: length of firmware event data to allocate + * + * Allocate firmware event with required length and initialize + * the reference counter. + * + * Return: firmware event reference. + */ +static struct mpi3mr_fwevt *mpi3mr_alloc_fwevt(int len) +{ + struct mpi3mr_fwevt *fwevt; + + fwevt = kzalloc(sizeof(*fwevt) + len, GFP_ATOMIC); + if (!fwevt) + return NULL; + + kref_init(&fwevt->ref_count); + return fwevt; +} + +/** + * mpi3mr_fwevt_add_to_list - Add firmware event to the list + * @mrioc: Adapter instance reference + * @fwevt: Firmware event reference + * + * Add the given firmware event to the firmware event list. + * + * Return: Nothing. + */ +static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + unsigned long flags; + + if (!mrioc->fwevt_worker_thread) + return; + + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + /* get fwevt reference count while adding it to fwevt_list */ + mpi3mr_fwevt_get(fwevt); + INIT_LIST_HEAD(&fwevt->list); + list_add_tail(&fwevt->list, &mrioc->fwevt_list); + INIT_WORK(&fwevt->work, mpi3mr_fwevt_worker); + /* get fwevt reference count while enqueueing it to worker queue */ + mpi3mr_fwevt_get(fwevt); + queue_work(mrioc->fwevt_worker_thread, &fwevt->work); + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); +} + +/** + * mpi3mr_fwevt_del_from_list - Delete firmware event from list + * @mrioc: Adapter instance reference + * @fwevt: Firmware event reference + * + * Delete the given firmware event from the firmware event list. + * + * Return: Nothing. + */ +static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + unsigned long flags; + + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (!list_empty(&fwevt->list)) { + list_del_init(&fwevt->list); + /* + * Put fwevt reference count after + * removing it from fwevt_list + */ + mpi3mr_fwevt_put(fwevt); + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); +} + +/** + * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list + * @mrioc: Adapter instance reference + * + * Dequeue a firmware event from the firmware event list. + * + * Return: firmware event. + */ +static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt( + struct mpi3mr_ioc *mrioc) +{ + unsigned long flags; + struct mpi3mr_fwevt *fwevt = NULL; + + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + if (!list_empty(&mrioc->fwevt_list)) { + fwevt = list_first_entry(&mrioc->fwevt_list, + struct mpi3mr_fwevt, list); + list_del_init(&fwevt->list); + /* + * Put fwevt reference count after + * removing it from fwevt_list + */ + mpi3mr_fwevt_put(fwevt); + } + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + + return fwevt; +} + +/** + * mpi3mr_cleanup_fwevt_list - Cleanup firmware event list + * @mrioc: Adapter instance reference + * + * Flush all pending firmware events from the firmware event + * list. + * + * Return: Nothing. + */ +void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) +{ + struct mpi3mr_fwevt *fwevt = NULL; + + if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) || + !mrioc->fwevt_worker_thread) + return; + + while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)) || + (fwevt = mrioc->current_event)) { + /* + * Wait on the fwevt to complete. If this returns 1, then + * the event was never executed, and we need a put for the + * reference the work had on the fwevt. + * + * If it did execute, we wait for it to finish, and the put will + * happen from mpi3mr_process_fwevt() + */ + if (cancel_work_sync(&fwevt->work)) { + /* + * Put fwevt reference count after + * dequeuing it from worker queue + */ + mpi3mr_fwevt_put(fwevt); + /* + * Put fwevt reference count to neutralize + * kref_init increment + */ + mpi3mr_fwevt_put(fwevt); + } + } +} + +/** + * mpi3mr_invalidate_devhandles -Invalidate device handles + * @mrioc: Adapter instance reference + * + * Invalidate the device handles in the target device structures + * . Called post reset prior to reinitializing the controller. + * + * Return: Nothing. + */ +void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc) +{ + struct mpi3mr_tgt_dev *tgtdev; + struct mpi3mr_stgt_priv_data *tgt_priv; + + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + tgtdev->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + } + } +} + +/** + * mpi3mr_print_scmd - print individual SCSI command + * @rq: Block request + * @data: Adapter instance reference + * @reserved: N/A. Currently not used + * + * Print the SCSI command details if it is in LLD scope. + * + * Return: true always. + */ +static bool mpi3mr_print_scmd(struct request *rq, + void *data, bool reserved) +{ + struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data; + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); + struct scmd_priv *priv = NULL; + + if (scmd) { + priv = scsi_cmd_priv(scmd); + if (!priv->in_lld_scope) + goto out; + + ioc_info(mrioc, "%s :Host Tag = %d, qid = %d\n", + __func__, priv->host_tag, priv->req_q_idx + 1); + scsi_print_command(scmd); + } + +out: + return(true); +} + +/** + * mpi3mr_flush_scmd - Flush individual SCSI command + * @rq: Block request + * @data: Adapter instance reference + * @reserved: N/A. Currently not used + * + * Return the SCSI command to the upper layers if it is in LLD + * scope. + * + * Return: true always. + */ + +static bool mpi3mr_flush_scmd(struct request *rq, + void *data, bool reserved) +{ + struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data; + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); + struct scmd_priv *priv = NULL; + + if (scmd) { + priv = scsi_cmd_priv(scmd); + if (!priv->in_lld_scope) + goto out; + + if (priv->meta_sg_valid) + dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd), + scsi_prot_sg_count(scmd), scmd->sc_data_direction); + mpi3mr_clear_scmd_priv(mrioc, scmd); + scsi_dma_unmap(scmd); + scmd->result = DID_RESET << 16; + scsi_print_command(scmd); + scmd->scsi_done(scmd); + mrioc->flush_io_count++; + } + +out: + return(true); +} + +/** + * mpi3mr_flush_host_io - Flush host I/Os + * @mrioc: Adapter instance reference + * + * Flush all of the pending I/Os by calling + * blk_mq_tagset_busy_iter() for each possible tag. This is + * executed post controller reset + * + * Return: Nothing. + */ +void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc) +{ + struct Scsi_Host *shost = mrioc->shost; + + mrioc->flush_io_count = 0; + ioc_info(mrioc, "%s :Flushing Host I/O cmds post reset\n", __func__); + blk_mq_tagset_busy_iter(&shost->tag_set, + mpi3mr_flush_scmd, (void *)mrioc); + ioc_info(mrioc, "%s :Flushed %d Host I/O cmds\n", __func__, + mrioc->flush_io_count); +} + +/** + * mpi3mr_alloc_tgtdev - target device allocator + * + * Allocate target device instance and initialize the reference + * count + * + * Return: target device instance. + */ +static struct mpi3mr_tgt_dev *mpi3mr_alloc_tgtdev(void) +{ + struct mpi3mr_tgt_dev *tgtdev; + + tgtdev = kzalloc(sizeof(*tgtdev), GFP_ATOMIC); + if (!tgtdev) + return NULL; + kref_init(&tgtdev->ref_count); + return tgtdev; +} + +/** + * mpi3mr_tgtdev_add_to_list -Add tgtdevice to the list + * @mrioc: Adapter instance reference + * @tgtdev: Target device + * + * Add the target device to the target device list + * + * Return: Nothing. + */ +static void mpi3mr_tgtdev_add_to_list(struct mpi3mr_ioc *mrioc, + struct mpi3mr_tgt_dev *tgtdev) +{ + unsigned long flags; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + mpi3mr_tgtdev_get(tgtdev); + INIT_LIST_HEAD(&tgtdev->list); + list_add_tail(&tgtdev->list, &mrioc->tgtdev_list); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + +/** + * mpi3mr_tgtdev_del_from_list -Delete tgtdevice from the list + * @mrioc: Adapter instance reference + * @tgtdev: Target device + * + * Remove the target device from the target device list + * + * Return: Nothing. + */ +static void mpi3mr_tgtdev_del_from_list(struct mpi3mr_ioc *mrioc, + struct mpi3mr_tgt_dev *tgtdev) +{ + unsigned long flags; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + if (!list_empty(&tgtdev->list)) { + list_del_init(&tgtdev->list); + mpi3mr_tgtdev_put(tgtdev); + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + +/** + * __mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle + * @mrioc: Adapter instance reference + * @handle: Device handle + * + * Accessor to retrieve target device from the device handle. + * Non Lock version + * + * Return: Target device reference. + */ +static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_handle( + struct mpi3mr_ioc *mrioc, u16 handle) +{ + struct mpi3mr_tgt_dev *tgtdev; + + assert_spin_locked(&mrioc->tgtdev_lock); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) + if (tgtdev->dev_handle == handle) + goto found_tgtdev; + return NULL; + +found_tgtdev: + mpi3mr_tgtdev_get(tgtdev); + return tgtdev; +} + +/** + * mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle + * @mrioc: Adapter instance reference + * @handle: Device handle + * + * Accessor to retrieve target device from the device handle. + * Lock version + * + * Return: Target device reference. + */ +static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle( + struct mpi3mr_ioc *mrioc, u16 handle) +{ + struct mpi3mr_tgt_dev *tgtdev; + unsigned long flags; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgtdev = __mpi3mr_get_tgtdev_by_handle(mrioc, handle); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + return tgtdev; +} + +/** + * __mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persist ID + * @mrioc: Adapter instance reference + * @persist_id: Persistent ID + * + * Accessor to retrieve target device from the Persistent ID. + * Non Lock version + * + * Return: Target device reference. + */ +static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_perst_id( + struct mpi3mr_ioc *mrioc, u16 persist_id) +{ + struct mpi3mr_tgt_dev *tgtdev; + + assert_spin_locked(&mrioc->tgtdev_lock); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) + if (tgtdev->perst_id == persist_id) + goto found_tgtdev; + return NULL; + +found_tgtdev: + mpi3mr_tgtdev_get(tgtdev); + return tgtdev; +} + +/** + * mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persistent ID + * @mrioc: Adapter instance reference + * @persist_id: Persistent ID + * + * Accessor to retrieve target device from the Persistent ID. + * Lock version + * + * Return: Target device reference. + */ +static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id( + struct mpi3mr_ioc *mrioc, u16 persist_id) +{ + struct mpi3mr_tgt_dev *tgtdev; + unsigned long flags; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, persist_id); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + return tgtdev; +} + +/** + * __mpi3mr_get_tgtdev_from_tgtpriv -Get tgtdev from tgt private + * @mrioc: Adapter instance reference + * @tgt_priv: Target private data + * + * Accessor to return target device from the target private + * data. Non Lock version + * + * Return: Target device reference. + */ +static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv( + struct mpi3mr_ioc *mrioc, struct mpi3mr_stgt_priv_data *tgt_priv) +{ + struct mpi3mr_tgt_dev *tgtdev; + + assert_spin_locked(&mrioc->tgtdev_lock); + tgtdev = tgt_priv->tgt_dev; + if (tgtdev) + mpi3mr_tgtdev_get(tgtdev); + return tgtdev; +} + +/** + * mpi3mr_remove_tgtdev_from_host - Remove dev from upper layers + * @mrioc: Adapter instance reference + * @tgtdev: Target device structure + * + * Checks whether the device is exposed to upper layers and if it + * is then remove the device from upper layers by calling + * scsi_remove_target(). + * + * Return: 0 on success, non zero on failure. + */ +static void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc, + struct mpi3mr_tgt_dev *tgtdev) +{ + struct mpi3mr_stgt_priv_data *tgt_priv; + + ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n", + __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid); + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + } + + if (tgtdev->starget) { + scsi_remove_target(&tgtdev->starget->dev); + tgtdev->host_exposed = 0; + } + ioc_info(mrioc, "%s :Removed handle(0x%04x), wwid(0x%016llx)\n", + __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid); +} + +/** + * mpi3mr_report_tgtdev_to_host - Expose device to upper layers + * @mrioc: Adapter instance reference + * @perst_id: Persistent ID of the device + * + * Checks whether the device can be exposed to upper layers and + * if it is not then expose the device to upper layers by + * calling scsi_scan_target(). + * + * Return: 0 on success, non zero on failure. + */ +static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc, + u16 perst_id) +{ + int retval = 0; + struct mpi3mr_tgt_dev *tgtdev; + + tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id); + if (!tgtdev) { + retval = -1; + goto out; + } + if (tgtdev->is_hidden) { + retval = -1; + goto out; + } + if (!tgtdev->host_exposed && !mrioc->reset_in_progress) { + tgtdev->host_exposed = 1; + scsi_scan_target(&mrioc->shost->shost_gendev, 0, + tgtdev->perst_id, + SCAN_WILD_CARD, SCSI_SCAN_INITIAL); + if (!tgtdev->starget) + tgtdev->host_exposed = 0; + } +out: + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + + return retval; +} + +/** + * mpi3mr_change_queue_depth- Change QD callback handler + * @sdev: SCSI device reference + * @q_depth: Queue depth + * + * Validate and limit QD and call scsi_change_queue_depth. + * + * Return: return value of scsi_change_queue_depth + */ +static int mpi3mr_change_queue_depth(struct scsi_device *sdev, + int q_depth) +{ + struct scsi_target *starget = scsi_target(sdev); + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + int retval = 0; + + if (!sdev->tagged_supported) + q_depth = 1; + if (q_depth > shost->can_queue) + q_depth = shost->can_queue; + else if (!q_depth) + q_depth = MPI3MR_DEFAULT_SDEV_QD; + retval = scsi_change_queue_depth(sdev, q_depth); + + return retval; +} + +/** + * mpi3mr_update_sdev - Update SCSI device information + * @sdev: SCSI device reference + * @data: target device reference + * + * This is an iterator function called for each SCSI device in a + * target to update the target specific information into each + * SCSI device. + * + * Return: Nothing. + */ +static void +mpi3mr_update_sdev(struct scsi_device *sdev, void *data) +{ + struct mpi3mr_tgt_dev *tgtdev; + + tgtdev = (struct mpi3mr_tgt_dev *)data; + if (!tgtdev) + return; + + mpi3mr_change_queue_depth(sdev, tgtdev->q_depth); + switch (tgtdev->dev_type) { + case MPI3_DEVICE_DEVFORM_PCIE: + /*The block layer hw sector size = 512*/ + blk_queue_max_hw_sectors(sdev->request_queue, + tgtdev->dev_spec.pcie_inf.mdts / 512); + blk_queue_virt_boundary(sdev->request_queue, + ((1 << tgtdev->dev_spec.pcie_inf.pgsz) - 1)); + + break; + default: + break; + } +} + +/** + * mpi3mr_rfresh_tgtdevs - Refresh target device exposure + * @mrioc: Adapter instance reference + * + * This is executed post controller reset to identify any + * missing devices during reset and remove from the upper layers + * or expose any newly detected device to the upper layers. + * + * Return: Nothing. + */ + +void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) +{ + struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next; + + list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, + list) { + if ((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) && + tgtdev->host_exposed) { + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + mpi3mr_tgtdev_del_from_list(mrioc, tgtdev); + mpi3mr_tgtdev_put(tgtdev); + } + } + + tgtdev = NULL; + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + if ((tgtdev->dev_handle != MPI3MR_INVALID_DEV_HANDLE) && + !tgtdev->is_hidden && !tgtdev->host_exposed) + mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id); + } +} + +/** + * mpi3mr_update_tgtdev - DevStatusChange evt bottomhalf + * @mrioc: Adapter instance reference + * @tgtdev: Target device internal structure + * @dev_pg0: New device page0 + * + * Update the information from the device page0 into the driver + * cached target device structure. + * + * Return: Nothing. + */ +static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, + struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0) +{ + u16 flags = 0; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + u8 prot_mask = 0; + + tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id); + tgtdev->dev_handle = le16_to_cpu(dev_pg0->dev_handle); + tgtdev->dev_type = dev_pg0->device_form; + tgtdev->encl_handle = le16_to_cpu(dev_pg0->enclosure_handle); + tgtdev->parent_handle = le16_to_cpu(dev_pg0->parent_dev_handle); + tgtdev->slot = le16_to_cpu(dev_pg0->slot); + tgtdev->q_depth = le16_to_cpu(dev_pg0->queue_depth); + tgtdev->wwid = le64_to_cpu(dev_pg0->wwid); + + flags = le16_to_cpu(dev_pg0->flags); + tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN); + + if (tgtdev->starget && tgtdev->starget->hostdata) { + scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) + tgtdev->starget->hostdata; + scsi_tgt_priv_data->perst_id = tgtdev->perst_id; + scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle; + scsi_tgt_priv_data->dev_type = tgtdev->dev_type; + } + + switch (tgtdev->dev_type) { + case MPI3_DEVICE_DEVFORM_SAS_SATA: + { + struct mpi3_device0_sas_sata_format *sasinf = + &dev_pg0->device_specific.sas_sata_format; + u16 dev_info = le16_to_cpu(sasinf->device_info); + + tgtdev->dev_spec.sas_sata_inf.dev_info = dev_info; + tgtdev->dev_spec.sas_sata_inf.sas_address = + le64_to_cpu(sasinf->sas_address); + if ((dev_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) != + MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE) + tgtdev->is_hidden = 1; + else if (!(dev_info & (MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET | + MPI3_SAS_DEVICE_INFO_SSP_TARGET))) + tgtdev->is_hidden = 1; + break; + } + case MPI3_DEVICE_DEVFORM_PCIE: + { + struct mpi3_device0_pcie_format *pcieinf = + &dev_pg0->device_specific.pcie_format; + u16 dev_info = le16_to_cpu(pcieinf->device_info); + + tgtdev->dev_spec.pcie_inf.capb = + le32_to_cpu(pcieinf->capabilities); + tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS; + /* 2^12 = 4096 */ + tgtdev->dev_spec.pcie_inf.pgsz = 12; + if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) { + tgtdev->dev_spec.pcie_inf.mdts = + le32_to_cpu(pcieinf->maximum_data_transfer_size); + tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size; + tgtdev->dev_spec.pcie_inf.reset_to = + pcieinf->controller_reset_to; + tgtdev->dev_spec.pcie_inf.abort_to = + pcieinf->nv_me_abort_to; + } + if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024)) + tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024); + if ((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) != + MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE) + tgtdev->is_hidden = 1; + if (mrioc->shost) + prot_mask = scsi_host_get_prot(mrioc->shost); + if (prot_mask & SHOST_DIX_TYPE0_PROTECTION) { + scsi_host_set_prot(mrioc->shost, prot_mask & 0x77); + ioc_info(mrioc, + "%s : Disabling DIX0 prot capability\n", __func__); + ioc_info(mrioc, + "because HBA does not support DIX0 operation on NVME drives\n"); + } + break; + } + case MPI3_DEVICE_DEVFORM_VD: + { + struct mpi3_device0_vd_format *vdinf = + &dev_pg0->device_specific.vd_format; + + tgtdev->dev_spec.vol_inf.state = vdinf->vd_state; + if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE) + tgtdev->is_hidden = 1; + break; + } + default: + break; + } +} + +/** + * mpi3mr_devstatuschg_evt_bh - DevStatusChange evt bottomhalf + * @mrioc: Adapter instance reference + * @fwevt: Firmware event information. + * + * Process Device status Change event and based on device's new + * information, either expose the device to the upper layers, or + * remove the device from upper layers. + * + * Return: Nothing. + */ +static void mpi3mr_devstatuschg_evt_bh(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + u16 dev_handle = 0; + u8 uhide = 0, delete = 0, cleanup = 0; + struct mpi3mr_tgt_dev *tgtdev = NULL; + struct mpi3_event_data_device_status_change *evtdata = + (struct mpi3_event_data_device_status_change *)fwevt->event_data; + + dev_handle = le16_to_cpu(evtdata->dev_handle); + ioc_info(mrioc, + "%s :device status change: handle(0x%04x): reason code(0x%x)\n", + __func__, dev_handle, evtdata->reason_code); + switch (evtdata->reason_code) { + case MPI3_EVENT_DEV_STAT_RC_HIDDEN: + delete = 1; + break; + case MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN: + uhide = 1; + break; + case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING: + delete = 1; + cleanup = 1; + break; + default: + ioc_info(mrioc, "%s :Unhandled reason code(0x%x)\n", __func__, + evtdata->reason_code); + break; + } + + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle); + if (!tgtdev) + goto out; + if (uhide) { + tgtdev->is_hidden = 0; + if (!tgtdev->host_exposed) + mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id); + } + if (tgtdev->starget && tgtdev->starget->hostdata) { + if (delete) + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + } + if (cleanup) { + mpi3mr_tgtdev_del_from_list(mrioc, tgtdev); + mpi3mr_tgtdev_put(tgtdev); + } + +out: + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); +} + +/** + * mpi3mr_devinfochg_evt_bh - DeviceInfoChange evt bottomhalf + * @mrioc: Adapter instance reference + * @dev_pg0: New device page0 + * + * Process Device Info Change event and based on device's new + * information, either expose the device to the upper layers, or + * remove the device from upper layers or update the details of + * the device. + * + * Return: Nothing. + */ +static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, + struct mpi3_device_page0 *dev_pg0) +{ + struct mpi3mr_tgt_dev *tgtdev = NULL; + u16 dev_handle = 0, perst_id = 0; + + perst_id = le16_to_cpu(dev_pg0->persistent_id); + dev_handle = le16_to_cpu(dev_pg0->dev_handle); + ioc_info(mrioc, + "%s :Device info change: handle(0x%04x): persist_id(0x%x)\n", + __func__, dev_handle, perst_id); + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle); + if (!tgtdev) + goto out; + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + if (!tgtdev->is_hidden && !tgtdev->host_exposed) + mpi3mr_report_tgtdev_to_host(mrioc, perst_id); + if (tgtdev->is_hidden && tgtdev->host_exposed) + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + if (!tgtdev->is_hidden && tgtdev->host_exposed && tgtdev->starget) + starget_for_each_device(tgtdev->starget, (void *)tgtdev, + mpi3mr_update_sdev); +out: + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); +} + +/** + * mpi3mr_sastopochg_evt_debug - SASTopoChange details + * @mrioc: Adapter instance reference + * @event_data: SAS topology change list event data + * + * Prints information about the SAS topology change event. + * + * Return: Nothing. + */ +static void +mpi3mr_sastopochg_evt_debug(struct mpi3mr_ioc *mrioc, + struct mpi3_event_data_sas_topology_change_list *event_data) +{ + int i; + u16 handle; + u8 reason_code, phy_number; + char *status_str = NULL; + u8 link_rate, prev_link_rate; + + switch (event_data->exp_status) { + case MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING: + status_str = "remove"; + break; + case MPI3_EVENT_SAS_TOPO_ES_RESPONDING: + status_str = "responding"; + break; + case MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: + status_str = "remove delay"; + break; + case MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER: + status_str = "direct attached"; + break; + default: + status_str = "unknown status"; + break; + } + ioc_info(mrioc, "%s :sas topology change: (%s)\n", + __func__, status_str); + ioc_info(mrioc, + "%s :\texpander_handle(0x%04x), enclosure_handle(0x%04x) start_phy(%02d), num_entries(%d)\n", + __func__, le16_to_cpu(event_data->expander_dev_handle), + le16_to_cpu(event_data->enclosure_handle), + event_data->start_phy_num, event_data->num_entries); + for (i = 0; i < event_data->num_entries; i++) { + handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle); + if (!handle) + continue; + phy_number = event_data->start_phy_num + i; + reason_code = event_data->phy_entry[i].status & + MPI3_EVENT_SAS_TOPO_PHY_RC_MASK; + switch (reason_code) { + case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING: + status_str = "target remove"; + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING: + status_str = "delay target remove"; + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED: + status_str = "link status change"; + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE: + status_str = "link status no change"; + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING: + status_str = "target responding"; + break; + default: + status_str = "unknown"; + break; + } + link_rate = event_data->phy_entry[i].link_rate >> 4; + prev_link_rate = event_data->phy_entry[i].link_rate & 0xF; + ioc_info(mrioc, + "%s :\tphy(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n", + __func__, phy_number, handle, status_str, link_rate, + prev_link_rate); + } +} + +/** + * mpi3mr_sastopochg_evt_bh - SASTopologyChange evt bottomhalf + * @mrioc: Adapter instance reference + * @fwevt: Firmware event reference + * + * Prints information about the SAS topology change event and + * for "not responding" event code, removes the device from the + * upper layers. + * + * Return: Nothing. + */ +static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + struct mpi3_event_data_sas_topology_change_list *event_data = + (struct mpi3_event_data_sas_topology_change_list *)fwevt->event_data; + int i; + u16 handle; + u8 reason_code; + struct mpi3mr_tgt_dev *tgtdev = NULL; + + mpi3mr_sastopochg_evt_debug(mrioc, event_data); + + for (i = 0; i < event_data->num_entries; i++) { + handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle); + if (!handle) + continue; + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle); + if (!tgtdev) + continue; + + reason_code = event_data->phy_entry[i].status & + MPI3_EVENT_SAS_TOPO_PHY_RC_MASK; + + switch (reason_code) { + case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING: + if (tgtdev->host_exposed) + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + mpi3mr_tgtdev_del_from_list(mrioc, tgtdev); + mpi3mr_tgtdev_put(tgtdev); + break; + default: + break; + } + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + } +} + +/** + * mpi3mr_pcietopochg_evt_debug - PCIeTopoChange details + * @mrioc: Adapter instance reference + * @event_data: PCIe topology change list event data + * + * Prints information about the PCIe topology change event. + * + * Return: Nothing. + */ +static void +mpi3mr_pcietopochg_evt_debug(struct mpi3mr_ioc *mrioc, + struct mpi3_event_data_pcie_topology_change_list *event_data) +{ + int i; + u16 handle; + u16 reason_code; + u8 port_number; + char *status_str = NULL; + u8 link_rate, prev_link_rate; + + switch (event_data->switch_status) { + case MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING: + status_str = "remove"; + break; + case MPI3_EVENT_PCIE_TOPO_SS_RESPONDING: + status_str = "responding"; + break; + case MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING: + status_str = "remove delay"; + break; + case MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH: + status_str = "direct attached"; + break; + default: + status_str = "unknown status"; + break; + } + ioc_info(mrioc, "%s :pcie topology change: (%s)\n", + __func__, status_str); + ioc_info(mrioc, + "%s :\tswitch_handle(0x%04x), enclosure_handle(0x%04x) start_port(%02d), num_entries(%d)\n", + __func__, le16_to_cpu(event_data->switch_dev_handle), + le16_to_cpu(event_data->enclosure_handle), + event_data->start_port_num, event_data->num_entries); + for (i = 0; i < event_data->num_entries; i++) { + handle = + le16_to_cpu(event_data->port_entry[i].attached_dev_handle); + if (!handle) + continue; + port_number = event_data->start_port_num + i; + reason_code = event_data->port_entry[i].port_status; + switch (reason_code) { + case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + status_str = "target remove"; + break; + case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING: + status_str = "delay target remove"; + break; + case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED: + status_str = "link status change"; + break; + case MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE: + status_str = "link status no change"; + break; + case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING: + status_str = "target responding"; + break; + default: + status_str = "unknown"; + break; + } + link_rate = event_data->port_entry[i].current_port_info & + MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK; + prev_link_rate = event_data->port_entry[i].previous_port_info & + MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK; + ioc_info(mrioc, + "%s :\tport(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n", + __func__, port_number, handle, status_str, link_rate, + prev_link_rate); + } +} + +/** + * mpi3mr_pcietopochg_evt_bh - PCIeTopologyChange evt bottomhalf + * @mrioc: Adapter instance reference + * @fwevt: Firmware event reference + * + * Prints information about the PCIe topology change event and + * for "not responding" event code, removes the device from the + * upper layers. + * + * Return: Nothing. + */ +static void mpi3mr_pcietopochg_evt_bh(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + struct mpi3_event_data_pcie_topology_change_list *event_data = + (struct mpi3_event_data_pcie_topology_change_list *)fwevt->event_data; + int i; + u16 handle; + u8 reason_code; + struct mpi3mr_tgt_dev *tgtdev = NULL; + + mpi3mr_pcietopochg_evt_debug(mrioc, event_data); + + for (i = 0; i < event_data->num_entries; i++) { + handle = + le16_to_cpu(event_data->port_entry[i].attached_dev_handle); + if (!handle) + continue; + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle); + if (!tgtdev) + continue; + + reason_code = event_data->port_entry[i].port_status; + + switch (reason_code) { + case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + if (tgtdev->host_exposed) + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + mpi3mr_tgtdev_del_from_list(mrioc, tgtdev); + mpi3mr_tgtdev_put(tgtdev); + break; + default: + break; + } + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + } +} + +/** + * mpi3mr_fwevt_bh - Firmware event bottomhalf handler + * @mrioc: Adapter instance reference + * @fwevt: Firmware event reference + * + * Identifies the firmware event and calls corresponding bottomg + * half handler and sends event acknowledgment if required. + * + * Return: Nothing. + */ +static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, + struct mpi3mr_fwevt *fwevt) +{ + mrioc->current_event = fwevt; + mpi3mr_fwevt_del_from_list(mrioc, fwevt); + + if (mrioc->stop_drv_processing) + goto out; + + if (!fwevt->process_evt) + goto evt_ack; + + switch (fwevt->event_id) { + case MPI3_EVENT_DEVICE_ADDED: + { + struct mpi3_device_page0 *dev_pg0 = + (struct mpi3_device_page0 *)fwevt->event_data; + mpi3mr_report_tgtdev_to_host(mrioc, + le16_to_cpu(dev_pg0->persistent_id)); + break; + } + case MPI3_EVENT_DEVICE_INFO_CHANGED: + { + mpi3mr_devinfochg_evt_bh(mrioc, + (struct mpi3_device_page0 *)fwevt->event_data); + break; + } + case MPI3_EVENT_DEVICE_STATUS_CHANGE: + { + mpi3mr_devstatuschg_evt_bh(mrioc, fwevt); + break; + } + case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + { + mpi3mr_sastopochg_evt_bh(mrioc, fwevt); + break; + } + case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + { + mpi3mr_pcietopochg_evt_bh(mrioc, fwevt); + break; + } + default: + break; + } + +evt_ack: + if (fwevt->send_ack) + mpi3mr_send_event_ack(mrioc, fwevt->event_id, + fwevt->evt_ctx); +out: + /* Put fwevt reference count to neutralize kref_init increment */ + mpi3mr_fwevt_put(fwevt); + mrioc->current_event = NULL; +} + +/** + * mpi3mr_fwevt_worker - Firmware event worker + * @work: Work struct containing firmware event + * + * Extracts the firmware event and calls mpi3mr_fwevt_bh. + * + * Return: Nothing. + */ +static void mpi3mr_fwevt_worker(struct work_struct *work) +{ + struct mpi3mr_fwevt *fwevt = container_of(work, struct mpi3mr_fwevt, + work); + mpi3mr_fwevt_bh(fwevt->mrioc, fwevt); + /* + * Put fwevt reference count after + * dequeuing it from worker queue + */ + mpi3mr_fwevt_put(fwevt); +} + +/** + * mpi3mr_create_tgtdev - Create and add a target device + * @mrioc: Adapter instance reference + * @dev_pg0: Device Page 0 data + * + * If the device specified by the device page 0 data is not + * present in the driver's internal list, allocate the memory + * for the device, populate the data and add to the list, else + * update the device data. The key is persistent ID. + * + * Return: 0 on success, -ENOMEM on memory allocation failure + */ +static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc, + struct mpi3_device_page0 *dev_pg0) +{ + int retval = 0; + struct mpi3mr_tgt_dev *tgtdev = NULL; + u16 perst_id = 0; + + perst_id = le16_to_cpu(dev_pg0->persistent_id); + tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id); + if (tgtdev) { + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_tgtdev_put(tgtdev); + } else { + tgtdev = mpi3mr_alloc_tgtdev(); + if (!tgtdev) + return -ENOMEM; + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_tgtdev_add_to_list(mrioc, tgtdev); + } + + return retval; +} + +/** + * mpi3mr_flush_delayed_rmhs_list - Flush pending commands + * @mrioc: Adapter instance reference + * + * Flush pending commands in the delayed removal handshake list + * due to a controller reset or driver removal as a cleanup. + * + * Return: Nothing + */ +void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc) +{ + struct delayed_dev_rmhs_node *_rmhs_node; + + while (!list_empty(&mrioc->delayed_rmhs_list)) { + _rmhs_node = list_entry(mrioc->delayed_rmhs_list.next, + struct delayed_dev_rmhs_node, list); + list_del(&_rmhs_node->list); + kfree(_rmhs_node); + } +} + +/** + * mpi3mr_dev_rmhs_complete_iou - Device removal IOUC completion + * @mrioc: Adapter instance reference + * @drv_cmd: Internal command tracker + * + * Issues a target reset TM to the firmware from the device + * removal TM pend list or retry the removal handshake sequence + * based on the IOU control request IOC status. + * + * Return: Nothing + */ +static void mpi3mr_dev_rmhs_complete_iou(struct mpi3mr_ioc *mrioc, + struct mpi3mr_drv_cmd *drv_cmd) +{ + u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN; + struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL; + + ioc_info(mrioc, + "%s :dev_rmhs_iouctrl_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x)\n", + __func__, drv_cmd->dev_handle, drv_cmd->ioc_status, + drv_cmd->ioc_loginfo); + if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) { + if (drv_cmd->retry_count < MPI3MR_DEV_RMHS_RETRY_COUNT) { + drv_cmd->retry_count++; + ioc_info(mrioc, + "%s :dev_rmhs_iouctrl_complete: handle(0x%04x)retrying handshake retry=%d\n", + __func__, drv_cmd->dev_handle, + drv_cmd->retry_count); + mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle, + drv_cmd, drv_cmd->iou_rc); + return; + } + ioc_err(mrioc, + "%s :dev removal handshake failed after all retries: handle(0x%04x)\n", + __func__, drv_cmd->dev_handle); + } else { + ioc_info(mrioc, + "%s :dev removal handshake completed successfully: handle(0x%04x)\n", + __func__, drv_cmd->dev_handle); + clear_bit(drv_cmd->dev_handle, mrioc->removepend_bitmap); + } + + if (!list_empty(&mrioc->delayed_rmhs_list)) { + delayed_dev_rmhs = list_entry(mrioc->delayed_rmhs_list.next, + struct delayed_dev_rmhs_node, list); + drv_cmd->dev_handle = delayed_dev_rmhs->handle; + drv_cmd->retry_count = 0; + drv_cmd->iou_rc = delayed_dev_rmhs->iou_rc; + ioc_info(mrioc, + "%s :dev_rmhs_iouctrl_complete: processing delayed TM: handle(0x%04x)\n", + __func__, drv_cmd->dev_handle); + mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle, drv_cmd, + drv_cmd->iou_rc); + list_del(&delayed_dev_rmhs->list); + kfree(delayed_dev_rmhs); + return; + } + drv_cmd->state = MPI3MR_CMD_NOTUSED; + drv_cmd->callback = NULL; + drv_cmd->retry_count = 0; + drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + clear_bit(cmd_idx, mrioc->devrem_bitmap); +} + +/** + * mpi3mr_dev_rmhs_complete_tm - Device removal TM completion + * @mrioc: Adapter instance reference + * @drv_cmd: Internal command tracker + * + * Issues a target reset TM to the firmware from the device + * removal TM pend list or issue IO unit control request as + * part of device removal or hidden acknowledgment handshake. + * + * Return: Nothing + */ +static void mpi3mr_dev_rmhs_complete_tm(struct mpi3mr_ioc *mrioc, + struct mpi3mr_drv_cmd *drv_cmd) +{ + struct mpi3_iounit_control_request iou_ctrl; + u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN; + struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL; + int retval; + + if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID) + tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply; + + if (tm_reply) + pr_info(IOCNAME + "dev_rmhs_tr_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x), term_count(%d)\n", + mrioc->name, drv_cmd->dev_handle, drv_cmd->ioc_status, + drv_cmd->ioc_loginfo, + le32_to_cpu(tm_reply->termination_count)); + + pr_info(IOCNAME "Issuing IOU CTL: handle(0x%04x) dev_rmhs idx(%d)\n", + mrioc->name, drv_cmd->dev_handle, cmd_idx); + + memset(&iou_ctrl, 0, sizeof(iou_ctrl)); + + drv_cmd->state = MPI3MR_CMD_PENDING; + drv_cmd->is_waiting = 0; + drv_cmd->callback = mpi3mr_dev_rmhs_complete_iou; + iou_ctrl.operation = drv_cmd->iou_rc; + iou_ctrl.param16[0] = cpu_to_le16(drv_cmd->dev_handle); + iou_ctrl.host_tag = cpu_to_le16(drv_cmd->host_tag); + iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL; + + retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl, sizeof(iou_ctrl), + 1); + if (retval) { + pr_err(IOCNAME "Issue DevRmHsTMIOUCTL: Admin post failed\n", + mrioc->name); + goto out_failed; + } + + return; +out_failed: + drv_cmd->state = MPI3MR_CMD_NOTUSED; + drv_cmd->callback = NULL; + drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + drv_cmd->retry_count = 0; + clear_bit(cmd_idx, mrioc->devrem_bitmap); +} + +/** + * mpi3mr_dev_rmhs_send_tm - Issue TM for device removal + * @mrioc: Adapter instance reference + * @handle: Device handle + * @cmdparam: Internal command tracker + * @iou_rc: IO unit reason code + * + * Issues a target reset TM to the firmware or add it to a pend + * list as part of device removal or hidden acknowledgment + * handshake. + * + * Return: Nothing + */ +static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle, + struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc) +{ + struct mpi3_scsi_task_mgmt_request tm_req; + int retval = 0; + u16 cmd_idx = MPI3MR_NUM_DEVRMCMD; + u8 retrycount = 5; + struct mpi3mr_drv_cmd *drv_cmd = cmdparam; + struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL; + + if (drv_cmd) + goto issue_cmd; + do { + cmd_idx = find_first_zero_bit(mrioc->devrem_bitmap, + MPI3MR_NUM_DEVRMCMD); + if (cmd_idx < MPI3MR_NUM_DEVRMCMD) { + if (!test_and_set_bit(cmd_idx, mrioc->devrem_bitmap)) + break; + cmd_idx = MPI3MR_NUM_DEVRMCMD; + } + } while (retrycount--); + + if (cmd_idx >= MPI3MR_NUM_DEVRMCMD) { + delayed_dev_rmhs = kzalloc(sizeof(*delayed_dev_rmhs), + GFP_ATOMIC); + if (!delayed_dev_rmhs) + return; + INIT_LIST_HEAD(&delayed_dev_rmhs->list); + delayed_dev_rmhs->handle = handle; + delayed_dev_rmhs->iou_rc = iou_rc; + list_add_tail(&delayed_dev_rmhs->list, + &mrioc->delayed_rmhs_list); + ioc_info(mrioc, "%s :DevRmHs: tr:handle(0x%04x) is postponed\n", + __func__, handle); + return; + } + drv_cmd = &mrioc->dev_rmhs_cmds[cmd_idx]; + +issue_cmd: + cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN; + ioc_info(mrioc, + "%s :Issuing TR TM: for devhandle 0x%04x with dev_rmhs %d\n", + __func__, handle, cmd_idx); + + memset(&tm_req, 0, sizeof(tm_req)); + if (drv_cmd->state & MPI3MR_CMD_PENDING) { + ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__); + goto out; + } + drv_cmd->state = MPI3MR_CMD_PENDING; + drv_cmd->is_waiting = 0; + drv_cmd->callback = mpi3mr_dev_rmhs_complete_tm; + drv_cmd->dev_handle = handle; + drv_cmd->iou_rc = iou_rc; + tm_req.dev_handle = cpu_to_le16(handle); + tm_req.task_type = MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + tm_req.host_tag = cpu_to_le16(drv_cmd->host_tag); + tm_req.task_host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INVALID); + tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT; + + set_bit(handle, mrioc->removepend_bitmap); + retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1); + if (retval) { + ioc_err(mrioc, "%s :Issue DevRmHsTM: Admin Post failed\n", + __func__); + goto out_failed; + } +out: + return; +out_failed: + drv_cmd->state = MPI3MR_CMD_NOTUSED; + drv_cmd->callback = NULL; + drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + drv_cmd->retry_count = 0; + clear_bit(cmd_idx, mrioc->devrem_bitmap); +} + +/** + * mpi3mr_pcietopochg_evt_th - PCIETopologyChange evt tophalf + * @mrioc: Adapter instance reference + * @event_reply: event data + * + * Checks for the reason code and based on that either block I/O + * to device, or unblock I/O to the device, or start the device + * removal handshake with reason as remove with the firmware for + * PCIe devices. + * + * Return: Nothing + */ +static void mpi3mr_pcietopochg_evt_th(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + struct mpi3_event_data_pcie_topology_change_list *topo_evt = + (struct mpi3_event_data_pcie_topology_change_list *)event_reply->event_data; + int i; + u16 handle; + u8 reason_code; + struct mpi3mr_tgt_dev *tgtdev = NULL; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; + + for (i = 0; i < topo_evt->num_entries; i++) { + handle = le16_to_cpu(topo_evt->port_entry[i].attached_dev_handle); + if (!handle) + continue; + reason_code = topo_evt->port_entry[i].port_status; + scsi_tgt_priv_data = NULL; + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle); + if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata) + scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) + tgtdev->starget->hostdata; + switch (reason_code) { + case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING: + if (scsi_tgt_priv_data) { + scsi_tgt_priv_data->dev_removed = 1; + scsi_tgt_priv_data->dev_removedelay = 0; + atomic_set(&scsi_tgt_priv_data->block_io, 0); + } + mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL, + MPI3_CTRL_OP_REMOVE_DEVICE); + break; + case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING: + if (scsi_tgt_priv_data) { + scsi_tgt_priv_data->dev_removedelay = 1; + atomic_inc(&scsi_tgt_priv_data->block_io); + } + break; + case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING: + if (scsi_tgt_priv_data && + scsi_tgt_priv_data->dev_removedelay) { + scsi_tgt_priv_data->dev_removedelay = 0; + atomic_dec_if_positive + (&scsi_tgt_priv_data->block_io); + } + break; + case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED: + default: + break; + } + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + } +} + +/** + * mpi3mr_sastopochg_evt_th - SASTopologyChange evt tophalf + * @mrioc: Adapter instance reference + * @event_reply: event data + * + * Checks for the reason code and based on that either block I/O + * to device, or unblock I/O to the device, or start the device + * removal handshake with reason as remove with the firmware for + * SAS/SATA devices. + * + * Return: Nothing + */ +static void mpi3mr_sastopochg_evt_th(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + struct mpi3_event_data_sas_topology_change_list *topo_evt = + (struct mpi3_event_data_sas_topology_change_list *)event_reply->event_data; + int i; + u16 handle; + u8 reason_code; + struct mpi3mr_tgt_dev *tgtdev = NULL; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; + + for (i = 0; i < topo_evt->num_entries; i++) { + handle = le16_to_cpu(topo_evt->phy_entry[i].attached_dev_handle); + if (!handle) + continue; + reason_code = topo_evt->phy_entry[i].status & + MPI3_EVENT_SAS_TOPO_PHY_RC_MASK; + scsi_tgt_priv_data = NULL; + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle); + if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata) + scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) + tgtdev->starget->hostdata; + switch (reason_code) { + case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING: + if (scsi_tgt_priv_data) { + scsi_tgt_priv_data->dev_removed = 1; + scsi_tgt_priv_data->dev_removedelay = 0; + atomic_set(&scsi_tgt_priv_data->block_io, 0); + } + mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL, + MPI3_CTRL_OP_REMOVE_DEVICE); + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING: + if (scsi_tgt_priv_data) { + scsi_tgt_priv_data->dev_removedelay = 1; + atomic_inc(&scsi_tgt_priv_data->block_io); + } + break; + case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING: + if (scsi_tgt_priv_data && + scsi_tgt_priv_data->dev_removedelay) { + scsi_tgt_priv_data->dev_removedelay = 0; + atomic_dec_if_positive + (&scsi_tgt_priv_data->block_io); + } + case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED: + default: + break; + } + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + } +} + +/** + * mpi3mr_devstatuschg_evt_th - DeviceStatusChange evt tophalf + * @mrioc: Adapter instance reference + * @event_reply: event data + * + * Checks for the reason code and based on that either block I/O + * to device, or unblock I/O to the device, or start the device + * removal handshake with reason as remove/hide acknowledgment + * with the firmware. + * + * Return: Nothing + */ +static void mpi3mr_devstatuschg_evt_th(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + u16 dev_handle = 0; + u8 ublock = 0, block = 0, hide = 0, delete = 0, remove = 0; + struct mpi3mr_tgt_dev *tgtdev = NULL; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; + struct mpi3_event_data_device_status_change *evtdata = + (struct mpi3_event_data_device_status_change *)event_reply->event_data; + + if (mrioc->stop_drv_processing) + goto out; + + dev_handle = le16_to_cpu(evtdata->dev_handle); + + switch (evtdata->reason_code) { + case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT: + case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT: + block = 1; + break; + case MPI3_EVENT_DEV_STAT_RC_HIDDEN: + delete = 1; + hide = 1; + break; + case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING: + delete = 1; + remove = 1; + break; + case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP: + case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP: + ublock = 1; + break; + default: + break; + } + + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle); + if (!tgtdev) + goto out; + if (hide) + tgtdev->is_hidden = hide; + if (tgtdev->starget && tgtdev->starget->hostdata) { + scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) + tgtdev->starget->hostdata; + if (block) + atomic_inc(&scsi_tgt_priv_data->block_io); + if (delete) + scsi_tgt_priv_data->dev_removed = 1; + if (ublock) + atomic_dec_if_positive(&scsi_tgt_priv_data->block_io); + } + if (remove) + mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL, + MPI3_CTRL_OP_REMOVE_DEVICE); + if (hide) + mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL, + MPI3_CTRL_OP_HIDDEN_ACK); + +out: + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); +} + +/** + * mpi3mr_energypackchg_evt_th - Energy pack change evt tophalf + * @mrioc: Adapter instance reference + * @event_reply: event data + * + * Identifies the new shutdown timeout value and update. + * + * Return: Nothing + */ +static void mpi3mr_energypackchg_evt_th(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + struct mpi3_event_data_energy_pack_change *evtdata = + (struct mpi3_event_data_energy_pack_change *)event_reply->event_data; + u16 shutdown_timeout = le16_to_cpu(evtdata->shutdown_timeout); + + if (shutdown_timeout <= 0) { + ioc_warn(mrioc, + "%s :Invalid Shutdown Timeout received = %d\n", + __func__, shutdown_timeout); + return; + } + + ioc_info(mrioc, + "%s :Previous Shutdown Timeout Value = %d New Shutdown Timeout Value = %d\n", + __func__, mrioc->facts.shutdown_timeout, shutdown_timeout); + mrioc->facts.shutdown_timeout = shutdown_timeout; +} + +/** + * mpi3mr_os_handle_events - Firmware event handler + * @mrioc: Adapter instance reference + * @event_reply: event data + * + * Identify whteher the event has to handled and acknowledged + * and either process the event in the tophalf and/or schedule a + * bottom half through mpi3mr_fwevt_worker. + * + * Return: Nothing + */ +void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc, + struct mpi3_event_notification_reply *event_reply) +{ + u16 evt_type, sz; + struct mpi3mr_fwevt *fwevt = NULL; + bool ack_req = 0, process_evt_bh = 0; + + if (mrioc->stop_drv_processing) + return; + + if ((event_reply->msg_flags & MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK) + == MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED) + ack_req = 1; + + evt_type = event_reply->event; + + switch (evt_type) { + case MPI3_EVENT_DEVICE_ADDED: + { + struct mpi3_device_page0 *dev_pg0 = + (struct mpi3_device_page0 *)event_reply->event_data; + if (mpi3mr_create_tgtdev(mrioc, dev_pg0)) + ioc_err(mrioc, + "%s :Failed to add device in the device add event\n", + __func__); + else + process_evt_bh = 1; + break; + } + case MPI3_EVENT_DEVICE_STATUS_CHANGE: + { + process_evt_bh = 1; + mpi3mr_devstatuschg_evt_th(mrioc, event_reply); + break; + } + case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + { + process_evt_bh = 1; + mpi3mr_sastopochg_evt_th(mrioc, event_reply); + break; + } + case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST: + { + process_evt_bh = 1; + mpi3mr_pcietopochg_evt_th(mrioc, event_reply); + break; + } + case MPI3_EVENT_DEVICE_INFO_CHANGED: + { + process_evt_bh = 1; + break; + } + case MPI3_EVENT_ENERGY_PACK_CHANGE: + { + mpi3mr_energypackchg_evt_th(mrioc, event_reply); + break; + } + case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE: + case MPI3_EVENT_SAS_DISCOVERY: + case MPI3_EVENT_CABLE_MGMT: + case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR: + case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE: + case MPI3_EVENT_PCIE_ENUMERATION: + break; + default: + ioc_info(mrioc, "%s :event 0x%02x is not handled\n", + __func__, evt_type); + break; + } + if (process_evt_bh || ack_req) { + sz = event_reply->event_data_length * 4; + fwevt = mpi3mr_alloc_fwevt(sz); + if (!fwevt) { + ioc_info(mrioc, "%s :failure at %s:%d/%s()!\n", + __func__, __FILE__, __LINE__, __func__); + return; + } + + memcpy(fwevt->event_data, event_reply->event_data, sz); + fwevt->mrioc = mrioc; + fwevt->event_id = evt_type; + fwevt->send_ack = ack_req; + fwevt->process_evt = process_evt_bh; + fwevt->evt_ctx = le32_to_cpu(event_reply->event_context); + mpi3mr_fwevt_add_to_list(mrioc, fwevt); + } +} + +/** + * mpi3mr_setup_eedp - Setup EEDP information in MPI3 SCSI IO + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * @scsiio_req: MPI3 SCSI IO request + * + * Identifies the protection information flags from the SCSI + * command and set appropriate flags in the MPI3 SCSI IO + * request. + * + * Return: Nothing + */ +static void mpi3mr_setup_eedp(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) +{ + u16 eedp_flags = 0; + unsigned char prot_op = scsi_get_prot_op(scmd); + unsigned char prot_type = scsi_get_prot_type(scmd); + + switch (prot_op) { + case SCSI_PROT_NORMAL: + return; + case SCSI_PROT_READ_STRIP: + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE; + break; + case SCSI_PROT_WRITE_INSERT: + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT; + break; + case SCSI_PROT_READ_INSERT: + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT; + scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID; + break; + case SCSI_PROT_WRITE_STRIP: + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE; + scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID; + break; + case SCSI_PROT_READ_PASS: + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK | + MPI3_EEDPFLAGS_CHK_REF_TAG | MPI3_EEDPFLAGS_CHK_APP_TAG | + MPI3_EEDPFLAGS_CHK_GUARD; + scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID; + break; + case SCSI_PROT_WRITE_PASS: + if (scsi_host_get_guard(scmd->device->host) + & SHOST_DIX_GUARD_IP) { + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN | + MPI3_EEDPFLAGS_CHK_APP_TAG | + MPI3_EEDPFLAGS_CHK_GUARD | + MPI3_EEDPFLAGS_INCR_PRI_REF_TAG; + scsiio_req->sgl[0].eedp.application_tag_translation_mask = + 0xffff; + } else { + eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK | + MPI3_EEDPFLAGS_CHK_REF_TAG | + MPI3_EEDPFLAGS_CHK_APP_TAG | + MPI3_EEDPFLAGS_CHK_GUARD; + } + scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID; + break; + default: + return; + } + + if (scsi_host_get_guard(scmd->device->host) & SHOST_DIX_GUARD_IP) + eedp_flags |= MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM; + + switch (prot_type) { + case SCSI_PROT_DIF_TYPE0: + eedp_flags |= MPI3_EEDPFLAGS_INCR_PRI_REF_TAG; + scsiio_req->cdb.eedp32.primary_reference_tag = + cpu_to_be32(t10_pi_ref_tag(scmd->request)); + break; + case SCSI_PROT_DIF_TYPE1: + case SCSI_PROT_DIF_TYPE2: + eedp_flags |= MPI3_EEDPFLAGS_INCR_PRI_REF_TAG | + MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE | + MPI3_EEDPFLAGS_CHK_GUARD; + scsiio_req->cdb.eedp32.primary_reference_tag = + cpu_to_be32(t10_pi_ref_tag(scmd->request)); + break; + case SCSI_PROT_DIF_TYPE3: + eedp_flags |= MPI3_EEDPFLAGS_CHK_GUARD | + MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE; + break; + + default: + scsiio_req->msg_flags &= ~(MPI3_SCSIIO_MSGFLAGS_METASGL_VALID); + return; + } + + switch (scmd->device->sector_size) { + case 512: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_512; + break; + case 520: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_520; + break; + case 4080: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4080; + break; + case 4088: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4088; + break; + case 4096: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4096; + break; + case 4104: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4104; + break; + case 4160: + scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4160; + break; + default: + break; + } + + scsiio_req->sgl[0].eedp.eedp_flags = cpu_to_le16(eedp_flags); + scsiio_req->sgl[0].eedp.flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED; +} + +/** + * mpi3mr_build_sense_buffer - Map sense information + * @desc: Sense type + * @buf: Sense buffer to populate + * @key: Sense key + * @asc: Additional sense code + * @ascq: Additional sense code qualifier + * + * Maps the given sense information into either descriptor or + * fixed format sense data. + * + * Return: Nothing + */ +static inline void mpi3mr_build_sense_buffer(int desc, u8 *buf, u8 key, + u8 asc, u8 ascq) +{ + if (desc) { + buf[0] = 0x72; /* descriptor, current */ + buf[1] = key; + buf[2] = asc; + buf[3] = ascq; + buf[7] = 0; + } else { + buf[0] = 0x70; /* fixed, current */ + buf[2] = key; + buf[7] = 0xa; + buf[12] = asc; + buf[13] = ascq; + } +} + +/** + * mpi3mr_map_eedp_error - Map EEDP errors from IOC status + * @scmd: SCSI command reference + * @ioc_status: status of MPI3 request + * + * Maps the EEDP error status of the SCSI IO request to sense + * data. + * + * Return: Nothing + */ +static void mpi3mr_map_eedp_error(struct scsi_cmnd *scmd, + u16 ioc_status) +{ + u8 ascq = 0; + + switch (ioc_status) { + case MPI3_IOCSTATUS_EEDP_GUARD_ERROR: + ascq = 0x01; + break; + case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR: + ascq = 0x02; + break; + case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR: + ascq = 0x03; + break; + default: + ascq = 0x00; + break; + } + + mpi3mr_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, ascq); + scmd->result = (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION; +} + +/** + * mpi3mr_process_op_reply_desc - reply descriptor handler + * @mrioc: Adapter instance reference + * @reply_desc: Operational reply descriptor + * @reply_dma: place holder for reply DMA address + * @qidx: Operational queue index + * + * Process the operational reply descriptor and identifies the + * descriptor type. Based on the descriptor map the MPI3 request + * status to a SCSI command status and calls scsi_done call + * back. + * + * Return: Nothing + */ +void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, + struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma, u16 qidx) +{ + u16 reply_desc_type, host_tag = 0; + u16 ioc_status = MPI3_IOCSTATUS_SUCCESS; + u32 ioc_loginfo = 0; + struct mpi3_status_reply_descriptor *status_desc = NULL; + struct mpi3_address_reply_descriptor *addr_desc = NULL; + struct mpi3_success_reply_descriptor *success_desc = NULL; + struct mpi3_scsi_io_reply *scsi_reply = NULL; + struct scsi_cmnd *scmd = NULL; + struct scmd_priv *priv = NULL; + u8 *sense_buf = NULL; + u8 scsi_state = 0, scsi_status = 0, sense_state = 0; + u32 xfer_count = 0, sense_count = 0, resp_data = 0; + u16 dev_handle = 0xFFFF; + struct scsi_sense_hdr sshdr; + + *reply_dma = 0; + reply_desc_type = le16_to_cpu(reply_desc->reply_flags) & + MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK; + switch (reply_desc_type) { + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS: + status_desc = (struct mpi3_status_reply_descriptor *)reply_desc; + host_tag = le16_to_cpu(status_desc->host_tag); + ioc_status = le16_to_cpu(status_desc->ioc_status); + if (ioc_status & + MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) + ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info); + ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; + break; + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY: + addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc; + *reply_dma = le64_to_cpu(addr_desc->reply_frame_address); + scsi_reply = mpi3mr_get_reply_virt_addr(mrioc, + *reply_dma); + if (!scsi_reply) { + panic("%s: scsi_reply is NULL, this shouldn't happen\n", + mrioc->name); + goto out; + } + host_tag = le16_to_cpu(scsi_reply->host_tag); + ioc_status = le16_to_cpu(scsi_reply->ioc_status); + scsi_status = scsi_reply->scsi_status; + scsi_state = scsi_reply->scsi_state; + dev_handle = le16_to_cpu(scsi_reply->dev_handle); + sense_state = (scsi_state & MPI3_SCSI_STATE_SENSE_MASK); + xfer_count = le32_to_cpu(scsi_reply->transfer_count); + sense_count = le32_to_cpu(scsi_reply->sense_count); + resp_data = le32_to_cpu(scsi_reply->response_data); + sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc, + le64_to_cpu(scsi_reply->sense_data_buffer_address)); + if (ioc_status & + MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) + ioc_loginfo = le32_to_cpu(scsi_reply->ioc_log_info); + ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; + if (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY) + panic("%s: Ran out of sense buffers\n", mrioc->name); + break; + case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS: + success_desc = (struct mpi3_success_reply_descriptor *)reply_desc; + host_tag = le16_to_cpu(success_desc->host_tag); + break; + default: + break; + } + scmd = mpi3mr_scmd_from_host_tag(mrioc, host_tag, qidx); + if (!scmd) { + panic("%s: Cannot Identify scmd for host_tag 0x%x\n", + mrioc->name, host_tag); + goto out; + } + priv = scsi_cmd_priv(scmd); + if (success_desc) { + scmd->result = DID_OK << 16; + goto out_success; + } + if (ioc_status == MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN && + xfer_count == 0 && (scsi_status == MPI3_SCSI_STATUS_BUSY || + scsi_status == MPI3_SCSI_STATUS_RESERVATION_CONFLICT || + scsi_status == MPI3_SCSI_STATUS_TASK_SET_FULL)) + ioc_status = MPI3_IOCSTATUS_SUCCESS; + + if ((sense_state == MPI3_SCSI_STATE_SENSE_VALID) && sense_count && + sense_buf) { + u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, sense_count); + + memcpy(scmd->sense_buffer, sense_buf, sz); + } + + switch (ioc_status) { + case MPI3_IOCSTATUS_BUSY: + case MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES: + scmd->result = SAM_STAT_BUSY; + break; + case MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + scmd->result = DID_NO_CONNECT << 16; + break; + case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED: + scmd->result = DID_SOFT_ERROR << 16; + break; + case MPI3_IOCSTATUS_SCSI_TASK_TERMINATED: + case MPI3_IOCSTATUS_SCSI_EXT_TERMINATED: + scmd->result = DID_RESET << 16; + break; + case MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + if ((xfer_count == 0) || (scmd->underflow > xfer_count)) + scmd->result = DID_SOFT_ERROR << 16; + else + scmd->result = (DID_OK << 16) | scsi_status; + break; + case MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN: + scmd->result = (DID_OK << 16) | scsi_status; + if (sense_state == MPI3_SCSI_STATE_SENSE_VALID) + break; + if (xfer_count < scmd->underflow) { + if (scsi_status == SAM_STAT_BUSY) + scmd->result = SAM_STAT_BUSY; + else + scmd->result = DID_SOFT_ERROR << 16; + } else if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) || + (sense_state != MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE)) + scmd->result = DID_SOFT_ERROR << 16; + else if (scsi_state & MPI3_SCSI_STATE_TERMINATED) + scmd->result = DID_RESET << 16; + break; + case MPI3_IOCSTATUS_SCSI_DATA_OVERRUN: + scsi_set_resid(scmd, 0); + fallthrough; + case MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR: + case MPI3_IOCSTATUS_SUCCESS: + scmd->result = (DID_OK << 16) | scsi_status; + if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) || + (sense_state == MPI3_SCSI_STATE_SENSE_FAILED) || + (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY)) + scmd->result = DID_SOFT_ERROR << 16; + else if (scsi_state & MPI3_SCSI_STATE_TERMINATED) + scmd->result = DID_RESET << 16; + break; + case MPI3_IOCSTATUS_EEDP_GUARD_ERROR: + case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR: + case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR: + mpi3mr_map_eedp_error(scmd, ioc_status); + break; + case MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR: + case MPI3_IOCSTATUS_INVALID_FUNCTION: + case MPI3_IOCSTATUS_INVALID_SGL: + case MPI3_IOCSTATUS_INTERNAL_ERROR: + case MPI3_IOCSTATUS_INVALID_FIELD: + case MPI3_IOCSTATUS_INVALID_STATE: + case MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR: + case MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED: + case MPI3_IOCSTATUS_INSUFFICIENT_POWER: + default: + scmd->result = DID_SOFT_ERROR << 16; + break; + } + + if (scmd->result != (DID_OK << 16) && (scmd->cmnd[0] != ATA_12) && + (scmd->cmnd[0] != ATA_16)) { + ioc_info(mrioc, "%s :scmd->result 0x%x\n", __func__, + scmd->result); + scsi_print_command(scmd); + ioc_info(mrioc, + "%s :Command issued to handle 0x%02x returned with error 0x%04x loginfo 0x%08x, qid %d\n", + __func__, dev_handle, ioc_status, ioc_loginfo, + priv->req_q_idx + 1); + ioc_info(mrioc, + " host_tag %d scsi_state 0x%02x scsi_status 0x%02x, xfer_cnt %d resp_data 0x%x\n", + host_tag, scsi_state, scsi_status, xfer_count, resp_data); + if (sense_buf) { + scsi_normalize_sense(sense_buf, sense_count, &sshdr); + ioc_info(mrioc, + "%s :sense_count 0x%x, sense_key 0x%x ASC 0x%x, ASCQ 0x%x\n", + __func__, sense_count, sshdr.sense_key, + sshdr.asc, sshdr.ascq); + } + } +out_success: + if (priv->meta_sg_valid) { + dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd), + scsi_prot_sg_count(scmd), scmd->sc_data_direction); + } + mpi3mr_clear_scmd_priv(mrioc, scmd); + scsi_dma_unmap(scmd); + scmd->scsi_done(scmd); +out: + if (sense_buf) + mpi3mr_repost_sense_buf(mrioc, + le64_to_cpu(scsi_reply->sense_data_buffer_address)); +} + +/** + * mpi3mr_get_chain_idx - get free chain buffer index + * @mrioc: Adapter instance reference + * + * Try to get a free chain buffer index from the free pool. + * + * Return: -1 on failure or the free chain buffer index + */ +static int mpi3mr_get_chain_idx(struct mpi3mr_ioc *mrioc) +{ + u8 retry_count = 5; + int cmd_idx = -1; + + do { + spin_lock(&mrioc->chain_buf_lock); + cmd_idx = find_first_zero_bit(mrioc->chain_bitmap, + mrioc->chain_buf_count); + if (cmd_idx < mrioc->chain_buf_count) { + set_bit(cmd_idx, mrioc->chain_bitmap); + spin_unlock(&mrioc->chain_buf_lock); + break; + } + spin_unlock(&mrioc->chain_buf_lock); + cmd_idx = -1; + } while (retry_count--); + return cmd_idx; +} + +/** + * mpi3mr_prepare_sg_scmd - build scatter gather list + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * @scsiio_req: MPI3 SCSI IO request + * + * This function maps SCSI command's data and protection SGEs to + * MPI request SGEs. If required additional 4K chain buffer is + * used to send the SGEs. + * + * Return: 0 on success, -ENOMEM on dma_map_sg failure + */ +static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) +{ + dma_addr_t chain_dma; + struct scatterlist *sg_scmd; + void *sg_local, *chain; + u32 chain_length; + int sges_left, chain_idx; + u32 sges_in_segment; + u8 simple_sgl_flags; + u8 simple_sgl_flags_last; + u8 last_chain_sgl_flags; + struct chain_element *chain_req; + struct scmd_priv *priv = NULL; + u32 meta_sg = le32_to_cpu(scsiio_req->flags) & + MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI; + + priv = scsi_cmd_priv(scmd); + + simple_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | + MPI3_SGE_FLAGS_DLAS_SYSTEM; + simple_sgl_flags_last = simple_sgl_flags | + MPI3_SGE_FLAGS_END_OF_LIST; + last_chain_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN | + MPI3_SGE_FLAGS_DLAS_SYSTEM; + + if (meta_sg) + sg_local = &scsiio_req->sgl[MPI3_SCSIIO_METASGL_INDEX]; + else + sg_local = &scsiio_req->sgl; + + if (!scsiio_req->data_length && !meta_sg) { + mpi3mr_build_zero_len_sge(sg_local); + return 0; + } + + if (meta_sg) { + sg_scmd = scsi_prot_sglist(scmd); + sges_left = dma_map_sg(&mrioc->pdev->dev, + scsi_prot_sglist(scmd), + scsi_prot_sg_count(scmd), + scmd->sc_data_direction); + priv->meta_sg_valid = 1; /* To unmap meta sg DMA */ + } else { + sg_scmd = scsi_sglist(scmd); + sges_left = scsi_dma_map(scmd); + } + + if (sges_left < 0) { + sdev_printk(KERN_ERR, scmd->device, + "scsi_dma_map failed: request for %d bytes!\n", + scsi_bufflen(scmd)); + return -ENOMEM; + } + if (sges_left > MPI3MR_SG_DEPTH) { + sdev_printk(KERN_ERR, scmd->device, + "scsi_dma_map returned unsupported sge count %d!\n", + sges_left); + return -ENOMEM; + } + + sges_in_segment = (mrioc->facts.op_req_sz - + offsetof(struct mpi3_scsi_io_request, sgl)) / sizeof(struct mpi3_sge_common); + + if (scsiio_req->sgl[0].eedp.flags == + MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED && !meta_sg) { + sg_local += sizeof(struct mpi3_sge_common); + sges_in_segment--; + /* Reserve 1st segment (scsiio_req->sgl[0]) for eedp */ + } + + if (scsiio_req->msg_flags == + MPI3_SCSIIO_MSGFLAGS_METASGL_VALID && !meta_sg) { + sges_in_segment--; + /* Reserve last segment (scsiio_req->sgl[3]) for meta sg */ + } + + if (meta_sg) + sges_in_segment = 1; + + if (sges_left <= sges_in_segment) + goto fill_in_last_segment; + + /* fill in main message segment when there is a chain following */ + while (sges_in_segment > 1) { + mpi3mr_add_sg_single(sg_local, simple_sgl_flags, + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += sizeof(struct mpi3_sge_common); + sges_left--; + sges_in_segment--; + } + + chain_idx = mpi3mr_get_chain_idx(mrioc); + if (chain_idx < 0) + return -1; + chain_req = &mrioc->chain_sgl_list[chain_idx]; + if (meta_sg) + priv->meta_chain_idx = chain_idx; + else + priv->chain_idx = chain_idx; + + chain = chain_req->addr; + chain_dma = chain_req->dma_addr; + sges_in_segment = sges_left; + chain_length = sges_in_segment * sizeof(struct mpi3_sge_common); + + mpi3mr_add_sg_single(sg_local, last_chain_sgl_flags, + chain_length, chain_dma); + + sg_local = chain; + +fill_in_last_segment: + while (sges_left > 0) { + if (sges_left == 1) + mpi3mr_add_sg_single(sg_local, + simple_sgl_flags_last, sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + else + mpi3mr_add_sg_single(sg_local, simple_sgl_flags, + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += sizeof(struct mpi3_sge_common); + sges_left--; + } + + return 0; +} + +/** + * mpi3mr_build_sg_scmd - build scatter gather list for SCSI IO + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * @scsiio_req: MPI3 SCSI IO request + * + * This function calls mpi3mr_prepare_sg_scmd for constructing + * both data SGEs and protection information SGEs in the MPI + * format from the SCSI Command as appropriate . + * + * Return: return value of mpi3mr_prepare_sg_scmd. + */ +static int mpi3mr_build_sg_scmd(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) +{ + int ret; + + ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req); + if (ret) + return ret; + + if (scsiio_req->msg_flags == MPI3_SCSIIO_MSGFLAGS_METASGL_VALID) { + /* There is a valid meta sg */ + scsiio_req->flags |= + cpu_to_le32(MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI); + ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req); + } + + return ret; +} + +/** + * mpi3mr_print_response_code - print TM response as a string + * @mrioc: Adapter instance reference + * @resp_code: TM response code + * + * Print TM response code as a readable string. + * + * Return: Nothing. + */ +static void mpi3mr_print_response_code(struct mpi3mr_ioc *mrioc, u8 resp_code) +{ + char *desc; + + switch (resp_code) { + case MPI3MR_RSP_TM_COMPLETE: + desc = "task management request completed"; + break; + case MPI3MR_RSP_INVALID_FRAME: + desc = "invalid frame"; + break; + case MPI3MR_RSP_TM_NOT_SUPPORTED: + desc = "task management request not supported"; + break; + case MPI3MR_RSP_TM_FAILED: + desc = "task management request failed"; + break; + case MPI3MR_RSP_TM_SUCCEEDED: + desc = "task management request succeeded"; + break; + case MPI3MR_RSP_TM_INVALID_LUN: + desc = "invalid lun"; + break; + case MPI3MR_RSP_TM_OVERLAPPED_TAG: + desc = "overlapped tag attempted"; + break; + case MPI3MR_RSP_IO_QUEUED_ON_IOC: + desc = "task queued, however not sent to target"; + break; + default: + desc = "unknown"; + break; + } + ioc_info(mrioc, "%s :response_code(0x%01x): %s\n", __func__, + resp_code, desc); +} + +/** + * mpi3mr_issue_tm - Issue Task Management request + * @mrioc: Adapter instance reference + * @tm_type: Task Management type + * @handle: Device handle + * @lun: lun ID + * @htag: Host tag of the TM request + * @drv_cmd: Internal command tracker + * @resp_code: Response code place holder + * @cmd_priv: SCSI command private data + * + * Issues a Task Management Request to the controller for a + * specified target, lun and command and wait for its completion + * and check TM response. Recover the TM if it timed out by + * issuing controller reset. + * + * Return: 0 on success, non-zero on errors + */ +static int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type, + u16 handle, uint lun, u16 htag, ulong timeout, + struct mpi3mr_drv_cmd *drv_cmd, + u8 *resp_code, struct scmd_priv *cmd_priv) +{ + struct mpi3_scsi_task_mgmt_request tm_req; + struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL; + int retval = 0; + struct mpi3mr_tgt_dev *tgtdev = NULL; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; + struct op_req_qinfo *op_req_q = NULL; + + ioc_info(mrioc, "%s :Issue TM: TM type (0x%x) for devhandle 0x%04x\n", + __func__, tm_type, handle); + if (mrioc->unrecoverable) { + retval = -1; + ioc_err(mrioc, "%s :Issue TM: Unrecoverable controller\n", + __func__); + goto out; + } + + memset(&tm_req, 0, sizeof(tm_req)); + mutex_lock(&drv_cmd->mutex); + if (drv_cmd->state & MPI3MR_CMD_PENDING) { + retval = -1; + ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__); + mutex_unlock(&drv_cmd->mutex); + goto out; + } + if (mrioc->reset_in_progress) { + retval = -1; + ioc_err(mrioc, "%s :Issue TM: Reset in progress\n", __func__); + mutex_unlock(&drv_cmd->mutex); + goto out; + } + + drv_cmd->state = MPI3MR_CMD_PENDING; + drv_cmd->is_waiting = 1; + drv_cmd->callback = NULL; + tm_req.dev_handle = cpu_to_le16(handle); + tm_req.task_type = tm_type; + tm_req.host_tag = cpu_to_le16(htag); + + int_to_scsilun(lun, (struct scsi_lun *)tm_req.lun); + tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT; + + tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle); + if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata) { + scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) + tgtdev->starget->hostdata; + atomic_inc(&scsi_tgt_priv_data->block_io); + } + if (cmd_priv) { + op_req_q = &mrioc->req_qinfo[cmd_priv->req_q_idx]; + tm_req.task_host_tag = cpu_to_le16(cmd_priv->host_tag); + tm_req.task_request_queue_id = cpu_to_le16(op_req_q->qid); + } + if (tgtdev && (tgtdev->dev_type == MPI3_DEVICE_DEVFORM_PCIE)) { + if (cmd_priv && tgtdev->dev_spec.pcie_inf.abort_to) + timeout = tgtdev->dev_spec.pcie_inf.abort_to; + else if (!cmd_priv && tgtdev->dev_spec.pcie_inf.reset_to) + timeout = tgtdev->dev_spec.pcie_inf.reset_to; + } + + init_completion(&drv_cmd->done); + retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1); + if (retval) { + ioc_err(mrioc, "%s :Issue TM: Admin Post failed\n", __func__); + goto out_unlock; + } + wait_for_completion_timeout(&drv_cmd->done, (timeout * HZ)); + + if (!(drv_cmd->state & MPI3MR_CMD_COMPLETE)) { + ioc_err(mrioc, "%s :Issue TM: command timed out\n", __func__); + drv_cmd->is_waiting = 0; + retval = -1; + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_TM_TIMEOUT, 1); + goto out_unlock; + } + + if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID) + tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply; + + if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) { + ioc_err(mrioc, + "%s :Issue TM: handle(0x%04x) Failed ioc_status(0x%04x) Loginfo(0x%08x)\n", + __func__, handle, drv_cmd->ioc_status, + drv_cmd->ioc_loginfo); + retval = -1; + goto out_unlock; + } + + if (!tm_reply) { + ioc_err(mrioc, "%s :Issue TM: No TM Reply message\n", __func__); + retval = -1; + goto out_unlock; + } + + *resp_code = le32_to_cpu(tm_reply->response_data) & + MPI3MR_RI_MASK_RESPCODE; + switch (*resp_code) { + case MPI3MR_RSP_TM_SUCCEEDED: + case MPI3MR_RSP_TM_COMPLETE: + break; + case MPI3MR_RSP_IO_QUEUED_ON_IOC: + if (tm_type != MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK) + retval = -1; + break; + default: + retval = -1; + break; + } + + ioc_info(mrioc, + "%s :Issue TM: Completed TM type (0x%x) handle(0x%04x) ", + __func__, tm_type, handle); + ioc_info(mrioc, + "with ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n", + drv_cmd->ioc_status, drv_cmd->ioc_loginfo, + le32_to_cpu(tm_reply->termination_count)); + mpi3mr_print_response_code(mrioc, *resp_code); + +out_unlock: + drv_cmd->state = MPI3MR_CMD_NOTUSED; + mutex_unlock(&drv_cmd->mutex); + if (scsi_tgt_priv_data) + atomic_dec_if_positive(&scsi_tgt_priv_data->block_io); + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); + if (!retval) { + /* + * Flush all IRQ handlers by calling synchronize_irq(). + * mpi3mr_ioc_disable_intr() takes care of it. + */ + mpi3mr_ioc_disable_intr(mrioc); + mpi3mr_ioc_enable_intr(mrioc); + } +out: + return retval; +} + +/** + * mpi3mr_bios_param - BIOS param callback + * @sdev: SCSI device reference + * @bdev: Block device reference + * @capacity: Capacity in logical sectors + * @params: Parameter array + * + * Just the parameters with heads/secots/cylinders. + * + * Return: 0 always + */ +static int mpi3mr_bios_param(struct scsi_device *sdev, + struct block_device *bdev, sector_t capacity, int params[]) +{ + int heads; + int sectors; + sector_t cylinders; + ulong dummy; + + heads = 64; + sectors = 32; + + dummy = heads * sectors; + cylinders = capacity; + sector_div(cylinders, dummy); + + if ((ulong)capacity >= 0x200000) { + heads = 255; + sectors = 63; + dummy = heads * sectors; + cylinders = capacity; + sector_div(cylinders, dummy); + } + + params[0] = heads; + params[1] = sectors; + params[2] = cylinders; + return 0; +} + +/** + * mpi3mr_map_queues - Map queues callback handler + * @shost: SCSI host reference + * + * Call the blk_mq_pci_map_queues with from which operational + * queue the mapping has to be done + * + * Return: return of blk_mq_pci_map_queues + */ +static int mpi3mr_map_queues(struct Scsi_Host *shost) +{ + struct mpi3mr_ioc *mrioc = shost_priv(shost); + + return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT], + mrioc->pdev, mrioc->op_reply_q_offset); +} + +/** + * mpi3mr_get_fw_pending_ios - Calculate pending I/O count + * @mrioc: Adapter instance reference + * + * Calculate the pending I/Os for the controller and return. + * + * Return: Number of pending I/Os + */ +static inline int mpi3mr_get_fw_pending_ios(struct mpi3mr_ioc *mrioc) +{ + u16 i; + uint pend_ios = 0; + + for (i = 0; i < mrioc->num_op_reply_q; i++) + pend_ios += atomic_read(&mrioc->op_reply_qinfo[i].pend_ios); + return pend_ios; +} + +/** + * mpi3mr_print_pending_host_io - print pending I/Os + * @mrioc: Adapter instance reference + * + * Print number of pending I/Os and each I/O details prior to + * reset for debug purpose. + * + * Return: Nothing + */ +static void mpi3mr_print_pending_host_io(struct mpi3mr_ioc *mrioc) +{ + struct Scsi_Host *shost = mrioc->shost; + + ioc_info(mrioc, "%s :Pending commands prior to reset: %d\n", + __func__, mpi3mr_get_fw_pending_ios(mrioc)); + blk_mq_tagset_busy_iter(&shost->tag_set, + mpi3mr_print_scmd, (void *)mrioc); +} + +/** + * mpi3mr_wait_for_host_io - block for I/Os to complete + * @mrioc: Adapter instance reference + * @timeout: time out in seconds + * Waits for pending I/Os for the given adapter to complete or + * to hit the timeout. + * + * Return: Nothing + */ +void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout) +{ + enum mpi3mr_iocstate iocstate; + int i = 0; + + iocstate = mpi3mr_get_iocstate(mrioc); + if (iocstate != MRIOC_STATE_READY) + return; + + if (!mpi3mr_get_fw_pending_ios(mrioc)) + return; + ioc_info(mrioc, + "%s :Waiting for %d seconds prior to reset for %d I/O\n", + __func__, timeout, mpi3mr_get_fw_pending_ios(mrioc)); + + for (i = 0; i < timeout; i++) { + if (!mpi3mr_get_fw_pending_ios(mrioc)) + break; + iocstate = mpi3mr_get_iocstate(mrioc); + if (iocstate != MRIOC_STATE_READY) + break; + msleep(1000); + } + + ioc_info(mrioc, "%s :Pending I/Os after wait is: %d\n", __func__, + mpi3mr_get_fw_pending_ios(mrioc)); +} + +/** + * mpi3mr_eh_host_reset - Host reset error handling callback + * @scmd: SCSI command reference + * + * Issue controller reset if the scmd is for a Physical Device, + * if the scmd is for RAID volume, then wait for + * MPI3MR_RAID_ERRREC_RESET_TIMEOUT and checke whether any + * pending I/Os prior to issuing reset to the controller. + * + * Return: SUCCESS of successful reset else FAILED + */ +static int mpi3mr_eh_host_reset(struct scsi_cmnd *scmd) +{ + struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host); + struct mpi3mr_stgt_priv_data *stgt_priv_data; + struct mpi3mr_sdev_priv_data *sdev_priv_data; + u8 dev_type = MPI3_DEVICE_DEVFORM_VD; + int retval = FAILED, ret; + + sdev_priv_data = scmd->device->hostdata; + if (sdev_priv_data && sdev_priv_data->tgt_priv_data) { + stgt_priv_data = sdev_priv_data->tgt_priv_data; + dev_type = stgt_priv_data->dev_type; + } + + if (dev_type == MPI3_DEVICE_DEVFORM_VD) { + mpi3mr_wait_for_host_io(mrioc, + MPI3MR_RAID_ERRREC_RESET_TIMEOUT); + if (!mpi3mr_get_fw_pending_ios(mrioc)) { + retval = SUCCESS; + goto out; + } + } + + mpi3mr_print_pending_host_io(mrioc); + ret = mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_EH_HOS, 1); + if (ret) + goto out; + + retval = SUCCESS; +out: + sdev_printk(KERN_INFO, scmd->device, + "Host reset is %s for scmd(%p)\n", + ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + return retval; +} + +/** + * mpi3mr_eh_target_reset - Target reset error handling callback + * @scmd: SCSI command reference + * + * Issue Target reset Task Management and verify the scmd is + * terminated successfully and return status accordingly. + * + * Return: SUCCESS of successful termination of the scmd else + * FAILED + */ +static int mpi3mr_eh_target_reset(struct scsi_cmnd *scmd) +{ + struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host); + struct mpi3mr_stgt_priv_data *stgt_priv_data; + struct mpi3mr_sdev_priv_data *sdev_priv_data; + u16 dev_handle; + u8 resp_code = 0; + int retval = FAILED, ret = 0; + + sdev_printk(KERN_INFO, scmd->device, + "Attempting Target Reset! scmd(%p)\n", scmd); + scsi_print_command(scmd); + + sdev_priv_data = scmd->device->hostdata; + if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) { + sdev_printk(KERN_INFO, scmd->device, + "SCSI device is not available\n"); + retval = SUCCESS; + goto out; + } + + stgt_priv_data = sdev_priv_data->tgt_priv_data; + dev_handle = stgt_priv_data->dev_handle; + sdev_printk(KERN_INFO, scmd->device, + "Target Reset is issued to handle(0x%04x)\n", + dev_handle); + + ret = mpi3mr_issue_tm(mrioc, + MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET, dev_handle, + sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS, + MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, NULL); + + if (ret) + goto out; + + retval = SUCCESS; +out: + sdev_printk(KERN_INFO, scmd->device, + "Target reset is %s for scmd(%p)\n", + ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + return retval; +} + +/** + * mpi3mr_eh_dev_reset- Device reset error handling callback + * @scmd: SCSI command reference + * + * Issue lun reset Task Management and verify the scmd is + * terminated successfully and return status accordingly. + * + * Return: SUCCESS of successful termination of the scmd else + * FAILED + */ +static int mpi3mr_eh_dev_reset(struct scsi_cmnd *scmd) +{ + struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host); + struct mpi3mr_stgt_priv_data *stgt_priv_data; + struct mpi3mr_sdev_priv_data *sdev_priv_data; + u16 dev_handle; + u8 resp_code = 0; + int retval = FAILED, ret = 0; + + sdev_printk(KERN_INFO, scmd->device, + "Attempting Device(lun) Reset! scmd(%p)\n", scmd); + scsi_print_command(scmd); + + sdev_priv_data = scmd->device->hostdata; + if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) { + sdev_printk(KERN_INFO, scmd->device, + "SCSI device is not available\n"); + retval = SUCCESS; + goto out; + } + + stgt_priv_data = sdev_priv_data->tgt_priv_data; + dev_handle = stgt_priv_data->dev_handle; + sdev_printk(KERN_INFO, scmd->device, + "Device(lun) Reset is issued to handle(0x%04x)\n", dev_handle); + + ret = mpi3mr_issue_tm(mrioc, + MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, dev_handle, + sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS, + MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, NULL); + + if (ret) + goto out; + + retval = SUCCESS; +out: + sdev_printk(KERN_INFO, scmd->device, + "Device(lun) reset is %s for scmd(%p)\n", + ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + return retval; +} + +/** + * mpi3mr_scan_start - Scan start callback handler + * @shost: SCSI host reference + * + * Issue port enable request asynchronously. + * + * Return: Nothing + */ +static void mpi3mr_scan_start(struct Scsi_Host *shost) +{ + struct mpi3mr_ioc *mrioc = shost_priv(shost); + + mrioc->scan_started = 1; + ioc_info(mrioc, "%s :Issuing Port Enable\n", __func__); + if (mpi3mr_issue_port_enable(mrioc, 1)) { + ioc_err(mrioc, "%s :Issuing port enable failed\n", __func__); + mrioc->scan_started = 0; + mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR; + } +} + +/** + * mpi3mr_scan_finished - Scan finished callback handler + * @shost: SCSI host reference + * @time: Jiffies from the scan start + * + * Checks whether the port enable is completed or timedout or + * failed and set the scan status accordingly after taking any + * recovery if required. + * + * Return: 1 on scan finished or timed out, 0 for in progress + */ +static int mpi3mr_scan_finished(struct Scsi_Host *shost, + unsigned long time) +{ + struct mpi3mr_ioc *mrioc = shost_priv(shost); + u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT; + + if (time >= (pe_timeout * HZ)) { + mrioc->init_cmds.is_waiting = 0; + mrioc->init_cmds.callback = NULL; + mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; + ioc_err(mrioc, "%s :port enable request timed out\n", __func__); + mrioc->is_driver_loading = 0; + mpi3mr_soft_reset_handler(mrioc, + MPI3MR_RESET_FROM_PE_TIMEOUT, 1); + } + + if (mrioc->scan_failed) { + ioc_err(mrioc, + "%s :port enable failed with (ioc_status=0x%08x)\n", + __func__, mrioc->scan_failed); + mrioc->is_driver_loading = 0; + mrioc->stop_drv_processing = 1; + return 1; + } + + if (mrioc->scan_started) + return 0; + ioc_info(mrioc, "%s :port enable: SUCCESS\n", __func__); + mpi3mr_start_watchdog(mrioc); + mrioc->is_driver_loading = 0; + + return 1; +} + +/** + * mpi3mr_slave_destroy - Slave destroy callback handler + * @sdev: SCSI device reference + * + * Cleanup and free per device(lun) private data. + * + * Return: Nothing. + */ +static void mpi3mr_slave_destroy(struct scsi_device *sdev) +{ + struct Scsi_Host *shost; + struct mpi3mr_ioc *mrioc; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_tgt_dev *tgt_dev; + unsigned long flags; + struct scsi_target *starget; + + if (!sdev->hostdata) + return; + + starget = scsi_target(sdev); + shost = dev_to_shost(&starget->dev); + mrioc = shost_priv(shost); + scsi_tgt_priv_data = starget->hostdata; + + scsi_tgt_priv_data->num_luns--; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); + if (tgt_dev && (!scsi_tgt_priv_data->num_luns)) + tgt_dev->starget = NULL; + if (tgt_dev) + mpi3mr_tgtdev_put(tgt_dev); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + + kfree(sdev->hostdata); + sdev->hostdata = NULL; +} + +/** + * mpi3mr_target_destroy - Target destroy callback handler + * @starget: SCSI target reference + * + * Cleanup and free per target private data. + * + * Return: Nothing. + */ +static void mpi3mr_target_destroy(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct mpi3mr_ioc *mrioc; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_tgt_dev *tgt_dev; + unsigned long flags; + + if (!starget->hostdata) + return; + + shost = dev_to_shost(&starget->dev); + mrioc = shost_priv(shost); + scsi_tgt_priv_data = starget->hostdata; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgt_dev = __mpi3mr_get_tgtdev_from_tgtpriv(mrioc, scsi_tgt_priv_data); + if (tgt_dev && (tgt_dev->starget == starget) && + (tgt_dev->perst_id == starget->id)) + tgt_dev->starget = NULL; + if (tgt_dev) { + scsi_tgt_priv_data->tgt_dev = NULL; + scsi_tgt_priv_data->perst_id = 0; + mpi3mr_tgtdev_put(tgt_dev); + mpi3mr_tgtdev_put(tgt_dev); + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + + kfree(starget->hostdata); + starget->hostdata = NULL; +} + +/** + * mpi3mr_slave_configure - Slave configure callback handler + * @sdev: SCSI device reference + * + * Configure queue depth, max hardware sectors and virt boundary + * as required + * + * Return: 0 always. + */ +static int mpi3mr_slave_configure(struct scsi_device *sdev) +{ + struct scsi_target *starget; + struct Scsi_Host *shost; + struct mpi3mr_ioc *mrioc; + struct mpi3mr_tgt_dev *tgt_dev; + unsigned long flags; + int retval = 0; + + starget = scsi_target(sdev); + shost = dev_to_shost(&starget->dev); + mrioc = shost_priv(shost); + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + if (!tgt_dev) + return -ENXIO; + + mpi3mr_change_queue_depth(sdev, tgt_dev->q_depth); + switch (tgt_dev->dev_type) { + case MPI3_DEVICE_DEVFORM_PCIE: + /*The block layer hw sector size = 512*/ + blk_queue_max_hw_sectors(sdev->request_queue, + tgt_dev->dev_spec.pcie_inf.mdts / 512); + blk_queue_virt_boundary(sdev->request_queue, + ((1 << tgt_dev->dev_spec.pcie_inf.pgsz) - 1)); + break; + default: + break; + } + + mpi3mr_tgtdev_put(tgt_dev); + + return retval; +} + +/** + * mpi3mr_slave_alloc -Slave alloc callback handler + * @sdev: SCSI device reference + * + * Allocate per device(lun) private data and initialize it. + * + * Return: 0 on success -ENOMEM on memory allocation failure. + */ +static int mpi3mr_slave_alloc(struct scsi_device *sdev) +{ + struct Scsi_Host *shost; + struct mpi3mr_ioc *mrioc; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_tgt_dev *tgt_dev; + struct mpi3mr_sdev_priv_data *scsi_dev_priv_data; + unsigned long flags; + struct scsi_target *starget; + int retval = 0; + + starget = scsi_target(sdev); + shost = dev_to_shost(&starget->dev); + mrioc = shost_priv(shost); + scsi_tgt_priv_data = starget->hostdata; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); + + if (tgt_dev) { + if (tgt_dev->starget == NULL) + tgt_dev->starget = starget; + mpi3mr_tgtdev_put(tgt_dev); + retval = 0; + } else { + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + return -ENXIO; + } + + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + + scsi_dev_priv_data = kzalloc(sizeof(*scsi_dev_priv_data), GFP_KERNEL); + if (!scsi_dev_priv_data) + return -ENOMEM; + + scsi_dev_priv_data->lun_id = sdev->lun; + scsi_dev_priv_data->tgt_priv_data = scsi_tgt_priv_data; + sdev->hostdata = scsi_dev_priv_data; + + scsi_tgt_priv_data->num_luns++; + + return retval; +} + +/** + * mpi3mr_target_alloc - Target alloc callback handler + * @starget: SCSI target reference + * + * Allocate per target private data and initialize it. + * + * Return: 0 on success -ENOMEM on memory allocation failure. + */ +static int mpi3mr_target_alloc(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct mpi3mr_ioc *mrioc = shost_priv(shost); + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_tgt_dev *tgt_dev; + unsigned long flags; + int retval = 0; + + scsi_tgt_priv_data = kzalloc(sizeof(*scsi_tgt_priv_data), GFP_KERNEL); + if (!scsi_tgt_priv_data) + return -ENOMEM; + + starget->hostdata = scsi_tgt_priv_data; + scsi_tgt_priv_data->starget = starget; + scsi_tgt_priv_data->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); + if (tgt_dev && !tgt_dev->is_hidden) { + starget->hostdata = scsi_tgt_priv_data; + scsi_tgt_priv_data->starget = starget; + scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle; + scsi_tgt_priv_data->perst_id = tgt_dev->perst_id; + scsi_tgt_priv_data->dev_type = tgt_dev->dev_type; + scsi_tgt_priv_data->tgt_dev = tgt_dev; + tgt_dev->starget = starget; + atomic_set(&scsi_tgt_priv_data->block_io, 0); + retval = 0; + } else { + kfree(scsi_tgt_priv_data); + retval = -ENXIO; + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + + return retval; +} + +/** + * mpi3mr_check_return_unmap - Whether an unmap is allowed + * @mrioc: Adapter instance reference + * @scmd: SCSI Command reference + * + * The controller hardware cannot handle certain unmap commands + * for NVMe drives, this routine checks those and return true + * and completes the SCSI command with proper status and sense + * data. + * + * Return: TRUE for not allowed unmap, FALSE otherwise. + */ +static bool mpi3mr_check_return_unmap(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd) +{ + unsigned char *buf; + u16 param_len, desc_len; + + param_len = get_unaligned_be16(scmd->cmnd + 7); + + if (!param_len) { + ioc_warn(mrioc, + "%s: cdb received with zero parameter length\n", + __func__); + scsi_print_command(scmd); + scmd->result = DID_OK << 16; + scmd->scsi_done(scmd); + return true; + } + + if (param_len < 24) { + ioc_warn(mrioc, + "%s: cdb received with invalid param_len: %d\n", + __func__, param_len); + scsi_print_command(scmd); + scmd->result = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, + 0x1A, 0); + scmd->scsi_done(scmd); + return true; + } + if (param_len != scsi_bufflen(scmd)) { + ioc_warn(mrioc, + "%s: cdb received with param_len: %d bufflen: %d\n", + __func__, param_len, scsi_bufflen(scmd)); + scsi_print_command(scmd); + scmd->result = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, + 0x1A, 0); + scmd->scsi_done(scmd); + return true; + } + buf = kzalloc(scsi_bufflen(scmd), GFP_ATOMIC); + if (!buf) { + scsi_print_command(scmd); + scmd->result = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, + 0x55, 0x03); + scmd->scsi_done(scmd); + return true; + } + scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd)); + desc_len = get_unaligned_be16(&buf[2]); + + if (desc_len < 16) { + ioc_warn(mrioc, + "%s: Invalid descriptor length in param list: %d\n", + __func__, desc_len); + scsi_print_command(scmd); + scmd->result = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, + 0x26, 0); + scmd->scsi_done(scmd); + kfree(buf); + return true; + } + + if (param_len > (desc_len + 8)) { + scsi_print_command(scmd); + ioc_warn(mrioc, + "%s: Truncating param_len(%d) to desc_len+8(%d)\n", + __func__, param_len, (desc_len + 8)); + param_len = desc_len + 8; + put_unaligned_be16(param_len, scmd->cmnd + 7); + scsi_print_command(scmd); + } + + kfree(buf); + return false; +} + +/** + * mpi3mr_allow_scmd_to_fw - Command is allowed during shutdown + * @scmd: SCSI Command reference + * + * Checks whether a cdb is allowed during shutdown or not. + * + * Return: TRUE for allowed commands, FALSE otherwise. + */ + +inline bool mpi3mr_allow_scmd_to_fw(struct scsi_cmnd *scmd) +{ + switch (scmd->cmnd[0]) { + case SYNCHRONIZE_CACHE: + case START_STOP: + return true; + default: + return false; + } +} + +/** + * mpi3mr_qcmd - I/O request despatcher + * @shost: SCSI Host reference + * @scmd: SCSI Command reference + * + * Issues the SCSI Command as an MPI3 request. + * + * Return: 0 on successful queueing of the request or if the + * request is completed with failure. + * SCSI_MLQUEUE_DEVICE_BUSY when the device is busy. + * SCSI_MLQUEUE_HOST_BUSY when the host queue is full. + */ +static int mpi3mr_qcmd(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + struct mpi3mr_ioc *mrioc = shost_priv(shost); + struct mpi3mr_stgt_priv_data *stgt_priv_data; + struct mpi3mr_sdev_priv_data *sdev_priv_data; + struct scmd_priv *scmd_priv_data = NULL; + struct mpi3_scsi_io_request *scsiio_req = NULL; + struct op_req_qinfo *op_req_q = NULL; + int retval = 0; + u16 dev_handle; + u16 host_tag; + u32 scsiio_flags = 0; + struct request *rq = scmd->request; + int iprio_class; + + sdev_priv_data = scmd->device->hostdata; + if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + goto out; + } + + if (mrioc->stop_drv_processing && + !(mpi3mr_allow_scmd_to_fw(scmd))) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + goto out; + } + + if (mrioc->reset_in_progress) { + retval = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + + stgt_priv_data = sdev_priv_data->tgt_priv_data; + + dev_handle = stgt_priv_data->dev_handle; + if (dev_handle == MPI3MR_INVALID_DEV_HANDLE) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + goto out; + } + if (stgt_priv_data->dev_removed) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + goto out; + } + + if (atomic_read(&stgt_priv_data->block_io)) { + if (mrioc->stop_drv_processing) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + goto out; + } + retval = SCSI_MLQUEUE_DEVICE_BUSY; + goto out; + } + + if ((scmd->cmnd[0] == UNMAP) && + (stgt_priv_data->dev_type == MPI3_DEVICE_DEVFORM_PCIE) && + mpi3mr_check_return_unmap(mrioc, scmd)) + goto out; + + host_tag = mpi3mr_host_tag_for_scmd(mrioc, scmd); + if (host_tag == MPI3MR_HOSTTAG_INVALID) { + scmd->result = DID_ERROR << 16; + scmd->scsi_done(scmd); + goto out; + } + + if (scmd->sc_data_direction == DMA_FROM_DEVICE) + scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_READ; + else if (scmd->sc_data_direction == DMA_TO_DEVICE) + scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE; + else + scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER; + + scsiio_flags |= MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ; + + if (sdev_priv_data->ncq_prio_enable) { + iprio_class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); + if (iprio_class == IOPRIO_CLASS_RT) + scsiio_flags |= 1 << MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT; + } + + if (scmd->cmd_len > 16) + scsiio_flags |= MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16; + + scmd_priv_data = scsi_cmd_priv(scmd); + memset(scmd_priv_data->mpi3mr_scsiio_req, 0, MPI3MR_ADMIN_REQ_FRAME_SZ); + scsiio_req = (struct mpi3_scsi_io_request *)scmd_priv_data->mpi3mr_scsiio_req; + scsiio_req->function = MPI3_FUNCTION_SCSI_IO; + scsiio_req->host_tag = cpu_to_le16(host_tag); + + mpi3mr_setup_eedp(mrioc, scmd, scsiio_req); + + memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len); + scsiio_req->data_length = cpu_to_le32(scsi_bufflen(scmd)); + scsiio_req->dev_handle = cpu_to_le16(dev_handle); + scsiio_req->flags = cpu_to_le32(scsiio_flags); + int_to_scsilun(sdev_priv_data->lun_id, + (struct scsi_lun *)scsiio_req->lun); + + if (mpi3mr_build_sg_scmd(mrioc, scmd, scsiio_req)) { + mpi3mr_clear_scmd_priv(mrioc, scmd); + retval = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx]; + + if (mpi3mr_op_request_post(mrioc, op_req_q, + scmd_priv_data->mpi3mr_scsiio_req)) { + mpi3mr_clear_scmd_priv(mrioc, scmd); + retval = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + +out: + return retval; +} + +static struct scsi_host_template mpi3mr_driver_template = { + .module = THIS_MODULE, + .name = "MPI3 Storage Controller", + .proc_name = MPI3MR_DRIVER_NAME, + .queuecommand = mpi3mr_qcmd, + .target_alloc = mpi3mr_target_alloc, + .slave_alloc = mpi3mr_slave_alloc, + .slave_configure = mpi3mr_slave_configure, + .target_destroy = mpi3mr_target_destroy, + .slave_destroy = mpi3mr_slave_destroy, + .scan_finished = mpi3mr_scan_finished, + .scan_start = mpi3mr_scan_start, + .change_queue_depth = mpi3mr_change_queue_depth, + .eh_device_reset_handler = mpi3mr_eh_dev_reset, + .eh_target_reset_handler = mpi3mr_eh_target_reset, + .eh_host_reset_handler = mpi3mr_eh_host_reset, + .bios_param = mpi3mr_bios_param, + .map_queues = mpi3mr_map_queues, + .no_write_same = 1, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = MPI3MR_SG_DEPTH, + /* max xfer supported is 1M (2K in 512 byte sized sectors) + */ + .max_sectors = 2048, + .cmd_per_lun = MPI3MR_MAX_CMDS_LUN, + .track_queue_depth = 1, + .cmd_size = sizeof(struct scmd_priv), +}; + +/** + * mpi3mr_init_drv_cmd - Initialize internal command tracker + * @cmdptr: Internal command tracker + * @host_tag: Host tag used for the specific command + * + * Initialize the internal command tracker structure with + * specified host tag. + * + * Return: Nothing. + */ +static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr, + u16 host_tag) +{ + mutex_init(&cmdptr->mutex); + cmdptr->reply = NULL; + cmdptr->state = MPI3MR_CMD_NOTUSED; + cmdptr->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + cmdptr->host_tag = host_tag; +} + +/** + * osintfc_mrioc_security_status -Check controller secure status + * @pdev: PCI device instance + * + * Read the Device Serial Number capability from PCI config + * space and decide whether the controller is secure or not. + * + * Return: 0 on success, non-zero on failure. + */ +static int +osintfc_mrioc_security_status(struct pci_dev *pdev) +{ + u32 cap_data; + int base; + u32 ctlr_status; + u32 debug_status; + int retval = 0; + + base = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DSN); + if (!base) { + dev_err(&pdev->dev, + "%s: PCI_EXT_CAP_ID_DSN is not supported\n", __func__); + return -1; + } + + pci_read_config_dword(pdev, base + 4, &cap_data); + + debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK; + ctlr_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK; + + switch (ctlr_status) { + case MPI3MR_INVALID_DEVICE: + dev_err(&pdev->dev, + "%s: Non secure ctlr (Invalid) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", + __func__, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + retval = -1; + break; + case MPI3MR_CONFIG_SECURE_DEVICE: + if (!debug_status) + dev_info(&pdev->dev, + "%s: Config secure ctlr is detected\n", + __func__); + break; + case MPI3MR_HARD_SECURE_DEVICE: + break; + case MPI3MR_TAMPERED_DEVICE: + dev_err(&pdev->dev, + "%s: Non secure ctlr (Tampered) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", + __func__, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + retval = -1; + break; + default: + retval = -1; + break; + } + + if (!retval && debug_status) { + dev_err(&pdev->dev, + "%s: Non secure ctlr (Secure Dbg) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n", + __func__, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + retval = -1; + } + + return retval; +} + +/** + * mpi3mr_probe - PCI probe callback + * @pdev: PCI device instance + * @id: PCI device ID details + * + * controller initialization routine. Checks the security status + * of the controller and if it is invalid or tampered return the + * probe without initializing the controller. Otherwise, + * allocate per adapter instance through shost_priv and + * initialize controller specific data structures, initializae + * the controller hardware, add shost to the SCSI subsystem. + * + * Return: 0 on success, non-zero on failure. + */ + +static int +mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct mpi3mr_ioc *mrioc = NULL; + struct Scsi_Host *shost = NULL; + int retval = 0, i; + + if (osintfc_mrioc_security_status(pdev)) { + warn_non_secure_ctlr = 1; + return 1; /* For Invalid and Tampered device */ + } + + shost = scsi_host_alloc(&mpi3mr_driver_template, + sizeof(struct mpi3mr_ioc)); + if (!shost) { + retval = -ENODEV; + goto shost_failed; + } + + mrioc = shost_priv(shost); + mrioc->id = mrioc_ids++; + sprintf(mrioc->driver_name, "%s", MPI3MR_DRIVER_NAME); + sprintf(mrioc->name, "%s%d", mrioc->driver_name, mrioc->id); + INIT_LIST_HEAD(&mrioc->list); + spin_lock(&mrioc_list_lock); + list_add_tail(&mrioc->list, &mrioc_list); + spin_unlock(&mrioc_list_lock); + + spin_lock_init(&mrioc->admin_req_lock); + spin_lock_init(&mrioc->reply_free_queue_lock); + spin_lock_init(&mrioc->sbq_lock); + spin_lock_init(&mrioc->fwevt_lock); + spin_lock_init(&mrioc->tgtdev_lock); + spin_lock_init(&mrioc->watchdog_lock); + spin_lock_init(&mrioc->chain_buf_lock); + + INIT_LIST_HEAD(&mrioc->fwevt_list); + INIT_LIST_HEAD(&mrioc->tgtdev_list); + INIT_LIST_HEAD(&mrioc->delayed_rmhs_list); + + mutex_init(&mrioc->reset_mutex); + mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS); + mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS); + + for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) + mpi3mr_init_drv_cmd(&mrioc->dev_rmhs_cmds[i], + MPI3MR_HOSTTAG_DEVRMCMD_MIN + i); + + if (pdev->revision) + mrioc->enable_segqueue = true; + + init_waitqueue_head(&mrioc->reset_waitq); + mrioc->logging_level = logging_level; + mrioc->shost = shost; + mrioc->pdev = pdev; + + /* init shost parameters */ + shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH; + shost->max_lun = -1; + shost->unique_id = mrioc->id; + + shost->max_channel = 1; + shost->max_id = 0xFFFFFFFF; + + if (prot_mask >= 0) + scsi_host_set_prot(shost, prot_mask); + else { + prot_mask = SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE2_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION; + scsi_host_set_prot(shost, prot_mask); + } + + ioc_info(mrioc, + "%s :host protection capabilities enabled %s%s%s%s%s%s%s\n", + __func__, + (prot_mask & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "", + (prot_mask & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "", + (prot_mask & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "", + (prot_mask & SHOST_DIX_TYPE0_PROTECTION) ? " DIX0" : "", + (prot_mask & SHOST_DIX_TYPE1_PROTECTION) ? " DIX1" : "", + (prot_mask & SHOST_DIX_TYPE2_PROTECTION) ? " DIX2" : "", + (prot_mask & SHOST_DIX_TYPE3_PROTECTION) ? " DIX3" : ""); + + if (prot_guard_mask) + scsi_host_set_guard(shost, (prot_guard_mask & 3)); + else + scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); + + snprintf(mrioc->fwevt_worker_name, sizeof(mrioc->fwevt_worker_name), + "%s%d_fwevt_wrkr", mrioc->driver_name, mrioc->id); + mrioc->fwevt_worker_thread = alloc_ordered_workqueue( + mrioc->fwevt_worker_name, WQ_MEM_RECLAIM); + if (!mrioc->fwevt_worker_thread) { + ioc_err(mrioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + retval = -ENODEV; + goto out_fwevtthread_failed; + } + + mrioc->is_driver_loading = 1; + if (mpi3mr_init_ioc(mrioc, 0)) { + ioc_err(mrioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + retval = -ENODEV; + goto out_iocinit_failed; + } + + shost->nr_hw_queues = mrioc->num_op_reply_q; + shost->can_queue = mrioc->max_host_ios; + shost->sg_tablesize = MPI3MR_SG_DEPTH; + shost->max_id = mrioc->facts.max_perids; + + retval = scsi_add_host(shost, &pdev->dev); + if (retval) { + ioc_err(mrioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto addhost_failed; + } + + scsi_scan_host(shost); + return retval; + +addhost_failed: + mpi3mr_cleanup_ioc(mrioc, 0); +out_iocinit_failed: + destroy_workqueue(mrioc->fwevt_worker_thread); +out_fwevtthread_failed: + spin_lock(&mrioc_list_lock); + list_del(&mrioc->list); + spin_unlock(&mrioc_list_lock); + scsi_host_put(shost); +shost_failed: + return retval; +} + +/** + * mpi3mr_remove - PCI remove callback + * @pdev: PCI device instance + * + * Free up all memory and resources associated with the + * controllerand target devices, unregister the shost. + * + * Return: Nothing. + */ +static void mpi3mr_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct mpi3mr_ioc *mrioc; + struct workqueue_struct *wq; + unsigned long flags; + struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next; + + if (!shost) + return; + + mrioc = shost_priv(shost); + while (mrioc->reset_in_progress || mrioc->is_driver_loading) + ssleep(1); + + mrioc->stop_drv_processing = 1; + mpi3mr_cleanup_fwevt_list(mrioc); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + wq = mrioc->fwevt_worker_thread; + mrioc->fwevt_worker_thread = NULL; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (wq) + destroy_workqueue(wq); + scsi_remove_host(shost); + + list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, + list) { + mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); + mpi3mr_tgtdev_del_from_list(mrioc, tgtdev); + mpi3mr_tgtdev_put(tgtdev); + } + mpi3mr_cleanup_ioc(mrioc, 0); + + spin_lock(&mrioc_list_lock); + list_del(&mrioc->list); + spin_unlock(&mrioc_list_lock); + + scsi_host_put(shost); +} + +/** + * mpi3mr_shutdown - PCI shutdown callback + * @pdev: PCI device instance + * + * Free up all memory and resources associated with the + * controller + * + * Return: Nothing. + */ +static void mpi3mr_shutdown(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct mpi3mr_ioc *mrioc; + struct workqueue_struct *wq; + unsigned long flags; + + if (!shost) + return; + + mrioc = shost_priv(shost); + while (mrioc->reset_in_progress || mrioc->is_driver_loading) + ssleep(1); + + mrioc->stop_drv_processing = 1; + mpi3mr_cleanup_fwevt_list(mrioc); + spin_lock_irqsave(&mrioc->fwevt_lock, flags); + wq = mrioc->fwevt_worker_thread; + mrioc->fwevt_worker_thread = NULL; + spin_unlock_irqrestore(&mrioc->fwevt_lock, flags); + if (wq) + destroy_workqueue(wq); + mpi3mr_cleanup_ioc(mrioc, 0); +} + +#ifdef CONFIG_PM +/** + * mpi3mr_suspend - PCI power management suspend callback + * @pdev: PCI device instance + * @state: New power state + * + * Change the power state to the given value and cleanup the IOC + * by issuing MUR and shutdown notification + * + * Return: 0 always. + */ +static int mpi3mr_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct mpi3mr_ioc *mrioc; + pci_power_t device_state; + + if (!shost) + return 0; + + mrioc = shost_priv(shost); + while (mrioc->reset_in_progress || mrioc->is_driver_loading) + ssleep(1); + mrioc->stop_drv_processing = 1; + mpi3mr_cleanup_fwevt_list(mrioc); + scsi_block_requests(shost); + mpi3mr_stop_watchdog(mrioc); + mpi3mr_cleanup_ioc(mrioc, 1); + + device_state = pci_choose_state(pdev, state); + ioc_info(mrioc, "pdev=0x%p, slot=%s, entering operating state [D%d]\n", + pdev, pci_name(pdev), device_state); + pci_save_state(pdev); + pci_set_power_state(pdev, device_state); + mpi3mr_cleanup_resources(mrioc); + + return 0; +} + +/** + * mpi3mr_resume - PCI power management resume callback + * @pdev: PCI device instance + * + * Restore the power state to D0 and reinitialize the controller + * and resume I/O operations to the target devices + * + * Return: 0 on success, non-zero on failure + */ +static int mpi3mr_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct mpi3mr_ioc *mrioc; + pci_power_t device_state = pdev->current_state; + int r; + + if (!shost) + return 0; + + mrioc = shost_priv(shost); + + ioc_info(mrioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n", + pdev, pci_name(pdev), device_state); + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + mrioc->pdev = pdev; + mrioc->cpu_count = num_online_cpus(); + r = mpi3mr_setup_resources(mrioc); + if (r) { + ioc_info(mrioc, "%s: Setup resources failed[%d]\n", + __func__, r); + return r; + } + + mrioc->stop_drv_processing = 0; + mpi3mr_init_ioc(mrioc, 1); + scsi_unblock_requests(shost); + mpi3mr_start_watchdog(mrioc); + + return 0; +} +#endif + +static const struct pci_device_id mpi3mr_pci_id_table[] = { + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_LSI_LOGIC, 0x00A5, + PCI_ANY_ID, PCI_ANY_ID) + }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table); + +static struct pci_driver mpi3mr_pci_driver = { + .name = MPI3MR_DRIVER_NAME, + .id_table = mpi3mr_pci_id_table, + .probe = mpi3mr_probe, + .remove = mpi3mr_remove, + .shutdown = mpi3mr_shutdown, +#ifdef CONFIG_PM + .suspend = mpi3mr_suspend, + .resume = mpi3mr_resume, +#endif +}; + +static int __init mpi3mr_init(void) +{ + int ret_val; + + pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME, + MPI3MR_DRIVER_VERSION); + + ret_val = pci_register_driver(&mpi3mr_pci_driver); + + return ret_val; +} + +static void __exit mpi3mr_exit(void) +{ + if (warn_non_secure_ctlr) + pr_warn( + "Unloading %s version %s while managing a non secure controller\n", + MPI3MR_DRIVER_NAME, MPI3MR_DRIVER_VERSION); + else + pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME, + MPI3MR_DRIVER_VERSION); + + pci_unregister_driver(&mpi3mr_pci_driver); +} + +module_init(mpi3mr_init); +module_exit(mpi3mr_exit); diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index bc4ed3ed4b9a..4df8448cfbf2 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4437,6 +4437,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) ioc->pdev->subsystem_device); break; } + break; default: break; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 45a8bc67f3ae..dc2aaaf645d3 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -12076,6 +12076,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->multipath_on_hba = 1; else ioc->multipath_on_hba = 0; + break; default: break; } diff --git a/drivers/scsi/qedi/qedi.h b/drivers/scsi/qedi/qedi.h index c342defc3f52..ce199a7a16b8 100644 --- a/drivers/scsi/qedi/qedi.h +++ b/drivers/scsi/qedi/qedi.h @@ -284,6 +284,7 @@ struct qedi_ctx { #define QEDI_IN_RECOVERY 5 #define QEDI_IN_OFFLINE 6 #define QEDI_IN_SHUTDOWN 7 +#define QEDI_BLOCK_IO 8 u8 mac[ETH_ALEN]; u32 src_ip[4]; diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c index 440ddd2309f1..71333d3c5c86 100644 --- a/drivers/scsi/qedi/qedi_fw.c +++ b/drivers/scsi/qedi/qedi_fw.c @@ -14,8 +14,8 @@ #include "qedi_fw_iscsi.h" #include "qedi_fw_scsi.h" -static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, - struct iscsi_task *mtask); +static int send_iscsi_tmf(struct qedi_conn *qedi_conn, + struct iscsi_task *mtask, struct iscsi_task *ctask); void qedi_iscsi_unmap_sg_list(struct qedi_cmd *cmd) { @@ -73,7 +73,6 @@ static void qedi_process_logout_resp(struct qedi_ctx *qedi, spin_unlock(&qedi_conn->list_lock); cmd->state = RESPONSE_RECEIVED; - qedi_clear_task_idx(qedi, cmd->task_id); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0); spin_unlock(&session->back_lock); @@ -138,7 +137,6 @@ static void qedi_process_text_resp(struct qedi_ctx *qedi, spin_unlock(&qedi_conn->list_lock); cmd->state = RESPONSE_RECEIVED; - qedi_clear_task_idx(qedi, cmd->task_id); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, qedi_conn->gen_pdu.resp_buf, @@ -158,19 +156,11 @@ static void qedi_tmf_resp_work(struct work_struct *work) struct iscsi_tm_rsp *resp_hdr_ptr; int rval = 0; - set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); resp_hdr_ptr = (struct iscsi_tm_rsp *)qedi_cmd->tmf_resp_buf; - iscsi_block_session(session->cls_session); rval = qedi_cleanup_all_io(qedi, qedi_conn, qedi_cmd->task, true); - if (rval) { - qedi_clear_task_idx(qedi, qedi_cmd->task_id); - iscsi_unblock_session(session->cls_session); + if (rval) goto exit_tmf_resp; - } - - iscsi_unblock_session(session->cls_session); - qedi_clear_task_idx(qedi, qedi_cmd->task_id); spin_lock(&session->back_lock); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0); @@ -178,7 +168,10 @@ static void qedi_tmf_resp_work(struct work_struct *work) exit_tmf_resp: kfree(resp_hdr_ptr); - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); + + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->fw_cleanup_works--; + spin_unlock(&qedi_conn->tmf_work_lock); } static void qedi_process_tmf_resp(struct qedi_ctx *qedi, @@ -234,18 +227,25 @@ static void qedi_process_tmf_resp(struct qedi_ctx *qedi, } spin_unlock(&qedi_conn->list_lock); - if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) || - ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_TARGET_WARM_RESET) || - ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_TARGET_COLD_RESET)) { + spin_lock(&qedi_conn->tmf_work_lock); + switch (tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) { + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + case ISCSI_TM_FUNC_TARGET_COLD_RESET: + if (qedi_conn->ep_disconnect_starting) { + /* Session is down so ep_disconnect will clean up */ + spin_unlock(&qedi_conn->tmf_work_lock); + goto unblock_sess; + } + + qedi_conn->fw_cleanup_works++; + spin_unlock(&qedi_conn->tmf_work_lock); + INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_resp_work); queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work); goto unblock_sess; } - - qedi_clear_task_idx(qedi, qedi_cmd->task_id); + spin_unlock(&qedi_conn->tmf_work_lock); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0); kfree(resp_hdr_ptr); @@ -314,7 +314,6 @@ static void qedi_process_login_resp(struct qedi_ctx *qedi, "Freeing tid=0x%x for cid=0x%x\n", cmd->task_id, qedi_conn->iscsi_conn_id); cmd->state = RESPONSE_RECEIVED; - qedi_clear_task_idx(qedi, cmd->task_id); } static void qedi_get_rq_bdq_buf(struct qedi_ctx *qedi, @@ -468,7 +467,6 @@ static int qedi_process_nopin_mesg(struct qedi_ctx *qedi, } spin_unlock(&qedi_conn->list_lock); - qedi_clear_task_idx(qedi, cmd->task_id); } done: @@ -673,7 +671,6 @@ static void qedi_scsi_completion(struct qedi_ctx *qedi, if (qedi_io_tracing) qedi_trace_io(qedi, task, cmd->task_id, QEDI_IO_TRACE_RSP); - qedi_clear_task_idx(qedi, cmd->task_id); __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, conn->data, datalen); error: @@ -730,7 +727,6 @@ static void qedi_process_nopin_local_cmpl(struct qedi_ctx *qedi, cqe->itid, cmd->task_id); cmd->state = RESPONSE_RECEIVED; - qedi_clear_task_idx(qedi, cmd->task_id); spin_lock_bh(&session->back_lock); __iscsi_put_task(task); @@ -739,20 +735,17 @@ static void qedi_process_nopin_local_cmpl(struct qedi_ctx *qedi, static void qedi_process_cmd_cleanup_resp(struct qedi_ctx *qedi, struct iscsi_cqe_solicited *cqe, - struct iscsi_task *task, struct iscsi_conn *conn) { struct qedi_work_map *work, *work_tmp; u32 proto_itt = cqe->itid; - u32 ptmp_itt = 0; itt_t protoitt = 0; int found = 0; struct qedi_cmd *qedi_cmd = NULL; - u32 rtid = 0; u32 iscsi_cid; struct qedi_conn *qedi_conn; struct qedi_cmd *dbg_cmd; - struct iscsi_task *mtask; + struct iscsi_task *mtask, *task; struct iscsi_tm *tmf_hdr = NULL; iscsi_cid = cqe->conn_id; @@ -778,93 +771,64 @@ static void qedi_process_cmd_cleanup_resp(struct qedi_ctx *qedi, } found = 1; mtask = qedi_cmd->task; + task = work->ctask; tmf_hdr = (struct iscsi_tm *)mtask->hdr; - rtid = work->rtid; list_del_init(&work->list); kfree(work); qedi_cmd->list_tmf_work = NULL; } } - spin_unlock_bh(&qedi_conn->tmf_work_lock); - - if (found) { - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, - "TMF work, cqe->tid=0x%x, tmf flags=0x%x, cid=0x%x\n", - proto_itt, tmf_hdr->flags, qedi_conn->iscsi_conn_id); - - if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_ABORT_TASK) { - spin_lock_bh(&conn->session->back_lock); - - protoitt = build_itt(get_itt(tmf_hdr->rtt), - conn->session->age); - task = iscsi_itt_to_task(conn, protoitt); - - spin_unlock_bh(&conn->session->back_lock); - - if (!task) { - QEDI_NOTICE(&qedi->dbg_ctx, - "IO task completed, tmf rtt=0x%x, cid=0x%x\n", - get_itt(tmf_hdr->rtt), - qedi_conn->iscsi_conn_id); - return; - } - - dbg_cmd = task->dd_data; - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, - "Abort tmf rtt=0x%x, i/o itt=0x%x, i/o tid=0x%x, cid=0x%x\n", - get_itt(tmf_hdr->rtt), get_itt(task->itt), - dbg_cmd->task_id, qedi_conn->iscsi_conn_id); + if (!found) { + spin_unlock_bh(&qedi_conn->tmf_work_lock); + goto check_cleanup_reqs; + } - if (qedi_cmd->state == CLEANUP_WAIT_FAILED) - qedi_cmd->state = CLEANUP_RECV; + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, + "TMF work, cqe->tid=0x%x, tmf flags=0x%x, cid=0x%x\n", + proto_itt, tmf_hdr->flags, qedi_conn->iscsi_conn_id); + + spin_lock_bh(&conn->session->back_lock); + if (iscsi_task_is_completed(task)) { + QEDI_NOTICE(&qedi->dbg_ctx, + "IO task completed, tmf rtt=0x%x, cid=0x%x\n", + get_itt(tmf_hdr->rtt), qedi_conn->iscsi_conn_id); + goto unlock; + } - qedi_clear_task_idx(qedi_conn->qedi, rtid); + dbg_cmd = task->dd_data; - spin_lock(&qedi_conn->list_lock); - if (likely(dbg_cmd->io_cmd_in_list)) { - dbg_cmd->io_cmd_in_list = false; - list_del_init(&dbg_cmd->io_cmd); - qedi_conn->active_cmd_count--; - } - spin_unlock(&qedi_conn->list_lock); - qedi_cmd->state = CLEANUP_RECV; - wake_up_interruptible(&qedi_conn->wait_queue); - } - } else if (qedi_conn->cmd_cleanup_req > 0) { - spin_lock_bh(&conn->session->back_lock); - qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt); - protoitt = build_itt(ptmp_itt, conn->session->age); - task = iscsi_itt_to_task(conn, protoitt); - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, - "cleanup io itid=0x%x, protoitt=0x%x, cmd_cleanup_cmpl=%d, cid=0x%x\n", - cqe->itid, protoitt, qedi_conn->cmd_cleanup_cmpl, - qedi_conn->iscsi_conn_id); + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, + "Abort tmf rtt=0x%x, i/o itt=0x%x, i/o tid=0x%x, cid=0x%x\n", + get_itt(tmf_hdr->rtt), get_itt(task->itt), dbg_cmd->task_id, + qedi_conn->iscsi_conn_id); - spin_unlock_bh(&conn->session->back_lock); - if (!task) { - QEDI_NOTICE(&qedi->dbg_ctx, - "task is null, itid=0x%x, cid=0x%x\n", - cqe->itid, qedi_conn->iscsi_conn_id); - return; - } - qedi_conn->cmd_cleanup_cmpl++; - wake_up(&qedi_conn->wait_queue); + spin_lock(&qedi_conn->list_lock); + if (likely(dbg_cmd->io_cmd_in_list)) { + dbg_cmd->io_cmd_in_list = false; + list_del_init(&dbg_cmd->io_cmd); + qedi_conn->active_cmd_count--; + } + spin_unlock(&qedi_conn->list_lock); + qedi_cmd->state = CLEANUP_RECV; +unlock: + spin_unlock_bh(&conn->session->back_lock); + spin_unlock_bh(&qedi_conn->tmf_work_lock); + wake_up_interruptible(&qedi_conn->wait_queue); + return; +check_cleanup_reqs: + if (qedi_conn->cmd_cleanup_req > 0) { QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_TID, "Freeing tid=0x%x for cid=0x%x\n", cqe->itid, qedi_conn->iscsi_conn_id); - qedi_clear_task_idx(qedi_conn->qedi, cqe->itid); - + qedi_conn->cmd_cleanup_cmpl++; + wake_up(&qedi_conn->wait_queue); } else { - qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt); - protoitt = build_itt(ptmp_itt, conn->session->age); - task = iscsi_itt_to_task(conn, protoitt); QEDI_ERR(&qedi->dbg_ctx, - "Delayed or untracked cleanup response, itt=0x%x, tid=0x%x, cid=0x%x, task=%p\n", - protoitt, cqe->itid, qedi_conn->iscsi_conn_id, task); + "Delayed or untracked cleanup response, itt=0x%x, tid=0x%x, cid=0x%x\n", + protoitt, cqe->itid, qedi_conn->iscsi_conn_id); } } @@ -959,8 +923,7 @@ void qedi_fp_process_cqes(struct qedi_work *work) goto exit_fp_process; case ISCSI_CQE_TYPE_TASK_CLEANUP: QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, "CleanUp CqE\n"); - qedi_process_cmd_cleanup_resp(qedi, &cqe->cqe_solicited, task, - conn); + qedi_process_cmd_cleanup_resp(qedi, &cqe->cqe_solicited, conn); goto exit_fp_process; default: QEDI_ERR(&qedi->dbg_ctx, "Error cqe.\n"); @@ -1368,7 +1331,7 @@ static int qedi_wait_for_cleanup_request(struct qedi_ctx *qedi, return 0; } -static void qedi_tmf_work(struct work_struct *work) +static void qedi_abort_work(struct work_struct *work) { struct qedi_cmd *qedi_cmd = container_of(work, struct qedi_cmd, tmf_work); @@ -1381,17 +1344,29 @@ static void qedi_tmf_work(struct work_struct *work) struct iscsi_task *ctask; struct iscsi_tm *tmf_hdr; s16 rval = 0; - s16 tid = 0; mtask = qedi_cmd->task; tmf_hdr = (struct iscsi_tm *)mtask->hdr; - set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); - ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt); - if (!ctask || !ctask->sc) { - QEDI_ERR(&qedi->dbg_ctx, "Task already completed\n"); - goto abort_ret; + spin_lock_bh(&conn->session->back_lock); + ctask = iscsi_itt_to_ctask(conn, tmf_hdr->rtt); + if (!ctask) { + spin_unlock_bh(&conn->session->back_lock); + QEDI_ERR(&qedi->dbg_ctx, "Invalid RTT. Letting abort timeout.\n"); + goto clear_cleanup; + } + + if (iscsi_task_is_completed(ctask)) { + spin_unlock_bh(&conn->session->back_lock); + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Task already completed\n"); + /* + * We have to still send the TMF because libiscsi needs the + * response to avoid a timeout. + */ + goto send_tmf; } + spin_unlock_bh(&conn->session->back_lock); cmd = (struct qedi_cmd *)ctask->dd_data; QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, @@ -1402,19 +1377,21 @@ static void qedi_tmf_work(struct work_struct *work) if (qedi_do_not_recover) { QEDI_ERR(&qedi->dbg_ctx, "DONT SEND CLEANUP/ABORT %d\n", qedi_do_not_recover); - goto abort_ret; + goto clear_cleanup; } - list_work = kzalloc(sizeof(*list_work), GFP_ATOMIC); + list_work = kzalloc(sizeof(*list_work), GFP_NOIO); if (!list_work) { QEDI_ERR(&qedi->dbg_ctx, "Memory allocation failed\n"); - goto abort_ret; + goto clear_cleanup; } qedi_cmd->type = TYPEIO; + qedi_cmd->state = CLEANUP_WAIT; list_work->qedi_cmd = qedi_cmd; list_work->rtid = cmd->task_id; list_work->state = QEDI_WORK_SCHEDULED; + list_work->ctask = ctask; qedi_cmd->list_tmf_work = list_work; QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, @@ -1437,23 +1414,13 @@ static void qedi_tmf_work(struct work_struct *work) goto ldel_exit; } - tid = qedi_get_task_idx(qedi); - if (tid == -1) { - QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n", - qedi_conn->iscsi_conn_id); - goto ldel_exit; - } - - qedi_cmd->task_id = tid; - qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task); - -abort_ret: - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); - return; +send_tmf: + send_iscsi_tmf(qedi_conn, qedi_cmd->task, ctask); + goto clear_cleanup; ldel_exit: spin_lock_bh(&qedi_conn->tmf_work_lock); - if (!qedi_cmd->list_tmf_work) { + if (qedi_cmd->list_tmf_work) { list_del_init(&list_work->list); qedi_cmd->list_tmf_work = NULL; kfree(list_work); @@ -1468,18 +1435,19 @@ ldel_exit: } spin_unlock(&qedi_conn->list_lock); - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); +clear_cleanup: + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->fw_cleanup_works--; + spin_unlock(&qedi_conn->tmf_work_lock); } -static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, - struct iscsi_task *mtask) +static int send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask, + struct iscsi_task *ctask) { struct iscsi_tmf_request_hdr tmf_pdu_header; struct iscsi_task_params task_params; struct qedi_ctx *qedi = qedi_conn->qedi; struct e4_iscsi_task_context *fw_task_ctx; - struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data; - struct iscsi_task *ctask; struct iscsi_tm *tmf_hdr; struct qedi_cmd *qedi_cmd; struct qedi_cmd *cmd; @@ -1487,7 +1455,6 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, u32 scsi_lun[2]; s16 tid = 0; u16 sq_idx = 0; - int rval = 0; tmf_hdr = (struct iscsi_tm *)mtask->hdr; qedi_cmd = (struct qedi_cmd *)mtask->dd_data; @@ -1520,12 +1487,6 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == ISCSI_TM_FUNC_ABORT_TASK) { - ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt); - if (!ctask || !ctask->sc) { - QEDI_ERR(&qedi->dbg_ctx, - "Could not get reference task\n"); - return 0; - } cmd = (struct qedi_cmd *)ctask->dd_data; tmf_pdu_header.rtt = qedi_set_itt(cmd->task_id, @@ -1551,10 +1512,7 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, task_params.sqe = &ep->sq[sq_idx]; memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); - rval = init_initiator_tmf_request_task(&task_params, - &tmf_pdu_header); - if (rval) - return -1; + init_initiator_tmf_request_task(&task_params, &tmf_pdu_header); spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); @@ -1566,47 +1524,34 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, return 0; } -int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn, - struct iscsi_task *mtask) +int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask) { + struct iscsi_tm *tmf_hdr = (struct iscsi_tm *)mtask->hdr; + struct qedi_cmd *qedi_cmd = mtask->dd_data; struct qedi_ctx *qedi = qedi_conn->qedi; - struct iscsi_tm *tmf_hdr; - struct qedi_cmd *qedi_cmd = (struct qedi_cmd *)mtask->dd_data; - s16 tid = 0; + int rc = 0; - tmf_hdr = (struct iscsi_tm *)mtask->hdr; - qedi_cmd->task = mtask; + switch (tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) { + case ISCSI_TM_FUNC_ABORT_TASK: + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->fw_cleanup_works++; + spin_unlock(&qedi_conn->tmf_work_lock); - /* If abort task then schedule the work and return */ - if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_ABORT_TASK) { - qedi_cmd->state = CLEANUP_WAIT; - INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_work); + INIT_WORK(&qedi_cmd->tmf_work, qedi_abort_work); queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work); - - } else if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) || - ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_TARGET_WARM_RESET) || - ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == - ISCSI_TM_FUNC_TARGET_COLD_RESET)) { - tid = qedi_get_task_idx(qedi); - if (tid == -1) { - QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n", - qedi_conn->iscsi_conn_id); - return -1; - } - qedi_cmd->task_id = tid; - - qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task); - - } else { + break; + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + case ISCSI_TM_FUNC_TARGET_COLD_RESET: + rc = send_iscsi_tmf(qedi_conn, mtask, NULL); + break; + default: QEDI_ERR(&qedi->dbg_ctx, "Invalid tmf, cid=0x%x\n", qedi_conn->iscsi_conn_id); - return -1; + return -EINVAL; } - return 0; + return rc; } int qedi_send_iscsi_text(struct qedi_conn *qedi_conn, diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h index 116645c08c71..fb44a282613e 100644 --- a/drivers/scsi/qedi/qedi_gbl.h +++ b/drivers/scsi/qedi/qedi_gbl.h @@ -31,8 +31,7 @@ int qedi_send_iscsi_login(struct qedi_conn *qedi_conn, struct iscsi_task *task); int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn, struct iscsi_task *task); -int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn, - struct iscsi_task *mtask); +int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask); int qedi_send_iscsi_text(struct qedi_conn *qedi_conn, struct iscsi_task *task); int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn, diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c index 08c05403cd72..bf581ecea897 100644 --- a/drivers/scsi/qedi/qedi_iscsi.c +++ b/drivers/scsi/qedi/qedi_iscsi.c @@ -330,12 +330,22 @@ free_conn: void qedi_mark_device_missing(struct iscsi_cls_session *cls_session) { - iscsi_block_session(cls_session); + struct iscsi_session *session = cls_session->dd_data; + struct qedi_conn *qedi_conn = session->leadconn->dd_data; + + spin_lock_bh(&session->frwd_lock); + set_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags); + spin_unlock_bh(&session->frwd_lock); } void qedi_mark_device_available(struct iscsi_cls_session *cls_session) { - iscsi_unblock_session(cls_session); + struct iscsi_session *session = cls_session->dd_data; + struct qedi_conn *qedi_conn = session->leadconn->dd_data; + + spin_lock_bh(&session->frwd_lock); + clear_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags); + spin_unlock_bh(&session->frwd_lock); } static int qedi_bind_conn_to_iscsi_cid(struct qedi_ctx *qedi, @@ -377,6 +387,7 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session, struct qedi_ctx *qedi = iscsi_host_priv(shost); struct qedi_endpoint *qedi_ep; struct iscsi_endpoint *ep; + int rc = 0; ep = iscsi_lookup_endpoint(transport_fd); if (!ep) @@ -384,11 +395,16 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session, qedi_ep = ep->dd_data; if ((qedi_ep->state == EP_STATE_TCP_FIN_RCVD) || - (qedi_ep->state == EP_STATE_TCP_RST_RCVD)) - return -EINVAL; + (qedi_ep->state == EP_STATE_TCP_RST_RCVD)) { + rc = -EINVAL; + goto put_ep; + } + + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) { + rc = -EINVAL; + goto put_ep; + } - if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) - return -EINVAL; qedi_ep->conn = qedi_conn; qedi_conn->ep = qedi_ep; @@ -398,13 +414,18 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session, qedi_conn->cmd_cleanup_req = 0; qedi_conn->cmd_cleanup_cmpl = 0; - if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn)) - return -EINVAL; + if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn)) { + rc = -EINVAL; + goto put_ep; + } + spin_lock_init(&qedi_conn->tmf_work_lock); INIT_LIST_HEAD(&qedi_conn->tmf_work_list); init_waitqueue_head(&qedi_conn->wait_queue); - return 0; +put_ep: + iscsi_put_endpoint(ep); + return rc; } static int qedi_iscsi_update_conn(struct qedi_ctx *qedi, @@ -582,7 +603,11 @@ static int qedi_conn_start(struct iscsi_cls_conn *cls_conn) goto start_err; } - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->fw_cleanup_works = 0; + qedi_conn->ep_disconnect_starting = false; + spin_unlock(&qedi_conn->tmf_work_lock); + qedi_conn->abrt_conn = 0; rval = iscsi_conn_start(cls_conn); @@ -742,7 +767,7 @@ static int qedi_iscsi_send_generic_request(struct iscsi_task *task) rc = qedi_send_iscsi_logout(qedi_conn, task); break; case ISCSI_OP_SCSI_TMFUNC: - rc = qedi_iscsi_abort_work(qedi_conn, task); + rc = qedi_send_iscsi_tmf(qedi_conn, task); break; case ISCSI_OP_TEXT: rc = qedi_send_iscsi_text(qedi_conn, task); @@ -772,7 +797,6 @@ static int qedi_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) } cmd->conn = conn->dd_data; - cmd->scsi_cmd = NULL; return qedi_iscsi_send_generic_request(task); } @@ -783,9 +807,16 @@ static int qedi_task_xmit(struct iscsi_task *task) struct qedi_cmd *cmd = task->dd_data; struct scsi_cmnd *sc = task->sc; + /* Clear now so in cleanup_task we know it didn't make it */ + cmd->scsi_cmd = NULL; + cmd->task_id = U16_MAX; + if (test_bit(QEDI_IN_SHUTDOWN, &qedi_conn->qedi->flags)) return -ENODEV; + if (test_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags)) + return -EACCES; + cmd->state = 0; cmd->task = NULL; cmd->use_slowpath = false; @@ -988,12 +1019,10 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep) { struct qedi_endpoint *qedi_ep; struct qedi_conn *qedi_conn = NULL; - struct iscsi_conn *conn = NULL; struct qedi_ctx *qedi; int ret = 0; int wait_delay; int abrt_conn = 0; - int count = 10; wait_delay = 60 * HZ + DEF_MAX_RT_TIME; qedi_ep = ep->dd_data; @@ -1007,17 +1036,21 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep) if (qedi_ep->conn) { qedi_conn = qedi_ep->conn; - conn = qedi_conn->cls_conn->dd_data; - iscsi_suspend_queue(conn); abrt_conn = qedi_conn->abrt_conn; - while (count--) { - if (!test_bit(QEDI_CONN_FW_CLEANUP, - &qedi_conn->flags)) { - break; - } + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "cid=0x%x qedi_ep=%p waiting for %d tmfs\n", + qedi_ep->iscsi_cid, qedi_ep, + qedi_conn->fw_cleanup_works); + + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->ep_disconnect_starting = true; + while (qedi_conn->fw_cleanup_works > 0) { + spin_unlock(&qedi_conn->tmf_work_lock); msleep(1000); + spin_lock(&qedi_conn->tmf_work_lock); } + spin_unlock(&qedi_conn->tmf_work_lock); if (test_bit(QEDI_IN_RECOVERY, &qedi->flags)) { if (qedi_do_not_recover) { @@ -1383,13 +1416,24 @@ static umode_t qedi_attr_is_visible(int param_type, int param) static void qedi_cleanup_task(struct iscsi_task *task) { - if (!task->sc || task->state == ISCSI_TASK_PENDING) { + struct qedi_cmd *cmd; + + if (task->state == ISCSI_TASK_PENDING) { QEDI_INFO(NULL, QEDI_LOG_IO, "Returning ref_cnt=%d\n", refcount_read(&task->refcount)); return; } - qedi_iscsi_unmap_sg_list(task->dd_data); + if (task->sc) + qedi_iscsi_unmap_sg_list(task->dd_data); + + cmd = task->dd_data; + if (cmd->task_id != U16_MAX) + qedi_clear_task_idx(iscsi_host_priv(task->conn->session->host), + cmd->task_id); + + cmd->task_id = U16_MAX; + cmd->scsi_cmd = NULL; } struct iscsi_transport qedi_iscsi_transport = { @@ -1401,6 +1445,7 @@ struct iscsi_transport qedi_iscsi_transport = { .destroy_session = qedi_session_destroy, .create_conn = qedi_conn_create, .bind_conn = qedi_conn_bind, + .unbind_conn = iscsi_conn_unbind, .start_conn = qedi_conn_start, .stop_conn = iscsi_conn_stop, .destroy_conn = qedi_conn_destroy, @@ -1620,8 +1665,11 @@ void qedi_clear_session_ctx(struct iscsi_cls_session *cls_sess) struct iscsi_conn *conn = session->leadconn; struct qedi_conn *qedi_conn = conn->dd_data; - if (iscsi_is_session_online(cls_sess)) + if (iscsi_is_session_online(cls_sess)) { + if (conn) + iscsi_suspend_queue(conn); qedi_ep_disconnect(qedi_conn->iscsi_ep); + } qedi_conn_destroy(qedi_conn->cls_conn); diff --git a/drivers/scsi/qedi/qedi_iscsi.h b/drivers/scsi/qedi/qedi_iscsi.h index 39dc27c85e3c..758735209e15 100644 --- a/drivers/scsi/qedi/qedi_iscsi.h +++ b/drivers/scsi/qedi/qedi_iscsi.h @@ -169,8 +169,8 @@ struct qedi_conn { struct list_head tmf_work_list; wait_queue_head_t wait_queue; spinlock_t tmf_work_lock; /* tmf work lock */ - unsigned long flags; -#define QEDI_CONN_FW_CLEANUP 1 + bool ep_disconnect_starting; + int fw_cleanup_works; }; struct qedi_cmd { @@ -212,6 +212,7 @@ struct qedi_cmd { struct qedi_work_map { struct list_head list; struct qedi_cmd *qedi_cmd; + struct iscsi_task *ctask; int rtid; int state; diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 2455d1448a7e..edf915432704 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -640,7 +640,7 @@ static struct qedi_ctx *qedi_host_alloc(struct pci_dev *pdev) goto exit_setup_shost; } - shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA; + shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA - 1; shost->max_channel = 0; shost->max_lun = ~0; shost->max_cmd_len = 16; diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 0cacb667a88b..e119f8b24e33 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -671,7 +671,7 @@ void qla_nvme_unregister_remote_port(struct fc_port *fcport) if (!IS_ENABLED(CONFIG_NVME_FC)) return; - ql_log(ql_log_warn, NULL, 0x2112, + ql_log(ql_log_warn, fcport->vha, 0x2112, "%s: unregister remoteport on %p %8phN\n", __func__, fcport, fcport->port_name); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index ad3afe30f617..0e7a7e82e028 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -259,6 +259,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = { .start_conn = qla4xxx_conn_start, .create_conn = qla4xxx_conn_create, .bind_conn = qla4xxx_conn_bind, + .unbind_conn = iscsi_conn_unbind, .stop_conn = iscsi_conn_stop, .destroy_conn = qla4xxx_conn_destroy, .set_param = iscsi_set_param, @@ -3234,6 +3235,7 @@ static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session, conn = cls_conn->dd_data; qla_conn = conn->dd_data; qla_conn->qla_ep = ep->dd_data; + iscsi_put_endpoint(ep); return 0; } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 441f0152193f..b07105ae7c91 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -86,16 +86,10 @@ struct iscsi_internal { struct transport_container session_cont; }; -/* Worker to perform connection failure on unresponsive connections - * completely in kernel space. - */ -static void stop_conn_work_fn(struct work_struct *work); -static DECLARE_WORK(stop_conn_work, stop_conn_work_fn); - static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ static struct workqueue_struct *iscsi_eh_timer_workq; -static struct workqueue_struct *iscsi_destroy_workq; +static struct workqueue_struct *iscsi_conn_cleanup_workq; static DEFINE_IDA(iscsi_sess_ida); /* @@ -268,9 +262,20 @@ void iscsi_destroy_endpoint(struct iscsi_endpoint *ep) } EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint); +void iscsi_put_endpoint(struct iscsi_endpoint *ep) +{ + put_device(&ep->dev); +} +EXPORT_SYMBOL_GPL(iscsi_put_endpoint); + +/** + * iscsi_lookup_endpoint - get ep from handle + * @handle: endpoint handle + * + * Caller must do a iscsi_put_endpoint. + */ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) { - struct iscsi_endpoint *ep; struct device *dev; dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, @@ -278,13 +283,7 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) if (!dev) return NULL; - ep = iscsi_dev_to_endpoint(dev); - /* - * we can drop this now because the interface will prevent - * removals and lookups from racing. - */ - put_device(dev); - return ep; + return iscsi_dev_to_endpoint(dev); } EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); @@ -1620,12 +1619,6 @@ static DECLARE_TRANSPORT_CLASS(iscsi_connection_class, static struct sock *nls; static DEFINE_MUTEX(rx_queue_mutex); -/* - * conn_mutex protects the {start,bind,stop,destroy}_conn from racing - * against the kernel stop_connection recovery mechanism - */ -static DEFINE_MUTEX(conn_mutex); - static LIST_HEAD(sesslist); static DEFINE_SPINLOCK(sesslock); static LIST_HEAD(connlist); @@ -1976,6 +1969,8 @@ static void __iscsi_unblock_session(struct work_struct *work) */ void iscsi_unblock_session(struct iscsi_cls_session *session) { + flush_work(&session->block_work); + queue_work(iscsi_eh_timer_workq, &session->unblock_work); /* * Blocking the session can be done from any context so we only @@ -2242,6 +2237,123 @@ void iscsi_remove_session(struct iscsi_cls_session *session) } EXPORT_SYMBOL_GPL(iscsi_remove_session); +static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag) +{ + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n"); + + switch (flag) { + case STOP_CONN_RECOVER: + conn->state = ISCSI_CONN_FAILED; + break; + case STOP_CONN_TERM: + conn->state = ISCSI_CONN_DOWN; + break; + default: + iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n", + flag); + return; + } + + conn->transport->stop_conn(conn, flag); + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn done.\n"); +} + +static int iscsi_if_stop_conn(struct iscsi_transport *transport, + struct iscsi_uevent *ev) +{ + int flag = ev->u.stop_conn.flag; + struct iscsi_cls_conn *conn; + + conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); + if (!conn) + return -EINVAL; + + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n"); + /* + * If this is a termination we have to call stop_conn with that flag + * so the correct states get set. If we haven't run the work yet try to + * avoid the extra run. + */ + if (flag == STOP_CONN_TERM) { + cancel_work_sync(&conn->cleanup_work); + iscsi_stop_conn(conn, flag); + } else { + /* + * Figure out if it was the kernel or userspace initiating this. + */ + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { + iscsi_stop_conn(conn, flag); + } else { + ISCSI_DBG_TRANS_CONN(conn, + "flush kernel conn cleanup.\n"); + flush_work(&conn->cleanup_work); + } + /* + * Only clear for recovery to avoid extra cleanup runs during + * termination. + */ + clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags); + } + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n"); + return 0; +} + +static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active) +{ + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); + struct iscsi_endpoint *ep; + + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n"); + conn->state = ISCSI_CONN_FAILED; + + if (!conn->ep || !session->transport->ep_disconnect) + return; + + ep = conn->ep; + conn->ep = NULL; + + session->transport->unbind_conn(conn, is_active); + session->transport->ep_disconnect(ep); + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep done.\n"); +} + +static void iscsi_cleanup_conn_work_fn(struct work_struct *work) +{ + struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn, + cleanup_work); + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); + + mutex_lock(&conn->ep_mutex); + /* + * If we are not at least bound there is nothing for us to do. Userspace + * will do a ep_disconnect call if offload is used, but will not be + * doing a stop since there is nothing to clean up, so we have to clear + * the cleanup bit here. + */ + if (conn->state != ISCSI_CONN_BOUND && conn->state != ISCSI_CONN_UP) { + ISCSI_DBG_TRANS_CONN(conn, "Got error while conn is already failed. Ignoring.\n"); + clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags); + mutex_unlock(&conn->ep_mutex); + return; + } + + iscsi_ep_disconnect(conn, false); + + if (system_state != SYSTEM_RUNNING) { + /* + * If the user has set up for the session to never timeout + * then hang like they wanted. For all other cases fail right + * away since userspace is not going to relogin. + */ + if (session->recovery_tmo > 0) + session->recovery_tmo = 0; + } + + iscsi_stop_conn(conn, STOP_CONN_RECOVER); + mutex_unlock(&conn->ep_mutex); + ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n"); +} + void iscsi_free_session(struct iscsi_cls_session *session) { ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n"); @@ -2281,7 +2393,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) mutex_init(&conn->ep_mutex); INIT_LIST_HEAD(&conn->conn_list); - INIT_LIST_HEAD(&conn->conn_list_err); + INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn); conn->transport = transport; conn->cid = cid; conn->state = ISCSI_CONN_DOWN; @@ -2338,7 +2450,6 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) spin_lock_irqsave(&connlock, flags); list_del(&conn->conn_list); - list_del(&conn->conn_list_err); spin_unlock_irqrestore(&connlock, flags); transport_unregister_device(&conn->dev); @@ -2348,6 +2459,18 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) } EXPORT_SYMBOL_GPL(iscsi_destroy_conn); +void iscsi_put_conn(struct iscsi_cls_conn *conn) +{ + put_device(&conn->dev); +} +EXPORT_SYMBOL_GPL(iscsi_put_conn); + +void iscsi_get_conn(struct iscsi_cls_conn *conn) +{ + get_device(&conn->dev); +} +EXPORT_SYMBOL_GPL(iscsi_get_conn); + /* * iscsi interface functions */ @@ -2453,77 +2576,6 @@ int iscsi_offload_mesg(struct Scsi_Host *shost, } EXPORT_SYMBOL_GPL(iscsi_offload_mesg); -/* - * This can be called without the rx_queue_mutex, if invoked by the kernel - * stop work. But, in that case, it is guaranteed not to race with - * iscsi_destroy by conn_mutex. - */ -static void iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag) -{ - /* - * It is important that this path doesn't rely on - * rx_queue_mutex, otherwise, a thread doing allocation on a - * start_session/start_connection could sleep waiting on a - * writeback to a failed iscsi device, that cannot be recovered - * because the lock is held. If we don't hold it here, the - * kernel stop_conn_work_fn has a chance to stop the broken - * session and resolve the allocation. - * - * Still, the user invoked .stop_conn() needs to be serialized - * with stop_conn_work_fn by a private mutex. Not pretty, but - * it works. - */ - mutex_lock(&conn_mutex); - switch (flag) { - case STOP_CONN_RECOVER: - conn->state = ISCSI_CONN_FAILED; - break; - case STOP_CONN_TERM: - conn->state = ISCSI_CONN_DOWN; - break; - default: - iscsi_cls_conn_printk(KERN_ERR, conn, - "invalid stop flag %d\n", flag); - goto unlock; - } - - conn->transport->stop_conn(conn, flag); -unlock: - mutex_unlock(&conn_mutex); -} - -static void stop_conn_work_fn(struct work_struct *work) -{ - struct iscsi_cls_conn *conn, *tmp; - unsigned long flags; - LIST_HEAD(recovery_list); - - spin_lock_irqsave(&connlock, flags); - if (list_empty(&connlist_err)) { - spin_unlock_irqrestore(&connlock, flags); - return; - } - list_splice_init(&connlist_err, &recovery_list); - spin_unlock_irqrestore(&connlock, flags); - - list_for_each_entry_safe(conn, tmp, &recovery_list, conn_list_err) { - uint32_t sid = iscsi_conn_get_sid(conn); - struct iscsi_cls_session *session; - - session = iscsi_session_lookup(sid); - if (session) { - if (system_state != SYSTEM_RUNNING) { - session->recovery_tmo = 0; - iscsi_if_stop_conn(conn, STOP_CONN_TERM); - } else { - iscsi_if_stop_conn(conn, STOP_CONN_RECOVER); - } - } - - list_del_init(&conn->conn_list_err); - } -} - void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) { struct nlmsghdr *nlh; @@ -2531,12 +2583,9 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) struct iscsi_uevent *ev; struct iscsi_internal *priv; int len = nlmsg_total_size(sizeof(*ev)); - unsigned long flags; - spin_lock_irqsave(&connlock, flags); - list_add(&conn->conn_list_err, &connlist_err); - spin_unlock_irqrestore(&connlock, flags); - queue_work(system_unbound_wq, &stop_conn_work); + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) + queue_work(iscsi_conn_cleanup_workq, &conn->cleanup_work); priv = iscsi_if_transport_lookup(conn->transport); if (!priv) @@ -2866,26 +2915,17 @@ static int iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) { struct iscsi_cls_conn *conn; - unsigned long flags; conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); if (!conn) return -EINVAL; - spin_lock_irqsave(&connlock, flags); - if (!list_empty(&conn->conn_list_err)) { - spin_unlock_irqrestore(&connlock, flags); - return -EAGAIN; - } - spin_unlock_irqrestore(&connlock, flags); - + ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n"); + flush_work(&conn->cleanup_work); ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); - mutex_lock(&conn_mutex); if (transport->destroy_conn) transport->destroy_conn(conn); - mutex_unlock(&conn_mutex); - return 0; } @@ -2975,15 +3015,31 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport, ep = iscsi_lookup_endpoint(ep_handle); if (!ep) return -EINVAL; + conn = ep->conn; - if (conn) { - mutex_lock(&conn->ep_mutex); - conn->ep = NULL; + if (!conn) { + /* + * conn was not even bound yet, so we can't get iscsi conn + * failures yet. + */ + transport->ep_disconnect(ep); + goto put_ep; + } + + mutex_lock(&conn->ep_mutex); + /* Check if this was a conn error and the kernel took ownership */ + if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { + ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n"); mutex_unlock(&conn->ep_mutex); - conn->state = ISCSI_CONN_FAILED; + + flush_work(&conn->cleanup_work); + goto put_ep; } - transport->ep_disconnect(ep); + iscsi_ep_disconnect(conn, false); + mutex_unlock(&conn->ep_mutex); +put_ep: + iscsi_put_endpoint(ep); return 0; } @@ -3009,6 +3065,7 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, ev->r.retcode = transport->ep_poll(ep, ev->u.ep_poll.timeout_ms); + iscsi_put_endpoint(ep); break; case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: rc = iscsi_if_ep_disconnect(transport, @@ -3639,18 +3696,129 @@ exit_host_stats: return err; } +static int iscsi_if_transport_conn(struct iscsi_transport *transport, + struct nlmsghdr *nlh) +{ + struct iscsi_uevent *ev = nlmsg_data(nlh); + struct iscsi_cls_session *session; + struct iscsi_cls_conn *conn = NULL; + struct iscsi_endpoint *ep; + uint32_t pdu_len; + int err = 0; + + switch (nlh->nlmsg_type) { + case ISCSI_UEVENT_CREATE_CONN: + return iscsi_if_create_conn(transport, ev); + case ISCSI_UEVENT_DESTROY_CONN: + return iscsi_if_destroy_conn(transport, ev); + case ISCSI_UEVENT_STOP_CONN: + return iscsi_if_stop_conn(transport, ev); + } + + /* + * The following cmds need to be run under the ep_mutex so in kernel + * conn cleanup (ep_disconnect + unbind and conn) is not done while + * these are running. They also must not run if we have just run a conn + * cleanup because they would set the state in a way that might allow + * IO or send IO themselves. + */ + switch (nlh->nlmsg_type) { + case ISCSI_UEVENT_START_CONN: + conn = iscsi_conn_lookup(ev->u.start_conn.sid, + ev->u.start_conn.cid); + break; + case ISCSI_UEVENT_BIND_CONN: + conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); + break; + case ISCSI_UEVENT_SEND_PDU: + conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); + break; + } + + if (!conn) + return -EINVAL; + + mutex_lock(&conn->ep_mutex); + if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { + mutex_unlock(&conn->ep_mutex); + ev->r.retcode = -ENOTCONN; + return 0; + } + + switch (nlh->nlmsg_type) { + case ISCSI_UEVENT_BIND_CONN: + if (conn->ep) { + /* + * For offload boot support where iscsid is restarted + * during the pivot root stage, the ep will be intact + * here when the new iscsid instance starts up and + * reconnects. + */ + iscsi_ep_disconnect(conn, true); + } + + session = iscsi_session_lookup(ev->u.b_conn.sid); + if (!session) { + err = -EINVAL; + break; + } + + ev->r.retcode = transport->bind_conn(session, conn, + ev->u.b_conn.transport_eph, + ev->u.b_conn.is_leading); + if (!ev->r.retcode) + conn->state = ISCSI_CONN_BOUND; + + if (ev->r.retcode || !transport->ep_connect) + break; + + ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); + if (ep) { + ep->conn = conn; + conn->ep = ep; + iscsi_put_endpoint(ep); + } else { + err = -ENOTCONN; + iscsi_cls_conn_printk(KERN_ERR, conn, + "Could not set ep conn binding\n"); + } + break; + case ISCSI_UEVENT_START_CONN: + ev->r.retcode = transport->start_conn(conn); + if (!ev->r.retcode) + conn->state = ISCSI_CONN_UP; + break; + case ISCSI_UEVENT_SEND_PDU: + pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); + + if ((ev->u.send_pdu.hdr_size > pdu_len) || + (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { + err = -EINVAL; + break; + } + + ev->r.retcode = transport->send_pdu(conn, + (struct iscsi_hdr *)((char *)ev + sizeof(*ev)), + (char *)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, + ev->u.send_pdu.data_size); + break; + default: + err = -ENOSYS; + } + + mutex_unlock(&conn->ep_mutex); + return err; +} static int iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) { int err = 0; u32 portid; - u32 pdu_len; struct iscsi_uevent *ev = nlmsg_data(nlh); struct iscsi_transport *transport = NULL; struct iscsi_internal *priv; struct iscsi_cls_session *session; - struct iscsi_cls_conn *conn; struct iscsi_endpoint *ep = NULL; if (!netlink_capable(skb, CAP_SYS_ADMIN)) @@ -3691,6 +3859,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) ev->u.c_bound_session.initial_cmdsn, ev->u.c_bound_session.cmds_max, ev->u.c_bound_session.queue_depth); + iscsi_put_endpoint(ep); break; case ISCSI_UEVENT_DESTROY_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); @@ -3715,7 +3884,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) list_del_init(&session->sess_list); spin_unlock_irqrestore(&sesslock, flags); - queue_work(iscsi_destroy_workq, &session->destroy_work); + queue_work(system_unbound_wq, &session->destroy_work); } break; case ISCSI_UEVENT_UNBIND_SESSION: @@ -3726,89 +3895,16 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) else err = -EINVAL; break; - case ISCSI_UEVENT_CREATE_CONN: - err = iscsi_if_create_conn(transport, ev); - break; - case ISCSI_UEVENT_DESTROY_CONN: - err = iscsi_if_destroy_conn(transport, ev); - break; - case ISCSI_UEVENT_BIND_CONN: - session = iscsi_session_lookup(ev->u.b_conn.sid); - conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); - - if (conn && conn->ep) - iscsi_if_ep_disconnect(transport, conn->ep->id); - - if (!session || !conn) { - err = -EINVAL; - break; - } - - mutex_lock(&conn_mutex); - ev->r.retcode = transport->bind_conn(session, conn, - ev->u.b_conn.transport_eph, - ev->u.b_conn.is_leading); - if (!ev->r.retcode) - conn->state = ISCSI_CONN_BOUND; - mutex_unlock(&conn_mutex); - - if (ev->r.retcode || !transport->ep_connect) - break; - - ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); - if (ep) { - ep->conn = conn; - - mutex_lock(&conn->ep_mutex); - conn->ep = ep; - mutex_unlock(&conn->ep_mutex); - } else - iscsi_cls_conn_printk(KERN_ERR, conn, - "Could not set ep conn " - "binding\n"); - break; case ISCSI_UEVENT_SET_PARAM: err = iscsi_set_param(transport, ev); break; - case ISCSI_UEVENT_START_CONN: - conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid); - if (conn) { - mutex_lock(&conn_mutex); - ev->r.retcode = transport->start_conn(conn); - if (!ev->r.retcode) - conn->state = ISCSI_CONN_UP; - mutex_unlock(&conn_mutex); - } - else - err = -EINVAL; - break; + case ISCSI_UEVENT_CREATE_CONN: + case ISCSI_UEVENT_DESTROY_CONN: case ISCSI_UEVENT_STOP_CONN: - conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); - if (conn) - iscsi_if_stop_conn(conn, ev->u.stop_conn.flag); - else - err = -EINVAL; - break; + case ISCSI_UEVENT_START_CONN: + case ISCSI_UEVENT_BIND_CONN: case ISCSI_UEVENT_SEND_PDU: - pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); - - if ((ev->u.send_pdu.hdr_size > pdu_len) || - (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { - err = -EINVAL; - break; - } - - conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); - if (conn) { - mutex_lock(&conn_mutex); - ev->r.retcode = transport->send_pdu(conn, - (struct iscsi_hdr*)((char*)ev + sizeof(*ev)), - (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, - ev->u.send_pdu.data_size); - mutex_unlock(&conn_mutex); - } - else - err = -EINVAL; + err = iscsi_if_transport_conn(transport, nlh); break; case ISCSI_UEVENT_GET_STATS: err = iscsi_if_get_stats(transport, nlh); @@ -4656,6 +4752,7 @@ iscsi_register_transport(struct iscsi_transport *tt) int err; BUG_ON(!tt); + WARN_ON(tt->ep_disconnect && !tt->unbind_conn); priv = iscsi_if_transport_lookup(tt); if (priv) @@ -4810,10 +4907,10 @@ static __init int iscsi_transport_init(void) goto release_nls; } - iscsi_destroy_workq = alloc_workqueue("%s", - WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, - 1, "iscsi_destroy"); - if (!iscsi_destroy_workq) { + iscsi_conn_cleanup_workq = alloc_workqueue("%s", + WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0, + "iscsi_conn_cleanup"); + if (!iscsi_conn_cleanup_workq) { err = -ENOMEM; goto destroy_wq; } @@ -4843,7 +4940,7 @@ unregister_transport_class: static void __exit iscsi_transport_exit(void) { - destroy_workqueue(iscsi_destroy_workq); + destroy_workqueue(iscsi_conn_cleanup_workq); destroy_workqueue(iscsi_eh_timer_workq); netlink_kernel_release(nls); bus_unregister(&iscsi_flashnode_bus); diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index 9912e208c2a1..bbe0596afc9d 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -844,6 +844,9 @@ static int ufs_mtk_device_reset(struct ufs_hba *hba) { struct arm_smccc_res res; + /* disable hba before device reset */ + ufshcd_hba_stop(hba); + ufs_mtk_device_reset_ctrl(0, res); /* diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 02267b090729..23df69de06f3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4492,7 +4492,7 @@ EXPORT_SYMBOL_GPL(ufshcd_make_hba_operational); * ufshcd_hba_stop - Send controller to reset state * @hba: per adapter instance */ -static inline void ufshcd_hba_stop(struct ufs_hba *hba) +void ufshcd_hba_stop(struct ufs_hba *hba) { unsigned long flags; int err; @@ -4511,6 +4511,7 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba) if (err) dev_err(hba->dev, "%s: Controller disable failed\n", __func__); } +EXPORT_SYMBOL_GPL(ufshcd_hba_stop); /** * ufshcd_hba_execute_hce - initialize the controller @@ -9769,10 +9770,7 @@ static const struct dev_pm_ops ufs_rpmb_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(NULL, ufshcd_rpmb_resume) }; -/** - * Describes the ufs rpmb wlun. - * Used only to send uac. - */ +/* ufs_rpmb_wlun_template - Describes UFS RPMB WLUN. Used only to send UAC. */ static struct scsi_driver ufs_rpmb_wlun_template = { .gendrv = { .name = "ufs_rpmb_wlun", diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 5735a9516faa..8b1d0dad0764 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -970,6 +970,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, unsigned long timeout_ms); void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk); void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val); +void ufshcd_hba_stop(struct ufs_hba *hba); static inline void check_upiu_size(void) { diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 02f966e9358f..4ee233e5a6ff 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -145,6 +145,13 @@ static inline void* iscsi_next_hdr(struct iscsi_task *task) return (void*)task->hdr + task->hdr_len; } +static inline bool iscsi_task_is_completed(struct iscsi_task *task) +{ + return task->state == ISCSI_TASK_COMPLETED || + task->state == ISCSI_TASK_ABRT_TMF || + task->state == ISCSI_TASK_ABRT_SESS_RECOV; +} + /* Connection's states */ enum { ISCSI_CONN_INITIAL_STAGE, @@ -195,12 +202,6 @@ struct iscsi_conn { unsigned long suspend_tx; /* suspend Tx */ unsigned long suspend_rx; /* suspend Rx */ - /* abort */ - wait_queue_head_t ehwait; /* used in eh_abort() */ - struct iscsi_tm tmhdr; - struct timer_list tmf_timer; - int tmf_state; /* see TMF_INITIAL, etc.*/ - /* negotiated params */ unsigned max_recv_dlength; /* initiator_max_recv_dsl*/ unsigned max_xmit_dlength; /* target_max_recv_dsl */ @@ -270,6 +271,12 @@ struct iscsi_session { * and recv lock. */ struct mutex eh_mutex; + /* abort */ + wait_queue_head_t ehwait; /* used in eh_abort() */ + struct iscsi_tm tmhdr; + struct timer_list tmf_timer; + int tmf_state; /* see TMF_INITIAL, etc.*/ + struct iscsi_task *running_aborted_task; /* iSCSI session-wide sequencing */ uint32_t cmdsn; @@ -424,6 +431,7 @@ extern int iscsi_conn_start(struct iscsi_cls_conn *); extern void iscsi_conn_stop(struct iscsi_cls_conn *, int); extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *, int); +extern void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active); extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err); extern void iscsi_session_failure(struct iscsi_session *session, enum iscsi_err err); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index fc5a39839b4b..c5d7810fd792 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -82,6 +82,7 @@ struct iscsi_transport { void (*destroy_session) (struct iscsi_cls_session *session); struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess, uint32_t cid); + void (*unbind_conn) (struct iscsi_cls_conn *conn, bool is_active); int (*bind_conn) (struct iscsi_cls_session *session, struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, int is_leading); @@ -196,15 +197,23 @@ enum iscsi_connection_state { ISCSI_CONN_BOUND, }; +#define ISCSI_CLS_CONN_BIT_CLEANUP 1 + struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ - struct list_head conn_list_err; /* item in connlist_err */ void *dd_data; /* LLD private data */ struct iscsi_transport *transport; uint32_t cid; /* connection id */ + /* + * This protects the conn startup and binding/unbinding of the ep to + * the conn. Unbinding includes ep_disconnect and stop_conn. + */ struct mutex ep_mutex; struct iscsi_endpoint *ep; + unsigned long flags; + struct work_struct cleanup_work; + struct device dev; /* sysfs transport/container device */ enum iscsi_connection_state state; }; @@ -434,6 +443,8 @@ extern void iscsi_remove_session(struct iscsi_cls_session *session); extern void iscsi_free_session(struct iscsi_cls_session *session); extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess, int dd_size, uint32_t cid); +extern void iscsi_put_conn(struct iscsi_cls_conn *conn); +extern void iscsi_get_conn(struct iscsi_cls_conn *conn); extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn); extern void iscsi_unblock_session(struct iscsi_cls_session *session); extern void iscsi_block_session(struct iscsi_cls_session *session); @@ -441,6 +452,7 @@ extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time); extern struct iscsi_endpoint *iscsi_create_endpoint(int dd_size); extern void iscsi_destroy_endpoint(struct iscsi_endpoint *ep); extern struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle); +extern void iscsi_put_endpoint(struct iscsi_endpoint *ep); extern int iscsi_block_scsi_eh(struct scsi_cmnd *cmd); extern struct iscsi_iface *iscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *t, |