|
@@ -59,7 +59,8 @@ static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
|
|
|
uint32_t);
|
|
|
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
|
|
|
uint8_t *, uint32_t *);
|
|
|
-
|
|
|
+static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
|
|
|
+ struct hbq_dmabuf *);
|
|
|
static IOCB_t *
|
|
|
lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
|
|
|
{
|
|
@@ -572,9 +573,9 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
|
|
|
sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
|
|
|
if (sglq) {
|
|
|
if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
|
|
|
- || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
|
|
|
+ && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
|
|
|
&& (iocbq->iocb.un.ulpWord[4]
|
|
|
- == IOERR_SLI_ABORTED))) {
|
|
|
+ == IOERR_ABORT_REQUESTED))) {
|
|
|
spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
|
|
|
iflag);
|
|
|
list_add(&sglq->list,
|
|
@@ -767,6 +768,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
|
|
|
case CMD_CLOSE_XRI_CX:
|
|
|
case CMD_XRI_ABORTED_CX:
|
|
|
case CMD_ABORT_MXRI64_CN:
|
|
|
+ case CMD_XMIT_BLS_RSP64_CX:
|
|
|
type = LPFC_ABORT_IOCB;
|
|
|
break;
|
|
|
case CMD_RCV_SEQUENCE_CX:
|
|
@@ -6081,6 +6083,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
|
|
|
command_type = OTHER_COMMAND;
|
|
|
xritag = 0;
|
|
|
break;
|
|
|
+ case CMD_XMIT_BLS_RSP64_CX:
|
|
|
+ /* As BLS ABTS-ACC WQE is very different from other WQEs,
|
|
|
+ * we re-construct this WQE here based on information in
|
|
|
+ * iocbq from scratch.
|
|
|
+ */
|
|
|
+ memset(wqe, 0, sizeof(union lpfc_wqe));
|
|
|
+ bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
|
|
|
+ iocbq->iocb.un.ulpWord[3]);
|
|
|
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
|
|
|
+ iocbq->sli4_xritag);
|
|
|
+ bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
|
|
|
+ bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
|
|
|
+ bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
|
|
|
+ iocbq->iocb.ulpContext);
|
|
|
+ /* Overwrite the pre-set comnd type with OTHER_COMMAND */
|
|
|
+ command_type = OTHER_COMMAND;
|
|
|
+ break;
|
|
|
case CMD_XRI_ABORTED_CX:
|
|
|
case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
|
|
|
/* words0-2 are all 0's no bde */
|
|
@@ -6139,7 +6158,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
|
|
|
|
|
|
if (piocb->sli4_xritag == NO_XRI) {
|
|
|
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
|
|
|
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
|
|
|
+ piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
|
|
|
sglq = NULL;
|
|
|
else {
|
|
|
sglq = __lpfc_sli_get_sglq(phba);
|
|
@@ -6464,7 +6483,7 @@ lpfc_sli_setup(struct lpfc_hba *phba)
|
|
|
pring->iotag_max = 4096;
|
|
|
pring->lpfc_sli_rcv_async_status =
|
|
|
lpfc_sli_async_event_handler;
|
|
|
- pring->num_mask = 4;
|
|
|
+ pring->num_mask = LPFC_MAX_RING_MASK;
|
|
|
pring->prt[0].profile = 0; /* Mask 0 */
|
|
|
pring->prt[0].rctl = FC_ELS_REQ;
|
|
|
pring->prt[0].type = FC_ELS_DATA;
|
|
@@ -6489,6 +6508,12 @@ lpfc_sli_setup(struct lpfc_hba *phba)
|
|
|
pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
|
|
|
pring->prt[3].lpfc_sli_rcv_unsol_event =
|
|
|
lpfc_ct_unsol_event;
|
|
|
+ /* abort unsolicited sequence */
|
|
|
+ pring->prt[4].profile = 0; /* Mask 4 */
|
|
|
+ pring->prt[4].rctl = FC_RCTL_BA_ABTS;
|
|
|
+ pring->prt[4].type = FC_TYPE_BLS;
|
|
|
+ pring->prt[4].lpfc_sli_rcv_unsol_event =
|
|
|
+ lpfc_sli4_ct_abort_unsol_event;
|
|
|
break;
|
|
|
}
|
|
|
totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
|
|
@@ -10869,6 +10894,177 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * lpfc_sli4_abort_partial_seq - Abort partially assembled unsol sequence
|
|
|
+ * @vport: pointer to a vitural port
|
|
|
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
|
|
|
+ *
|
|
|
+ * This function tries to abort from the partially assembed sequence, described
|
|
|
+ * by the information from basic abbort @dmabuf. It checks to see whether such
|
|
|
+ * partially assembled sequence held by the driver. If so, it shall free up all
|
|
|
+ * the frames from the partially assembled sequence.
|
|
|
+ *
|
|
|
+ * Return
|
|
|
+ * true -- if there is matching partially assembled sequence present and all
|
|
|
+ * the frames freed with the sequence;
|
|
|
+ * false -- if there is no matching partially assembled sequence present so
|
|
|
+ * nothing got aborted in the lower layer driver
|
|
|
+ **/
|
|
|
+static bool
|
|
|
+lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport,
|
|
|
+ struct hbq_dmabuf *dmabuf)
|
|
|
+{
|
|
|
+ struct fc_frame_header *new_hdr;
|
|
|
+ struct fc_frame_header *temp_hdr;
|
|
|
+ struct lpfc_dmabuf *d_buf, *n_buf, *h_buf;
|
|
|
+ struct hbq_dmabuf *seq_dmabuf = NULL;
|
|
|
+
|
|
|
+ /* Use the hdr_buf to find the sequence that matches this frame */
|
|
|
+ INIT_LIST_HEAD(&dmabuf->dbuf.list);
|
|
|
+ INIT_LIST_HEAD(&dmabuf->hbuf.list);
|
|
|
+ new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
|
|
|
+ list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
|
|
|
+ temp_hdr = (struct fc_frame_header *)h_buf->virt;
|
|
|
+ if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) ||
|
|
|
+ (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) ||
|
|
|
+ (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3)))
|
|
|
+ continue;
|
|
|
+ /* found a pending sequence that matches this frame */
|
|
|
+ seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Free up all the frames from the partially assembled sequence */
|
|
|
+ if (seq_dmabuf) {
|
|
|
+ list_for_each_entry_safe(d_buf, n_buf,
|
|
|
+ &seq_dmabuf->dbuf.list, list) {
|
|
|
+ list_del_init(&d_buf->list);
|
|
|
+ lpfc_in_buf_free(vport->phba, d_buf);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @cmd_iocbq: pointer to the command iocbq structure.
|
|
|
+ * @rsp_iocbq: pointer to the response iocbq structure.
|
|
|
+ *
|
|
|
+ * This function handles the sequence abort accept iocb command complete
|
|
|
+ * event. It properly releases the memory allocated to the sequence abort
|
|
|
+ * accept iocb.
|
|
|
+ **/
|
|
|
+static void
|
|
|
+lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba,
|
|
|
+ struct lpfc_iocbq *cmd_iocbq,
|
|
|
+ struct lpfc_iocbq *rsp_iocbq)
|
|
|
+{
|
|
|
+ if (cmd_iocbq)
|
|
|
+ lpfc_sli_release_iocbq(phba, cmd_iocbq);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_sli4_seq_abort_acc - Accept sequence abort
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @fc_hdr: pointer to a FC frame header.
|
|
|
+ *
|
|
|
+ * This function sends a basic accept to a previous unsol sequence abort
|
|
|
+ * event after aborting the sequence handling.
|
|
|
+ **/
|
|
|
+static void
|
|
|
+lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba,
|
|
|
+ struct fc_frame_header *fc_hdr)
|
|
|
+{
|
|
|
+ struct lpfc_iocbq *ctiocb = NULL;
|
|
|
+ struct lpfc_nodelist *ndlp;
|
|
|
+ uint16_t oxid;
|
|
|
+ uint32_t sid;
|
|
|
+ IOCB_t *icmd;
|
|
|
+
|
|
|
+ if (!lpfc_is_link_up(phba))
|
|
|
+ return;
|
|
|
+
|
|
|
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
|
|
|
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
|
|
|
+
|
|
|
+ ndlp = lpfc_findnode_did(phba->pport, sid);
|
|
|
+ if (!ndlp) {
|
|
|
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
|
|
|
+ "1268 Find ndlp returned NULL for oxid:x%x "
|
|
|
+ "SID:x%x\n", oxid, sid);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Allocate buffer for acc iocb */
|
|
|
+ ctiocb = lpfc_sli_get_iocbq(phba);
|
|
|
+ if (!ctiocb)
|
|
|
+ return;
|
|
|
+
|
|
|
+ icmd = &ctiocb->iocb;
|
|
|
+ icmd->un.xseq64.bdl.ulpIoTag32 = 0;
|
|
|
+ icmd->un.xseq64.bdl.bdeSize = 0;
|
|
|
+ icmd->un.xseq64.w5.hcsw.Dfctl = 0;
|
|
|
+ icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
|
|
|
+ icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
|
|
|
+
|
|
|
+ /* Fill in the rest of iocb fields */
|
|
|
+ icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX;
|
|
|
+ icmd->ulpBdeCount = 0;
|
|
|
+ icmd->ulpLe = 1;
|
|
|
+ icmd->ulpClass = CLASS3;
|
|
|
+ icmd->ulpContext = ndlp->nlp_rpi;
|
|
|
+ icmd->un.ulpWord[3] = oxid;
|
|
|
+
|
|
|
+ ctiocb->sli4_xritag = NO_XRI;
|
|
|
+ ctiocb->iocb_cmpl = NULL;
|
|
|
+ ctiocb->vport = phba->pport;
|
|
|
+ ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl;
|
|
|
+
|
|
|
+ /* Xmit CT abts accept on exchange <xid> */
|
|
|
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
|
|
|
+ "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n",
|
|
|
+ CMD_XMIT_BLS_RSP64_CX, phba->link_state);
|
|
|
+ lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_sli4_handle_unsol_abort - Handle sli-4 unsolicited abort event
|
|
|
+ * @vport: Pointer to the vport on which this sequence was received
|
|
|
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
|
|
|
+ *
|
|
|
+ * This function handles an SLI-4 unsolicited abort event. If the unsolicited
|
|
|
+ * receive sequence is only partially assembed by the driver, it shall abort
|
|
|
+ * the partially assembled frames for the sequence. Otherwise, if the
|
|
|
+ * unsolicited receive sequence has been completely assembled and passed to
|
|
|
+ * the Upper Layer Protocol (UPL), it then mark the per oxid status for the
|
|
|
+ * unsolicited sequence has been aborted. After that, it will issue a basic
|
|
|
+ * accept to accept the abort.
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
|
|
|
+ struct hbq_dmabuf *dmabuf)
|
|
|
+{
|
|
|
+ struct lpfc_hba *phba = vport->phba;
|
|
|
+ struct fc_frame_header fc_hdr;
|
|
|
+ bool abts_par;
|
|
|
+
|
|
|
+ /* Try to abort partially assembled seq */
|
|
|
+ abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
|
|
|
+
|
|
|
+ /* Make a copy of fc_hdr before the dmabuf being released */
|
|
|
+ memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
|
|
|
+
|
|
|
+ /* Send abort to ULP if partially seq abort failed */
|
|
|
+ if (abts_par == false)
|
|
|
+ lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
|
|
|
+ else
|
|
|
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
|
|
|
+ /* Send basic accept (BA_ACC) to the abort requester */
|
|
|
+ lpfc_sli4_seq_abort_acc(phba, &fc_hdr);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* lpfc_seq_complete - Indicates if a sequence is complete
|
|
|
* @dmabuf: pointer to a dmabuf that describes the FC sequence
|
|
@@ -10941,9 +11137,7 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
|
|
|
/* remove from receive buffer list */
|
|
|
list_del_init(&seq_dmabuf->hbuf.list);
|
|
|
/* get the Remote Port's SID */
|
|
|
- sid = (fc_hdr->fh_s_id[0] << 16 |
|
|
|
- fc_hdr->fh_s_id[1] << 8 |
|
|
|
- fc_hdr->fh_s_id[2]);
|
|
|
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
|
|
|
/* Get an iocbq struct to fill in. */
|
|
|
first_iocbq = lpfc_sli_get_iocbq(vport->phba);
|
|
|
if (first_iocbq) {
|
|
@@ -11010,6 +11204,43 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
|
|
|
return first_iocbq;
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
|
|
|
+ struct hbq_dmabuf *seq_dmabuf)
|
|
|
+{
|
|
|
+ struct fc_frame_header *fc_hdr;
|
|
|
+ struct lpfc_iocbq *iocbq, *curr_iocb, *next_iocb;
|
|
|
+ struct lpfc_hba *phba = vport->phba;
|
|
|
+
|
|
|
+ fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
|
|
|
+ iocbq = lpfc_prep_seq(vport, seq_dmabuf);
|
|
|
+ if (!iocbq) {
|
|
|
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
|
|
|
+ "2707 Ring %d handler: Failed to allocate "
|
|
|
+ "iocb Rctl x%x Type x%x received\n",
|
|
|
+ LPFC_ELS_RING,
|
|
|
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!lpfc_complete_unsol_iocb(phba,
|
|
|
+ &phba->sli.ring[LPFC_ELS_RING],
|
|
|
+ iocbq, fc_hdr->fh_r_ctl,
|
|
|
+ fc_hdr->fh_type))
|
|
|
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
|
|
|
+ "2540 Ring %d handler: unexpected Rctl "
|
|
|
+ "x%x Type x%x received\n",
|
|
|
+ LPFC_ELS_RING,
|
|
|
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
|
|
|
+
|
|
|
+ /* Free iocb created in lpfc_prep_seq */
|
|
|
+ list_for_each_entry_safe(curr_iocb, next_iocb,
|
|
|
+ &iocbq->list, list) {
|
|
|
+ list_del_init(&curr_iocb->list);
|
|
|
+ lpfc_sli_release_iocbq(phba, curr_iocb);
|
|
|
+ }
|
|
|
+ lpfc_sli_release_iocbq(phba, iocbq);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
|
|
|
* @phba: Pointer to HBA context object.
|
|
@@ -11030,7 +11261,6 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
|
|
|
struct fc_frame_header *fc_hdr;
|
|
|
struct lpfc_vport *vport;
|
|
|
uint32_t fcfi;
|
|
|
- struct lpfc_iocbq *iocbq;
|
|
|
|
|
|
/* Clear hba flag and get all received buffers into the cmplq */
|
|
|
spin_lock_irq(&phba->hbalock);
|
|
@@ -11051,6 +11281,12 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
|
|
|
lpfc_in_buf_free(phba, &dmabuf->dbuf);
|
|
|
return;
|
|
|
}
|
|
|
+ /* Handle the basic abort sequence (BA_ABTS) event */
|
|
|
+ if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) {
|
|
|
+ lpfc_sli4_handle_unsol_abort(vport, dmabuf);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
/* Link this frame */
|
|
|
seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
|
|
|
if (!seq_dmabuf) {
|
|
@@ -11068,17 +11304,8 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
|
|
|
dmabuf->tag = -1;
|
|
|
return;
|
|
|
}
|
|
|
- fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
|
|
|
- iocbq = lpfc_prep_seq(vport, seq_dmabuf);
|
|
|
- if (!lpfc_complete_unsol_iocb(phba,
|
|
|
- &phba->sli.ring[LPFC_ELS_RING],
|
|
|
- iocbq, fc_hdr->fh_r_ctl,
|
|
|
- fc_hdr->fh_type))
|
|
|
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
|
|
|
- "2540 Ring %d handler: unexpected Rctl "
|
|
|
- "x%x Type x%x received\n",
|
|
|
- LPFC_ELS_RING,
|
|
|
- fc_hdr->fh_r_ctl, fc_hdr->fh_type);
|
|
|
+ /* Send the complete sequence to the upper layer protocol */
|
|
|
+ lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf);
|
|
|
}
|
|
|
|
|
|
/**
|