root/usr/src/uts/common/io/fibre-channel/fca/emlxs/emlxs_fcp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.txt.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2004-2012 Emulex. All rights reserved.
 * Use is subject to license terms.
 * Copyright 2020 RackTop Systems, Inc.
 */

#include <emlxs.h>

/* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */
EMLXS_MSG_DEF(EMLXS_FCP_C);

#define EMLXS_GET_VADDR(hba, rp, icmd) emlxs_mem_get_vaddr(hba, rp, \
        PADDR(icmd->un.cont64[i].addrHigh, icmd->un.cont64[i].addrLow));

static void     emlxs_sbp_abort_add(emlxs_port_t *port, emlxs_buf_t *sbp,
    Q *abort, uint8_t *flag, emlxs_buf_t *fpkt);

#define SCSI3_PERSISTENT_RESERVE_IN     0x5e
#define SCSI_INQUIRY                    0x12
#define SCSI_RX_DIAG                    0x1C


/*
 *  emlxs_handle_fcp_event
 *
 *  Description: Process an FCP Rsp Ring completion
 *
 */
/* ARGSUSED */
extern void
emlxs_handle_fcp_event(emlxs_hba_t *hba, CHANNEL *cp, IOCBQ *iocbq)
{
        emlxs_port_t *port = &PPORT;
        emlxs_config_t  *cfg = &CFG;
        IOCB *cmd;
        emlxs_buf_t *sbp;
        fc_packet_t *pkt = NULL;
#ifdef SAN_DIAG_SUPPORT
        NODELIST *ndlp;
#endif
        uint32_t iostat;
        uint8_t localstat;
        fcp_rsp_t *rsp;
        uint32_t rsp_data_resid;
        uint32_t check_underrun;
        uint8_t asc;
        uint8_t ascq;
        uint8_t scsi_status;
        uint8_t sense;
        uint32_t did;
        uint32_t fix_it;
        uint8_t *scsi_cmd;
        uint8_t scsi_opcode;
        uint16_t scsi_dl;
        uint32_t data_rx;
        uint32_t length;

        cmd = &iocbq->iocb;

        /* Initialize the status */
        iostat = cmd->ULPSTATUS;
        localstat = 0;
        scsi_status = 0;
        asc = 0;
        ascq = 0;
        sense = 0;
        check_underrun = 0;
        fix_it = 0;

        HBASTATS.FcpEvent++;

        sbp = (emlxs_buf_t *)iocbq->sbp;

        if (!sbp) {
                /* completion with missing xmit command */
                HBASTATS.FcpStray++;

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_stray_fcp_completion_msg,
                    "cmd=%x iotag=%d", cmd->ULPCOMMAND, cmd->ULPIOTAG);

                return;
        }

        HBASTATS.FcpCompleted++;

#ifdef SAN_DIAG_SUPPORT
        emlxs_update_sd_bucket(sbp);
#endif /* SAN_DIAG_SUPPORT */

        pkt = PRIV2PKT(sbp);

        did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id);
        scsi_cmd = (uint8_t *)pkt->pkt_cmd;
        scsi_opcode = scsi_cmd[12];
        data_rx = 0;

        /* Sync data in data buffer only on FC_PKT_FCP_READ */
        if (pkt->pkt_datalen && (pkt->pkt_tran_type == FC_PKT_FCP_READ)) {
                EMLXS_MPDATA_SYNC(pkt->pkt_data_dma, 0, pkt->pkt_datalen,
                    DDI_DMA_SYNC_FORKERNEL);

#ifdef TEST_SUPPORT
                if (hba->underrun_counter && (iostat == IOSTAT_SUCCESS) &&
                    (pkt->pkt_datalen >= 512)) {
                        hba->underrun_counter--;
                        iostat = IOSTAT_FCP_RSP_ERROR;

                        /* Report 512 bytes missing by adapter */
                        cmd->un.fcpi.fcpi_parm = pkt->pkt_datalen - 512;

                        /* Corrupt 512 bytes of Data buffer */
                        bzero((uint8_t *)pkt->pkt_data, 512);

                        /* Set FCP response to STATUS_GOOD */
                        bzero((uint8_t *)pkt->pkt_resp, pkt->pkt_rsplen);
                }
#endif /* TEST_SUPPORT */
        }

        /* Process the pkt */
        mutex_enter(&sbp->mtx);

        /* Check for immediate return */
        if ((iostat == IOSTAT_SUCCESS) &&
            (pkt->pkt_comp) &&
            !(sbp->pkt_flags &
            (PACKET_ULP_OWNED | PACKET_COMPLETED |
            PACKET_IN_COMPLETION | PACKET_IN_TXQ | PACKET_IN_CHIPQ |
            PACKET_IN_DONEQ | PACKET_IN_TIMEOUT | PACKET_IN_FLUSH |
            PACKET_IN_ABORT | PACKET_POLLED))) {
                HBASTATS.FcpGood++;

                sbp->pkt_flags |=
                    (PACKET_STATE_VALID | PACKET_IN_COMPLETION |
                    PACKET_COMPLETED | PACKET_ULP_OWNED);
                mutex_exit(&sbp->mtx);

#if (EMLXS_MODREVX == EMLXS_MODREV2X)
                emlxs_unswap_pkt(sbp);
#endif /* EMLXS_MODREV2X */

#ifdef FMA_SUPPORT
                emlxs_check_dma(hba, sbp);
#endif  /* FMA_SUPPORT */

                cp->ulpCmplCmd++;
                (*pkt->pkt_comp) (pkt);

#ifdef FMA_SUPPORT
                if (hba->flag & FC_DMA_CHECK_ERROR) {
                        emlxs_thread_spawn(hba, emlxs_restart_thread,
                            NULL, NULL);
                }
#endif  /* FMA_SUPPORT */

                return;
        }

        /*
         * A response is only placed in the resp buffer if IOSTAT_FCP_RSP_ERROR
         * is reported.
         */

        /* Check if a response buffer was not provided */
        if ((iostat != IOSTAT_FCP_RSP_ERROR) || (pkt->pkt_rsplen == 0)) {
                goto done;
        }

        EMLXS_MPDATA_SYNC(pkt->pkt_resp_dma, 0, pkt->pkt_rsplen,
            DDI_DMA_SYNC_FORKERNEL);

        /* Get the response buffer pointer */
        rsp = (fcp_rsp_t *)pkt->pkt_resp;

        /* Validate the response payload */
        if (!rsp->fcp_u.fcp_status.resid_under &&
            !rsp->fcp_u.fcp_status.resid_over) {
                rsp->fcp_resid = 0;
        }

        if (!rsp->fcp_u.fcp_status.rsp_len_set) {
                rsp->fcp_response_len = 0;
        }

        if (!rsp->fcp_u.fcp_status.sense_len_set) {
                rsp->fcp_sense_len = 0;
        }

        length = sizeof (fcp_rsp_t) + LE_SWAP32(rsp->fcp_response_len) +
            LE_SWAP32(rsp->fcp_sense_len);

        if (length > pkt->pkt_rsplen) {
                iostat = IOSTAT_RSP_INVALID;
                pkt->pkt_data_resid = pkt->pkt_datalen;
                goto done;
        }

        /* Set the valid response flag */
        sbp->pkt_flags |= PACKET_FCP_RSP_VALID;

        scsi_status = rsp->fcp_u.fcp_status.scsi_status;

#ifdef SAN_DIAG_SUPPORT
        ndlp = (NODELIST *)iocbq->node;
        if (scsi_status == SCSI_STAT_QUE_FULL) {
                emlxs_log_sd_scsi_event(port, SD_SCSI_SUBCATEGORY_QFULL,
                    (HBA_WWN *)&ndlp->nlp_portname, sbp->lun);
        } else if (scsi_status == SCSI_STAT_BUSY) {
                emlxs_log_sd_scsi_event(port,
                    SD_SCSI_SUBCATEGORY_DEVBSY,
                    (HBA_WWN *)&ndlp->nlp_portname, sbp->lun);
        }
#endif

        /*
         * Convert a task abort to a check condition with no data
         * transferred. We saw a data corruption when Solaris received
         * a Task Abort from a tape.
         */

        if (scsi_status == SCSI_STAT_TASK_ABORT) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_fcp_completion_error_msg,
                    "Task Abort. "
                    "Fixed. did=0x%06x sbp=%p cmd=%02x dl=%d",
                    did, sbp, scsi_opcode, pkt->pkt_datalen);

                rsp->fcp_u.fcp_status.scsi_status =
                    SCSI_STAT_CHECK_COND;
                rsp->fcp_u.fcp_status.rsp_len_set = 0;
                rsp->fcp_u.fcp_status.sense_len_set = 0;
                rsp->fcp_u.fcp_status.resid_over = 0;

                if (pkt->pkt_datalen) {
                        rsp->fcp_u.fcp_status.resid_under = 1;
                        rsp->fcp_resid =
                            LE_SWAP32(pkt->pkt_datalen);
                } else {
                        rsp->fcp_u.fcp_status.resid_under = 0;
                        rsp->fcp_resid = 0;
                }

                scsi_status = SCSI_STAT_CHECK_COND;
        }

        /*
         * We only need to check underrun if data could
         * have been sent
         */

        /* Always check underrun if status is good */
        if (scsi_status == SCSI_STAT_GOOD) {
                check_underrun = 1;
        }
        /* Check the sense codes if this is a check condition */
        else if (scsi_status == SCSI_STAT_CHECK_COND) {
                check_underrun = 1;

                /* Check if sense data was provided */
                if (LE_SWAP32(rsp->fcp_sense_len) >= 14) {
                        sense = *((uint8_t *)rsp + 32 + 2);
                        asc = *((uint8_t *)rsp + 32 + 12);
                        ascq = *((uint8_t *)rsp + 32 + 13);
                }

#ifdef SAN_DIAG_SUPPORT
                emlxs_log_sd_scsi_check_event(port,
                    (HBA_WWN *)&ndlp->nlp_portname, sbp->lun,
                    scsi_opcode, sense, asc, ascq);
#endif
        }
        /* Status is not good and this is not a check condition */
        /* No data should have been sent */
        else {
                check_underrun = 0;
        }

        /* Initialize the resids */
        pkt->pkt_resp_resid = 0;
        pkt->pkt_data_resid = 0;

        /* Check if no data was to be transferred */
        if (pkt->pkt_datalen == 0) {
                goto done;
        }

        /* Get the residual underrun count reported by the SCSI reply */
        rsp_data_resid = (rsp->fcp_u.fcp_status.resid_under) ?
            LE_SWAP32(rsp->fcp_resid) : 0;

        /* Set the pkt_data_resid to what the scsi response resid */
        pkt->pkt_data_resid = rsp_data_resid;

        /* Adjust the pkt_data_resid field if needed */
        if (pkt->pkt_tran_type == FC_PKT_FCP_READ) {
                /*
                 * Get the residual underrun count reported by
                 * our adapter
                 */
                pkt->pkt_data_resid = cmd->un.fcpi.fcpi_parm;

#ifdef SAN_DIAG_SUPPORT
                if ((rsp_data_resid == 0) && (pkt->pkt_data_resid)) {
                        emlxs_log_sd_fc_rdchk_event(port,
                            (HBA_WWN *)&ndlp->nlp_portname, sbp->lun,
                            scsi_opcode, pkt->pkt_data_resid);
                }
#endif

                /* Get the actual amount of data transferred */
                data_rx = pkt->pkt_datalen - pkt->pkt_data_resid;

                /*
                 * If the residual being reported by the adapter is
                 * greater than the residual being reported in the
                 * reply, then we have a true underrun.
                 */
                if (check_underrun && (pkt->pkt_data_resid > rsp_data_resid)) {
                        switch (scsi_opcode) {
                        case SCSI_INQUIRY:
                                scsi_dl = scsi_cmd[16];
                                break;

                        case SCSI_RX_DIAG:
                                scsi_dl =
                                    (scsi_cmd[15] * 0x100) +
                                    scsi_cmd[16];
                                break;

                        default:
                                scsi_dl = pkt->pkt_datalen;
                        }

#ifdef FCP_UNDERRUN_PATCH1
if (cfg[CFG_ENABLE_PATCH].current & FCP_UNDERRUN_PATCH1) {
                        /*
                         * If status is not good and no data was
                         * actually transferred, then we must fix
                         * the issue
                         */
                        if ((scsi_status != SCSI_STAT_GOOD) && (data_rx == 0)) {
                                fix_it = 1;

                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_fcp_completion_error_msg,
                                    "Underrun(1). Fixed. "
                                    "did=0x%06x sbp=%p cmd=%02x "
                                    "dl=%d,%d rx=%d rsp=%d",
                                    did, sbp, scsi_opcode,
                                    pkt->pkt_datalen, scsi_dl,
                                    (pkt->pkt_datalen -
                                    pkt->pkt_data_resid),
                                    rsp_data_resid);

                        }
}
#endif /* FCP_UNDERRUN_PATCH1 */


#ifdef FCP_UNDERRUN_PATCH2
if (cfg[CFG_ENABLE_PATCH].current & FCP_UNDERRUN_PATCH2) {
                        if (scsi_status == SCSI_STAT_GOOD) {
                                emlxs_msg_t     *msg;

                                msg = &emlxs_fcp_completion_error_msg;
                                /*
                                 * If status is good and this is an
                                 * inquiry request and the amount of
                                 * data
                                 */
                                /*
                                 * requested <= data received, then we
                                 * must fix the issue.
                                 */

                                if ((scsi_opcode == SCSI_INQUIRY) &&
                                    (pkt->pkt_datalen >= data_rx) &&
                                    (scsi_dl <= data_rx)) {
                                        fix_it = 1;

                                        EMLXS_MSGF(EMLXS_CONTEXT, msg,
                                            "Underrun(2). Fixed. "
                                            "did=0x%06x sbp=%p "
                                            "cmd=%02x dl=%d,%d "
                                            "rx=%d rsp=%d",
                                            did, sbp, scsi_opcode,
                                            pkt->pkt_datalen, scsi_dl,
                                            data_rx, rsp_data_resid);

                                }

                                /*
                                 * If status is good and this is an
                                 * inquiry request and the amount of
                                 * data requested >= 128 bytes, but
                                 * only 128 bytes were received,
                                 * then we must fix the issue.
                                 */
                                else if ((scsi_opcode == SCSI_INQUIRY) &&
                                    (pkt->pkt_datalen >= 128) &&
                                    (scsi_dl >= 128) && (data_rx == 128)) {
                                        fix_it = 1;

                                        EMLXS_MSGF(EMLXS_CONTEXT, msg,
                                            "Underrun(3). Fixed. "
                                            "did=0x%06x sbp=%p "
                                            "cmd=%02x dl=%d,%d "
                                            "rx=%d rsp=%d",
                                            did, sbp, scsi_opcode,
                                            pkt->pkt_datalen, scsi_dl,
                                            data_rx, rsp_data_resid);

                                }
                        }
}
#endif /* FCP_UNDERRUN_PATCH2 */

                        /*
                         * Check if SCSI response payload should be
                         * fixed or if a DATA_UNDERRUN should be
                         * reported
                         */
                        if (fix_it) {
                                /*
                                 * Fix the SCSI response payload itself
                                 */
                                rsp->fcp_u.fcp_status.resid_under = 1;
                                rsp->fcp_resid =
                                    LE_SWAP32(pkt->pkt_data_resid);
                        } else {
                                /*
                                 * Change the status from
                                 * IOSTAT_FCP_RSP_ERROR to
                                 * IOSTAT_DATA_UNDERRUN
                                 */
                                iostat = IOSTAT_DATA_UNDERRUN;
                                pkt->pkt_data_resid =
                                    pkt->pkt_datalen;
                        }
                }

                /*
                 * If the residual being reported by the adapter is
                 * less than the residual being reported in the reply,
                 * then we have a true overrun. Since we don't know
                 * where the extra data came from or went to then we
                 * cannot trust anything we received
                 */
                else if (rsp_data_resid > pkt->pkt_data_resid) {
                        /*
                         * Change the status from
                         * IOSTAT_FCP_RSP_ERROR to
                         * IOSTAT_DATA_OVERRUN
                         */
                        iostat = IOSTAT_DATA_OVERRUN;
                        pkt->pkt_data_resid = pkt->pkt_datalen;
                }

        } else if ((hba->sli_mode == EMLXS_HBA_SLI4_MODE) &&
            (pkt->pkt_tran_type == FC_PKT_FCP_WRITE)) {
                /*
                 * Get the residual underrun count reported by
                 * our adapter
                 */
                pkt->pkt_data_resid = cmd->un.fcpi.fcpi_parm;

#ifdef SAN_DIAG_SUPPORT
                if ((rsp_data_resid == 0) && (pkt->pkt_data_resid)) {
                        emlxs_log_sd_fc_rdchk_event(port,
                            (HBA_WWN *)&ndlp->nlp_portname, sbp->lun,
                            scsi_opcode, pkt->pkt_data_resid);
                }
#endif /* SAN_DIAG_SUPPORT */

                /* Get the actual amount of data transferred */
                data_rx = pkt->pkt_datalen - pkt->pkt_data_resid;

                /*
                 * If the residual being reported by the adapter is
                 * greater than the residual being reported in the
                 * reply, then we have a true underrun.
                 */
                if (check_underrun && (pkt->pkt_data_resid > rsp_data_resid)) {

                        scsi_dl = pkt->pkt_datalen;

#ifdef FCP_UNDERRUN_PATCH1
if (cfg[CFG_ENABLE_PATCH].current & FCP_UNDERRUN_PATCH1) {
                        /*
                         * If status is not good and no data was
                         * actually transferred, then we must fix
                         * the issue
                         */
                        if ((scsi_status != SCSI_STAT_GOOD) && (data_rx == 0)) {
                                fix_it = 1;

                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_fcp_completion_error_msg,
                                    "Underrun(1). Fixed. "
                                    "did=0x%06x sbp=%p cmd=%02x "
                                    "dl=%d,%d rx=%d rsp=%d",
                                    did, sbp, scsi_opcode,
                                    pkt->pkt_datalen, scsi_dl,
                                    (pkt->pkt_datalen -
                                    pkt->pkt_data_resid),
                                    rsp_data_resid);

                        }
}
#endif /* FCP_UNDERRUN_PATCH1 */

                        /*
                         * Check if SCSI response payload should be
                         * fixed or if a DATA_UNDERRUN should be
                         * reported
                         */
                        if (fix_it) {
                                /*
                                 * Fix the SCSI response payload itself
                                 */
                                rsp->fcp_u.fcp_status.resid_under = 1;
                                rsp->fcp_resid =
                                    LE_SWAP32(pkt->pkt_data_resid);
                        } else {
                                /*
                                 * Change the status from
                                 * IOSTAT_FCP_RSP_ERROR to
                                 * IOSTAT_DATA_UNDERRUN
                                 */
                                iostat = IOSTAT_DATA_UNDERRUN;
                                pkt->pkt_data_resid =
                                    pkt->pkt_datalen;
                        }
                }

                /*
                 * If the residual being reported by the adapter is
                 * less than the residual being reported in the reply,
                 * then we have a true overrun. Since we don't know
                 * where the extra data came from or went to then we
                 * cannot trust anything we received
                 */
                else if (rsp_data_resid > pkt->pkt_data_resid) {
                        /*
                         * Change the status from
                         * IOSTAT_FCP_RSP_ERROR to
                         * IOSTAT_DATA_OVERRUN
                         */
                        iostat = IOSTAT_DATA_OVERRUN;
                        pkt->pkt_data_resid = pkt->pkt_datalen;
                }
        }

done:

        /* Print completion message */
        switch (iostat) {
        case IOSTAT_SUCCESS:
                /* Build SCSI GOOD status */
                if (pkt->pkt_rsplen) {
                        bzero((uint8_t *)pkt->pkt_resp, pkt->pkt_rsplen);
                }
                break;

        case IOSTAT_FCP_RSP_ERROR:
                break;

        case IOSTAT_REMOTE_STOP:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Remote Stop. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_LOCAL_REJECT:
                localstat = cmd->un.grsp.perr.statLocalError;

                switch (localstat) {
                case IOERR_SEQUENCE_TIMEOUT:
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_fcp_completion_error_msg,
                            "Local reject. "
                            "%s did=0x%06x sbp=%p cmd=%02x tmo=%d ",
                            emlxs_error_xlate(localstat), did, sbp,
                            scsi_opcode, pkt->pkt_timeout);
                        break;

                default:
                        EMLXS_MSGF(EMLXS_CONTEXT,
                            &emlxs_fcp_completion_error_msg,
                            "Local reject. %s 0x%06x %p %02x (%x)(%x)",
                            emlxs_error_xlate(localstat), did, sbp,
                            scsi_opcode, (uint16_t)cmd->ULPIOTAG,
                            (uint16_t)cmd->ULPCONTEXT);
                }

                break;

        case IOSTAT_NPORT_RJT:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Nport reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_FABRIC_RJT:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Fabric reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_NPORT_BSY:
#ifdef SAN_DIAG_SUPPORT
                ndlp = (NODELIST *)iocbq->node;
                emlxs_log_sd_fc_bsy_event(port, (HBA_WWN *)&ndlp->nlp_portname);
#endif

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Nport busy. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_FABRIC_BSY:
#ifdef SAN_DIAG_SUPPORT
                ndlp = (NODELIST *)iocbq->node;
                emlxs_log_sd_fc_bsy_event(port, NULL);
#endif

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Fabric busy. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_INTERMED_RSP:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Intermediate response. did=0x%06x sbp=%p cmd=%02x", did,
                    sbp, scsi_opcode);
                break;

        case IOSTAT_LS_RJT:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "LS Reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
                    scsi_opcode);
                break;

        case IOSTAT_DATA_UNDERRUN:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Underrun. did=0x%06x sbp=%p cmd=%02x "
                    "dl=%d,%d rx=%d rsp=%d (%02x,%02x,%02x,%02x)",
                    did, sbp, scsi_opcode, pkt->pkt_datalen, scsi_dl, data_rx,
                    rsp_data_resid, scsi_status, sense, asc, ascq);
                break;

        case IOSTAT_DATA_OVERRUN:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Overrun. did=0x%06x sbp=%p cmd=%02x "
                    "dl=%d,%d rx=%d rsp=%d (%02x,%02x,%02x,%02x)",
                    did, sbp, scsi_opcode, pkt->pkt_datalen, scsi_dl, data_rx,
                    rsp_data_resid, scsi_status, sense, asc, ascq);
                break;

        case IOSTAT_RSP_INVALID:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Rsp Invalid. did=0x%06x sbp=%p cmd=%02x dl=%d rl=%d"
                    "(%d, %d, %d)",
                    did, sbp, scsi_opcode, pkt->pkt_datalen, pkt->pkt_rsplen,
                    LE_SWAP32(rsp->fcp_resid),
                    LE_SWAP32(rsp->fcp_sense_len),
                    LE_SWAP32(rsp->fcp_response_len));
                break;

        default:
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
                    "Unknown status=%x reason=%x did=0x%06x sbp=%p cmd=%02x",
                    iostat, cmd->un.grsp.perr.statLocalError, did, sbp,
                    scsi_opcode);
                break;
        }

        if (iostat == IOSTAT_SUCCESS) {
                HBASTATS.FcpGood++;
        } else {
                HBASTATS.FcpError++;
        }

        mutex_exit(&sbp->mtx);

        emlxs_pkt_complete(sbp, iostat, localstat, 0);

        return;

} /* emlxs_handle_fcp_event() */


/*
 *  emlxs_post_buffer
 *
 *  This routine will post count buffers to the
 *  ring with the QUE_RING_BUF_CN command. This
 *  allows 2 buffers / command to be posted.
 *  Returns the number of buffers NOT posted.
 */
/* SLI3 */
extern int
emlxs_post_buffer(emlxs_hba_t *hba, RING *rp, int16_t cnt)
{
        emlxs_port_t *port = &PPORT;
        IOCB *icmd;
        IOCBQ *iocbq;
        MATCHMAP *mp;
        uint16_t tag;
        uint32_t maxqbuf;
        int32_t i;
        int32_t j;
        uint32_t seg;
        uint32_t size;

        mp = 0;
        maxqbuf = 2;
        tag = (uint16_t)cnt;
        cnt += rp->fc_missbufcnt;

        if (rp->ringno == hba->channel_els) {
                seg = MEM_BUF;
                size = MEM_ELSBUF_SIZE;
        } else if (rp->ringno == hba->channel_ip) {
                seg = MEM_IPBUF;
                size = MEM_IPBUF_SIZE;
        } else if (rp->ringno == hba->channel_ct) {
                seg = MEM_CTBUF;
                size = MEM_CTBUF_SIZE;
        }
#ifdef SFCT_SUPPORT
        else if (rp->ringno == hba->CHANNEL_FCT) {
                seg = MEM_FCTBUF;
                size = MEM_FCTBUF_SIZE;
        }
#endif /* SFCT_SUPPORT */
        else {
                return (0);
        }

        /*
         * While there are buffers to post
         */
        while (cnt) {
                if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == 0) {
                        rp->fc_missbufcnt = cnt;
                        return (cnt);
                }

                iocbq->channel = (void *)&hba->chan[rp->ringno];
                iocbq->port = (void *)port;
                iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);

                icmd = &iocbq->iocb;

                /*
                 * Max buffers can be posted per command
                 */
                for (i = 0; i < maxqbuf; i++) {
                        if (cnt <= 0)
                                break;

                        /* fill in BDEs for command */
                        if ((mp = (MATCHMAP *)emlxs_mem_get(hba, seg))
                            == 0) {
                                icmd->ULPBDECOUNT = i;
                                for (j = 0; j < i; j++) {
                                        mp = EMLXS_GET_VADDR(hba, rp, icmd);
                                        if (mp) {
                                                emlxs_mem_put(hba, seg,
                                                    (void *)mp);
                                        }
                                }

                                rp->fc_missbufcnt = cnt + i;

                                emlxs_mem_put(hba, MEM_IOCB, (void *)iocbq);

                                return (cnt + i);
                        }

                        /*
                         * map that page and save the address pair for lookup
                         * later
                         */
                        emlxs_mem_map_vaddr(hba,
                            rp,
                            mp,
                            (uint32_t *)&icmd->un.cont64[i].addrHigh,
                            (uint32_t *)&icmd->un.cont64[i].addrLow);

                        icmd->un.cont64[i].tus.f.bdeSize = size;
                        icmd->ULPCOMMAND = CMD_QUE_RING_BUF64_CN;

                        /*
                         * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                         *    "UB Post: ring=%d addr=%08x%08x size=%d",
                         *    rp->ringno, icmd->un.cont64[i].addrHigh,
                         *    icmd->un.cont64[i].addrLow, size);
                         */

                        cnt--;
                }

                icmd->ULPIOTAG = tag;
                icmd->ULPBDECOUNT = i;
                icmd->ULPLE = 1;
                icmd->ULPOWNER = OWN_CHIP;
                /* used for delimiter between commands */
                iocbq->bp = (void *)mp;

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[rp->ringno], iocbq);
        }

        rp->fc_missbufcnt = 0;

        return (0);

} /* emlxs_post_buffer() */


static void
emlxs_fcp_tag_nodes(emlxs_port_t *port)
{
        NODELIST *nlp;
        int i;

        /* We will process all nodes with this tag later */
        rw_enter(&port->node_rwlock, RW_READER);
        for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
                nlp = port->node_table[i];
                while (nlp != NULL) {
                        nlp->nlp_tag = 1;
                        nlp = nlp->nlp_list_next;
                }
        }
        rw_exit(&port->node_rwlock);
}


static NODELIST *
emlxs_find_tagged_node(emlxs_port_t *port)
{
        NODELIST *nlp;
        NODELIST *tagged;
        int i;

        /* Find first node */
        rw_enter(&port->node_rwlock, RW_READER);
        tagged = 0;
        for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
                nlp = port->node_table[i];
                while (nlp != NULL) {
                        if (!nlp->nlp_tag) {
                                nlp = nlp->nlp_list_next;
                                continue;
                        }
                        nlp->nlp_tag = 0;

                        if (nlp->nlp_Rpi == FABRIC_RPI) {
                                nlp = nlp->nlp_list_next;
                                continue;
                        }
                        tagged = nlp;
                        break;
                }
                if (tagged) {
                        break;
                }
        }
        rw_exit(&port->node_rwlock);
        return (tagged);
}


extern int
emlxs_port_offline(emlxs_port_t *port, uint32_t scope)
{
        emlxs_hba_t *hba = HBA;
        emlxs_config_t *cfg;
        NODELIST *nlp;
        fc_affected_id_t *aid;
        uint32_t mask;
        uint32_t aff_d_id;
        uint32_t linkdown;
        uint32_t vlinkdown;
        uint32_t action;
        int i;
        uint32_t unreg_vpi;
        uint32_t update;
        uint32_t adisc_support;
        uint32_t clear_all;
        uint8_t format;

        /* Target mode only uses this routine for linkdowns */
        if ((port->mode == MODE_TARGET) && (scope != 0xffffffff) &&
            (scope != 0xfeffffff) && (scope != 0xfdffffff)) {
                return (0);
        }

        cfg = &CFG;
        aid = (fc_affected_id_t *)&scope;
        linkdown = 0;
        vlinkdown = 0;
        unreg_vpi = 0;
        update = 0;
        clear_all = 0;

        if (!(port->flag & EMLXS_PORT_BOUND)) {
                return (0);
        }

        format = aid->aff_format;

        switch (format) {
        case 0: /* Port */
                mask = 0x00ffffff;
                break;

        case 1: /* Area */
                mask = 0x00ffff00;
                break;

        case 2: /* Domain */
                mask = 0x00ff0000;
                break;

        case 3: /* Network */
                mask = 0x00000000;
                break;

#ifdef DHCHAP_SUPPORT
        case 0xfe:      /* Virtual link down */
                mask = 0x00000000;
                vlinkdown = 1;
                break;
#endif /* DHCHAP_SUPPORT */

        case 0xff:      /* link is down */
                mask = 0x00000000;
                linkdown = 1;
                break;

        case 0xfd:      /* New fabric */
        default:
                mask = 0x00000000;
                linkdown = 1;
                clear_all = 1;
                break;
        }

        aff_d_id = aid->aff_d_id & mask;


        /*
         * If link is down then this is a hard shutdown and flush
         * If link not down then this is a soft shutdown and flush
         * (e.g. RSCN)
         */
        if (linkdown) {
                hba->flag &= ~FC_GPIO_LINK_UP;

                mutex_enter(&EMLXS_PORT_LOCK);

                port->flag &= EMLXS_PORT_LINKDOWN_MASK;

                if (port->ulp_statec != FC_STATE_OFFLINE) {
                        port->ulp_statec = FC_STATE_OFFLINE;

                        port->prev_did = port->did;
                        port->did = 0;
                        port->rdid = 0;

                        bcopy(&port->fabric_sparam, &port->prev_fabric_sparam,
                            sizeof (SERV_PARM));
                        bzero(&port->fabric_sparam, sizeof (SERV_PARM));

                        update = 1;
                }

                mutex_exit(&EMLXS_PORT_LOCK);

                emlxs_timer_cancel_clean_address(port);

                /* Tell ULP about it */
                if (update) {
                        if (port->flag & EMLXS_PORT_BOUND) {
                                if (port->vpi == 0) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_link_down_msg, NULL);
                                }

                                if (port->mode == MODE_INITIATOR) {
                                        emlxs_fca_link_down(port);
                                }
#ifdef SFCT_SUPPORT
                                else if (port->mode == MODE_TARGET) {
                                        emlxs_fct_link_down(port);
                                }
#endif /* SFCT_SUPPORT */

                        } else {
                                if (port->vpi == 0) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_link_down_msg, "*");
                                }
                        }


                }

                unreg_vpi = 1;

#ifdef DHCHAP_SUPPORT
                /* Stop authentication with all nodes */
                emlxs_dhc_auth_stop(port, NULL);
#endif /* DHCHAP_SUPPORT */

                /* Flush the base node */
                (void) emlxs_tx_node_flush(port, &port->node_base, 0, 0, 0);
                (void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0);

                /* Flush any pending ub buffers */
                emlxs_ub_flush(port);
        }
#ifdef DHCHAP_SUPPORT
        /* virtual link down */
        else if (vlinkdown) {
                mutex_enter(&EMLXS_PORT_LOCK);

                if (port->ulp_statec != FC_STATE_OFFLINE) {
                        port->ulp_statec = FC_STATE_OFFLINE;
                        update = 1;
                }

                mutex_exit(&EMLXS_PORT_LOCK);

                emlxs_timer_cancel_clean_address(port);

                /* Tell ULP about it */
                if (update) {
                        if (port->flag & EMLXS_PORT_BOUND) {
                                if (port->vpi == 0) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_link_down_msg,
                                            "Switch authentication failed.");
                                }

                                if (port->mode == MODE_INITIATOR) {
                                        emlxs_fca_link_down(port);
                                }
#ifdef SFCT_SUPPORT
                                else if (port->mode == MODE_TARGET) {
                                        emlxs_fct_link_down(port);
                                }
#endif /* SFCT_SUPPORT */
                        } else {
                                if (port->vpi == 0) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_link_down_msg,
                                            "Switch authentication failed. *");
                                }
                        }


                }

                /* Flush the base node */
                (void) emlxs_tx_node_flush(port, &port->node_base, 0, 0, 0);
                (void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0);
        }
#endif /* DHCHAP_SUPPORT */
        else {
                emlxs_timer_cancel_clean_address(port);
        }

        if (port->mode == MODE_TARGET) {
                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        /* Set the node tags */
                        emlxs_fcp_tag_nodes(port);
                        unreg_vpi = 0;
                        while ((nlp = emlxs_find_tagged_node(port))) {
                                (void) emlxs_rpi_pause_notify(port,
                                    nlp->rpip);
                                /*
                                 * In port_online we need to resume
                                 * these RPIs before we can use them.
                                 */
                        }
                }
                goto done;
        }

        /* Set the node tags */
        emlxs_fcp_tag_nodes(port);

        if (!clear_all && (hba->flag & FC_ONLINE_MODE)) {
                adisc_support = cfg[CFG_ADISC_SUPPORT].current;
        } else {
                adisc_support = 0;
        }

        /* Check ADISC support level */
        switch (adisc_support) {
        case 0: /* No support - Flush all IO to all matching nodes */

                for (;;) {
                        /*
                         * We need to hold the locks this way because
                         * EMLXS_SLI_UNREG_NODE and the flush routines enter the
                         * same locks. Also, when we release the lock the list
                         * can change out from under us.
                         */

                        /* Find first node */
                        rw_enter(&port->node_rwlock, RW_READER);
                        action = 0;
                        for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
                                nlp = port->node_table[i];
                                while (nlp != NULL) {
                                        if (!nlp->nlp_tag) {
                                                nlp = nlp->nlp_list_next;
                                                continue;
                                        }
                                        nlp->nlp_tag = 0;

                                        /*
                                         * Check for any device that matches
                                         * our mask
                                         */
                                        if ((nlp->nlp_DID & mask) == aff_d_id) {
                                                if (linkdown) {
                                                        action = 1;
                                                        break;
                                                } else { /* Must be an RCSN */

                                                        action = 2;
                                                        break;
                                                }
                                        }
                                        nlp = nlp->nlp_list_next;
                                }

                                if (action) {
                                        break;
                                }
                        }
                        rw_exit(&port->node_rwlock);


                        /* Check if nothing was found */
                        if (action == 0) {
                                break;
                        } else if (action == 1) {
                                (void) EMLXS_SLI_UNREG_NODE(port, nlp,
                                    NULL, NULL, NULL);
                        } else if (action == 2) {
                                EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);

#ifdef DHCHAP_SUPPORT
                                emlxs_dhc_auth_stop(port, nlp);
#endif /* DHCHAP_SUPPORT */

                                /*
                                 * Close the node for any further normal IO
                                 * A PLOGI with reopen the node
                                 */
                                emlxs_node_close(port, nlp,
                                    hba->channel_fcp, 60);
                                emlxs_node_close(port, nlp,
                                    hba->channel_ip, 60);

                                /* Flush tx queue */
                                (void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);

                                /* Flush chip queue */
                                (void) emlxs_chipq_node_flush(port, 0, nlp, 0);
                        }

                }

                break;

        case 1: /* Partial support - Flush IO for non-FCP2 matching nodes */

                for (;;) {

                        /*
                         * We need to hold the locks this way because
                         * EMLXS_SLI_UNREG_NODE and the flush routines enter the
                         * same locks. Also, when we release the lock the list
                         * can change out from under us.
                         */
                        rw_enter(&port->node_rwlock, RW_READER);
                        action = 0;
                        for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
                                nlp = port->node_table[i];
                                while (nlp != NULL) {
                                        if (!nlp->nlp_tag) {
                                                nlp = nlp->nlp_list_next;
                                                continue;
                                        }
                                        nlp->nlp_tag = 0;

                                        /*
                                         * Check for special FCP2 target device
                                         * that matches our mask
                                         */
                                        if ((nlp->nlp_fcp_info &
                                            NLP_FCP_TGT_DEVICE) &&
                                            (nlp-> nlp_fcp_info &
                                            NLP_FCP_2_DEVICE) &&
                                            (nlp->nlp_DID & mask) ==
                                            aff_d_id) {
                                                action = 3;
                                                break;
                                        }

                                        /*
                                         * Check for any other device that
                                         * matches our mask
                                         */
                                        else if ((nlp->nlp_DID & mask) ==
                                            aff_d_id) {
                                                if (linkdown) {
                                                        action = 1;
                                                        break;
                                                } else { /* Must be an RSCN */

                                                        action = 2;
                                                        break;
                                                }
                                        }

                                        nlp = nlp->nlp_list_next;
                                }

                                if (action) {
                                        break;
                                }
                        }
                        rw_exit(&port->node_rwlock);

                        /* Check if nothing was found */
                        if (action == 0) {
                                break;
                        } else if (action == 1) {
                                (void) EMLXS_SLI_UNREG_NODE(port, nlp,
                                    NULL, NULL, NULL);
                        } else if (action == 2) {
                                EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);

#ifdef DHCHAP_SUPPORT
                                emlxs_dhc_auth_stop(port, nlp);
#endif /* DHCHAP_SUPPORT */

                                /*
                                 * Close the node for any further normal IO
                                 * A PLOGI with reopen the node
                                 */
                                emlxs_node_close(port, nlp,
                                    hba->channel_fcp, 60);
                                emlxs_node_close(port, nlp,
                                    hba->channel_ip, 60);

                                /* Flush tx queue */
                                (void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);

                                /* Flush chip queue */
                                (void) emlxs_chipq_node_flush(port, 0, nlp, 0);

                        } else if (action == 3) {       /* FCP2 devices */
                                EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);

                                unreg_vpi = 0;

                                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                                        (void) emlxs_rpi_pause_notify(port,
                                            nlp->rpip);
                                }

#ifdef DHCHAP_SUPPORT
                                emlxs_dhc_auth_stop(port, nlp);
#endif /* DHCHAP_SUPPORT */

                                /*
                                 * Close the node for any further normal IO
                                 * An ADISC or a PLOGI with reopen the node
                                 */
                                emlxs_node_close(port, nlp,
                                    hba->channel_fcp, -1);
                                emlxs_node_close(port, nlp, hba->channel_ip,
                                    ((linkdown) ? 0 : 60));

                                /* Flush tx queues except for FCP ring */
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_ct], 0, 0);
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_els], 0, 0);
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_ip], 0, 0);

                                /* Flush chip queues except for FCP ring */
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_ct], nlp, 0);
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_els], nlp, 0);
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_ip], nlp, 0);
                        }
                }
                break;

        case 2: /* Full support - Hold FCP IO to FCP target matching nodes */

                if (!linkdown && !vlinkdown) {
                        break;
                }

                for (;;) {
                        /*
                         * We need to hold the locks this way because
                         * EMLXS_SLI_UNREG_NODE and the flush routines enter the
                         * same locks. Also, when we release the lock the list
                         * can change out from under us.
                         */
                        rw_enter(&port->node_rwlock, RW_READER);
                        action = 0;
                        for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
                                nlp = port->node_table[i];
                                while (nlp != NULL) {
                                        if (!nlp->nlp_tag) {
                                                nlp = nlp->nlp_list_next;
                                                continue;
                                        }
                                        nlp->nlp_tag = 0;

                                        /*
                                         * Check for FCP target device that
                                         * matches our mask
                                         */
                                        if ((nlp-> nlp_fcp_info &
                                            NLP_FCP_TGT_DEVICE) &&
                                            (nlp->nlp_DID & mask) ==
                                            aff_d_id) {
                                                action = 3;
                                                break;
                                        }

                                        /*
                                         * Check for any other device that
                                         * matches our mask
                                         */
                                        else if ((nlp->nlp_DID & mask) ==
                                            aff_d_id) {
                                                if (linkdown) {
                                                        action = 1;
                                                        break;
                                                } else { /* Must be an RSCN */

                                                        action = 2;
                                                        break;
                                                }
                                        }

                                        nlp = nlp->nlp_list_next;
                                }
                                if (action) {
                                        break;
                                }
                        }
                        rw_exit(&port->node_rwlock);

                        /* Check if nothing was found */
                        if (action == 0) {
                                break;
                        } else if (action == 1) {
                                (void) EMLXS_SLI_UNREG_NODE(port, nlp,
                                    NULL, NULL, NULL);
                        } else if (action == 2) {
                                EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);

                                /*
                                 * Close the node for any further normal IO
                                 * A PLOGI with reopen the node
                                 */
                                emlxs_node_close(port, nlp,
                                    hba->channel_fcp, 60);
                                emlxs_node_close(port, nlp,
                                    hba->channel_ip, 60);

                                /* Flush tx queue */
                                (void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);

                                /* Flush chip queue */
                                (void) emlxs_chipq_node_flush(port, 0, nlp, 0);

                        } else if (action == 3) {       /* FCP2 devices */
                                EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);

                                unreg_vpi = 0;

                                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                                        (void) emlxs_rpi_pause_notify(port,
                                            nlp->rpip);
                                }

                                /*
                                 * Close the node for any further normal IO
                                 * An ADISC or a PLOGI with reopen the node
                                 */
                                emlxs_node_close(port, nlp,
                                    hba->channel_fcp, -1);
                                emlxs_node_close(port, nlp, hba->channel_ip,
                                    ((linkdown) ? 0 : 60));

                                /* Flush tx queues except for FCP ring */
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_ct], 0, 0);
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_els], 0, 0);
                                (void) emlxs_tx_node_flush(port, nlp,
                                    &hba->chan[hba->channel_ip], 0, 0);

                                /* Flush chip queues except for FCP ring */
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_ct], nlp, 0);
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_els], nlp, 0);
                                (void) emlxs_chipq_node_flush(port,
                                    &hba->chan[hba->channel_ip], nlp, 0);
                        }
                }

                break;

        }       /* switch() */

done:

        if (unreg_vpi) {
                (void) emlxs_mb_unreg_vpi(port);
        }

        return (0);

} /* emlxs_port_offline() */


extern void
emlxs_port_online(emlxs_port_t *vport)
{
        emlxs_hba_t *hba = vport->hba;
        emlxs_port_t *port = &PPORT;
        NODELIST *nlp;
        uint32_t state;
        uint32_t update;
        uint32_t npiv_linkup;
        char topology[32];
        char linkspeed[32];
        char mode[32];

        /*
         * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_up_msg,
         *    "linkup_callback. vpi=%d fc_flag=%x", vport->vpi, hba->flag);
         */

        if ((vport->vpi > 0) &&
            (!(hba->flag & FC_NPIV_ENABLED) ||
            !(hba->flag & FC_NPIV_SUPPORTED))) {
                return;
        }

        if (!(vport->flag & EMLXS_PORT_BOUND) ||
            !(vport->flag & EMLXS_PORT_ENABLED)) {
                return;
        }

        /* Check for mode */
        if (port->mode == MODE_TARGET) {
                (void) strlcpy(mode, ", target", sizeof (mode));

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        /* Set the node tags */
                        emlxs_fcp_tag_nodes(vport);
                        while ((nlp = emlxs_find_tagged_node(vport))) {
                                /* The RPI was paused in port_offline */
                                (void) emlxs_rpi_resume_notify(vport,
                                    nlp->rpip, 0);
                        }
                }
        } else if (port->mode == MODE_INITIATOR) {
                (void) strlcpy(mode, ", initiator", sizeof (mode));
        } else {
                (void) strlcpy(mode, "unknown", sizeof (mode));
        }
        mutex_enter(&EMLXS_PORT_LOCK);

        /* Check for loop topology */
        if (hba->topology == TOPOLOGY_LOOP) {
                state = FC_STATE_LOOP;
                (void) strlcpy(topology, ", loop", sizeof (topology));
        } else {
                state = FC_STATE_ONLINE;
                (void) strlcpy(topology, ", fabric", sizeof (topology));
        }

        /* Set the link speed */
        switch (hba->linkspeed) {
        case 0:
                (void) strlcpy(linkspeed, "Gb", sizeof (linkspeed));
                state |= FC_STATE_1GBIT_SPEED;
                break;

        case LA_1GHZ_LINK:
                (void) strlcpy(linkspeed, "1Gb", sizeof (linkspeed));
                state |= FC_STATE_1GBIT_SPEED;
                break;
        case LA_2GHZ_LINK:
                (void) strlcpy(linkspeed, "2Gb", sizeof (linkspeed));
                state |= FC_STATE_2GBIT_SPEED;
                break;
        case LA_4GHZ_LINK:
                (void) strlcpy(linkspeed, "4Gb", sizeof (linkspeed));
                state |= FC_STATE_4GBIT_SPEED;
                break;
        case LA_8GHZ_LINK:
                (void) strlcpy(linkspeed, "8Gb", sizeof (linkspeed));
                state |= FC_STATE_8GBIT_SPEED;
                break;
        case LA_10GHZ_LINK:
                (void) strlcpy(linkspeed, "10Gb", sizeof (linkspeed));
                state |= FC_STATE_10GBIT_SPEED;
                break;
        case LA_16GHZ_LINK:
                (void) strlcpy(linkspeed, "16Gb", sizeof (linkspeed));
                state |= FC_STATE_16GBIT_SPEED;
                break;
        case LA_32GHZ_LINK:
                (void) strlcpy(linkspeed, "32Gb", sizeof (linkspeed));
                state |= FC_STATE_32GBIT_SPEED;
                break;
        default:
                (void) snprintf(linkspeed, sizeof (linkspeed), "unknown(0x%x)",
                    hba->linkspeed);
                break;
        }

        npiv_linkup = 0;
        update = 0;

        if ((hba->state >= FC_LINK_UP) &&
            !(hba->flag & FC_LOOPBACK_MODE) && (vport->ulp_statec != state)) {
                update = 1;
                vport->ulp_statec = state;

                if ((vport->vpi > 0) && !(hba->flag & FC_NPIV_LINKUP)) {
                        hba->flag |= FC_NPIV_LINKUP;
                        npiv_linkup = 1;
                }
        }

        mutex_exit(&EMLXS_PORT_LOCK);

        if (update) {
                if (vport->flag & EMLXS_PORT_BOUND) {
                        if (vport->vpi == 0) {
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_up_msg,
                                    "%s%s%s", linkspeed, topology, mode);

                        } else if (npiv_linkup) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_npiv_link_up_msg, "%s%s%s",
                                    linkspeed, topology, mode);
                        }

                        if (vport->mode == MODE_INITIATOR) {
                                emlxs_fca_link_up(vport);
                        }
#ifdef SFCT_SUPPORT
                        else if (vport->mode == MODE_TARGET) {
                                emlxs_fct_link_up(vport);
                        }
#endif /* SFCT_SUPPORT */
                } else {
                        if (vport->vpi == 0) {
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_up_msg,
                                    "%s%s%s *", linkspeed, topology, mode);

                        } else if (npiv_linkup) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_npiv_link_up_msg, "%s%s%s *",
                                    linkspeed, topology, mode);
                        }
                }

                /* Check for waiting threads */
                if (vport->vpi == 0) {
                        mutex_enter(&EMLXS_LINKUP_LOCK);
                        if (hba->linkup_wait_flag == TRUE) {
                                hba->linkup_wait_flag = FALSE;
                                cv_broadcast(&EMLXS_LINKUP_CV);
                        }
                        mutex_exit(&EMLXS_LINKUP_LOCK);
                }

                /* Flush any pending ub buffers */
                emlxs_ub_flush(vport);
        }

        hba->flag |= FC_GPIO_LINK_UP;

        return;

} /* emlxs_port_online() */


/* SLI3 */
extern void
emlxs_linkdown(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        int i;
        uint32_t scope;

        mutex_enter(&EMLXS_PORT_LOCK);

        if (hba->state > FC_LINK_DOWN) {
                HBASTATS.LinkDown++;
                EMLXS_STATE_CHANGE_LOCKED(hba, FC_LINK_DOWN);
        }

        /* Set scope */
        scope = (hba->flag & FC_NEW_FABRIC)? 0xFDFFFFFF:0xFFFFFFFF;

        /* Filter hba flags */
        hba->flag &= FC_LINKDOWN_MASK;
        hba->discovery_timer = 0;
        hba->linkup_timer = 0;

        mutex_exit(&EMLXS_PORT_LOCK);

        for (i = 0; i < MAX_VPORTS; i++) {
                port = &VPORT(i);

                if (!(port->flag & EMLXS_PORT_BOUND)) {
                        continue;
                }

                (void) emlxs_port_offline(port, scope);

        }

        emlxs_log_link_event(port);

        return;

} /* emlxs_linkdown() */


/* SLI3 */
extern void
emlxs_linkup(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        emlxs_config_t *cfg = &CFG;

        mutex_enter(&EMLXS_PORT_LOCK);

        /* Check for any mode changes */
        emlxs_mode_set(hba);

        HBASTATS.LinkUp++;
        EMLXS_STATE_CHANGE_LOCKED(hba, FC_LINK_UP);

#ifdef MENLO_SUPPORT
        if (hba->flag & FC_MENLO_MODE) {
                mutex_exit(&EMLXS_PORT_LOCK);

                /*
                 * Trigger linkup CV and don't start linkup & discovery
                 * timers
                 */
                mutex_enter(&EMLXS_LINKUP_LOCK);
                cv_broadcast(&EMLXS_LINKUP_CV);
                mutex_exit(&EMLXS_LINKUP_LOCK);

                emlxs_log_link_event(port);

                return;
        }
#endif /* MENLO_SUPPORT */

        /* Set the linkup & discovery timers */
        hba->linkup_timer = hba->timer_tics + cfg[CFG_LINKUP_TIMEOUT].current;
        hba->discovery_timer =
            hba->timer_tics + cfg[CFG_LINKUP_TIMEOUT].current +
            cfg[CFG_DISC_TIMEOUT].current;

        mutex_exit(&EMLXS_PORT_LOCK);

        emlxs_log_link_event(port);

        return;

} /* emlxs_linkup() */


/*
 *  emlxs_reset_link
 *
 *  Description:
 *  Called to reset the link with an init_link
 *
 *    Returns:
 *
 */
extern int
emlxs_reset_link(emlxs_hba_t *hba, uint32_t linkup, uint32_t wait)
{
        emlxs_port_t *port = &PPORT;
        emlxs_config_t *cfg;
        MAILBOXQ *mbq = NULL;
        MAILBOX *mb = NULL;
        int rval = 0;
        int tmo;
        int rc;

        /*
         * Get a buffer to use for the mailbox command
         */
        if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))
            == NULL) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_failed_msg,
                    "Unable to allocate mailbox buffer.");
                rval = 1;
                goto reset_link_fail;
        }

        if (linkup) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
                    "Resetting link...");
        } else {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
                    "Disabling link...");
        }

        mb = (MAILBOX *)mbq;

        /* Bring link down first */
        emlxs_mb_down_link(hba, mbq);

#define MBXERR_LINK_DOWN        0x33

        if (wait) {
                wait = MBX_WAIT;
        } else {
                wait = MBX_NOWAIT;
        }
        rc =  EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, wait, 0);
        if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS) &&
            (rc != MBXERR_LINK_DOWN)) {
                rval = 1;
                goto reset_link_fail;
        }

        tmo = 120;
        do {
                delay(drv_usectohz(500000));
                tmo--;

                if (!tmo)   {
                        rval = 1;

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
                            "Linkdown timeout.");

                        goto reset_link_fail;
                }
        } while ((hba->state >= FC_LINK_UP) && (hba->state != FC_ERROR));

        if (linkup) {
                /*
                 * Setup and issue mailbox INITIALIZE LINK command
                 */

                if (wait == MBX_NOWAIT) {
                        if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))
                            == NULL) {
                                EMLXS_MSGF(EMLXS_CONTEXT,
                                    &emlxs_link_reset_failed_msg,
                                    "Unable to allocate mailbox buffer.");
                                rval = 1;
                                goto reset_link_fail;
                        }
                        mb = (MAILBOX *)mbq;
                } else {
                        /* Reuse mbq from previous mbox */
                        mb = (MAILBOX *)mbq;
                }
                cfg = &CFG;

                emlxs_mb_init_link(hba, mbq,
                    cfg[CFG_TOPOLOGY].current, cfg[CFG_LINK_SPEED].current);

                mb->un.varInitLnk.lipsr_AL_PA = 0;

                /* Clear the loopback mode */
                mutex_enter(&EMLXS_PORT_LOCK);
                hba->flag &= ~FC_LOOPBACK_MODE;
                hba->loopback_tics = 0;
                mutex_exit(&EMLXS_PORT_LOCK);

                rc =  EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, wait, 0);
                if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
                        rval = 1;
                        goto reset_link_fail;
                }

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg, NULL);
        }

reset_link_fail:

        if ((wait == MBX_WAIT) && mbq) {
                emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
        }

        return (rval);
} /* emlxs_reset_link() */


extern int
emlxs_online(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        int32_t rval = 0;
        uint32_t i = 0;

        /* Make sure adapter is offline or exit trying (30 seconds) */
        while (i++ < 30) {
                /* Check if adapter is already going online */
                if (hba->flag & (FC_ONLINE_MODE | FC_ONLINING_MODE)) {
                        return (0);
                }

                mutex_enter(&EMLXS_PORT_LOCK);

                /* Check again */
                if (hba->flag & (FC_ONLINE_MODE | FC_ONLINING_MODE)) {
                        mutex_exit(&EMLXS_PORT_LOCK);
                        return (0);
                }

                /* Check if adapter is offline */
                if (hba->flag & FC_OFFLINE_MODE) {
                        /* Mark it going online */
                        hba->flag &= ~FC_OFFLINE_MODE;
                        hba->flag |= FC_ONLINING_MODE;

                        /* Currently !FC_ONLINE_MODE and !FC_OFFLINE_MODE */
                        mutex_exit(&EMLXS_PORT_LOCK);
                        break;
                }

                mutex_exit(&EMLXS_PORT_LOCK);

                BUSYWAIT_MS(1000);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_adapter_trans_msg,
            "Going online...");

        if (rval = EMLXS_SLI_ONLINE(hba)) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "status=%x",
                    rval);
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_offline_msg, NULL);

                /* Set FC_OFFLINE_MODE */
                mutex_enter(&EMLXS_PORT_LOCK);
                hba->flag |= FC_OFFLINE_MODE;
                hba->flag &= ~FC_ONLINING_MODE;
                mutex_exit(&EMLXS_PORT_LOCK);

                return (rval);
        }

        /* Start the timer */
        emlxs_timer_start(hba);

        /* Set FC_ONLINE_MODE */
        mutex_enter(&EMLXS_PORT_LOCK);
        hba->flag |= FC_ONLINE_MODE;
        hba->flag &= ~FC_ONLINING_MODE;
        mutex_exit(&EMLXS_PORT_LOCK);

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_online_msg, NULL);

#ifdef SFCT_SUPPORT
        if (port->flag & EMLXS_TGT_ENABLED) {
                (void) emlxs_fct_port_initialize(port);
        }
#endif /* SFCT_SUPPORT */

        return (rval);

} /* emlxs_online() */


extern int
emlxs_offline(emlxs_hba_t *hba, uint32_t reset_requested)
{
        emlxs_port_t *port = &PPORT;
        uint32_t i = 0;
        int rval = 1;

        /* Make sure adapter is online or exit trying (30 seconds) */
        while (i++ < 30) {
                /* Check if adapter is already going offline */
                if (hba->flag & (FC_OFFLINE_MODE | FC_OFFLINING_MODE)) {
                        return (0);
                }

                mutex_enter(&EMLXS_PORT_LOCK);

                /* Check again */
                if (hba->flag & (FC_OFFLINE_MODE | FC_OFFLINING_MODE)) {
                        mutex_exit(&EMLXS_PORT_LOCK);
                        return (0);
                }

                /* Check if adapter is online */
                if (hba->flag & FC_ONLINE_MODE) {
                        /* Mark it going offline */
                        hba->flag &= ~FC_ONLINE_MODE;
                        hba->flag |= FC_OFFLINING_MODE;

                        /* Currently !FC_ONLINE_MODE and !FC_OFFLINE_MODE */
                        mutex_exit(&EMLXS_PORT_LOCK);
                        break;
                }

                mutex_exit(&EMLXS_PORT_LOCK);

                BUSYWAIT_MS(1000);
        }

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_adapter_trans_msg,
            "Going offline...");

        /* Declare link down */
        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                (void) emlxs_fcf_shutdown_notify(port, 1);
        } else {
                emlxs_linkdown(hba);
        }

#ifdef SFCT_SUPPORT
        if (port->flag & EMLXS_TGT_ENABLED) {
                (void) emlxs_fct_port_shutdown(port);
        }
#endif /* SFCT_SUPPORT */

        /* Check if adapter was shutdown */
        if (hba->flag & FC_HARDWARE_ERROR) {
                /*
                 * Force mailbox cleanup
                 * This will wake any sleeping or polling threads
                 */
                emlxs_mb_fini(hba, NULL, MBX_HARDWARE_ERROR);
        }

        /* Pause here for the IO to settle */
        delay(drv_usectohz(1000000));   /* 1 sec */

        /* Unregister all nodes */
        emlxs_ffcleanup(hba);

        if (hba->bus_type == SBUS_FC) {
                WRITE_SBUS_CSR_REG(hba, FC_SHS_REG(hba), 0x9A);
#ifdef FMA_SUPPORT
                /* Access handle validation */
                EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.sbus_csr_handle);
#endif  /* FMA_SUPPORT */
        }

        /* Stop the timer */
        emlxs_timer_stop(hba);

        /* For safety flush every iotag list */
        if (emlxs_iotag_flush(hba)) {
                /* Pause here for the IO to flush */
                delay(drv_usectohz(1000));
        }

        /* Wait for poll command request to settle */
        while (hba->io_poll_count > 0) {
                delay(drv_usectohz(2000000));   /* 2 sec */
        }

        /* Shutdown the adapter interface */
        EMLXS_SLI_OFFLINE(hba, reset_requested);

        mutex_enter(&EMLXS_PORT_LOCK);
        hba->flag |= FC_OFFLINE_MODE;
        hba->flag &= ~FC_OFFLINING_MODE;
        mutex_exit(&EMLXS_PORT_LOCK);

        rval = 0;

        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_offline_msg, NULL);

        return (rval);

} /* emlxs_offline() */



extern int
emlxs_power_down(emlxs_hba_t *hba)
{
#ifdef FMA_SUPPORT
        emlxs_port_t *port = &PPORT;
#endif  /* FMA_SUPPORT */
        int32_t rval = 0;

        if ((rval = emlxs_offline(hba, 0))) {
                return (rval);
        }
        EMLXS_SLI_HBA_RESET(hba, 1, 1, 0);


#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);
                return (1);
        }
#endif  /* FMA_SUPPORT */

        return (0);

} /* End emlxs_power_down */


extern int
emlxs_power_up(emlxs_hba_t *hba)
{
#ifdef FMA_SUPPORT
        emlxs_port_t *port = &PPORT;
#endif  /* FMA_SUPPORT */
        int32_t rval = 0;


#ifdef FMA_SUPPORT
        if (emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle)
            != DDI_FM_OK) {
                EMLXS_MSGF(EMLXS_CONTEXT,
                    &emlxs_invalid_access_handle_msg, NULL);
                return (1);
        }
#endif  /* FMA_SUPPORT */

        /* Bring adapter online */
        if ((rval = emlxs_online(hba))) {
                if (hba->pci_cap_offset[PCI_CAP_ID_PM]) {
                        /* Put chip in D3 state */
                        (void) ddi_put8(hba->pci_acc_handle,
                            (uint8_t *)(hba->pci_addr +
                            hba->pci_cap_offset[PCI_CAP_ID_PM] +
                            PCI_PMCSR),
                            (uint8_t)PCI_PMCSR_D3HOT);
                }
                return (rval);
        }

        return (rval);

} /* emlxs_power_up() */


/*
 *
 * NAME:     emlxs_ffcleanup
 *
 * FUNCTION: Cleanup all the Firefly resources used by configuring the adapter
 *
 * EXECUTION ENVIRONMENT: process only
 *
 * CALLED FROM: CFG_TERM
 *
 * INPUT: hba       - pointer to the dev_ctl area.
 *
 * RETURNS: none
 */
extern void
emlxs_ffcleanup(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        uint32_t i;

        /* Disable all but the mailbox interrupt */
        EMLXS_SLI_DISABLE_INTR(hba, HC_MBINT_ENA);

        /* Make sure all port nodes are destroyed */
        for (i = 0; i < MAX_VPORTS; i++) {
                port = &VPORT(i);

                if (port->node_count) {
                        (void) EMLXS_SLI_UNREG_NODE(port, 0, 0, 0, 0);
                }
        }

        /* Clear all interrupt enable conditions */
        EMLXS_SLI_DISABLE_INTR(hba, 0);

        return;

} /* emlxs_ffcleanup() */


extern uint16_t
emlxs_register_pkt(CHANNEL *cp, emlxs_buf_t *sbp)
{
        emlxs_hba_t *hba;
        emlxs_port_t *port;
        uint16_t iotag;
        uint32_t i;

        hba = cp->hba;

        mutex_enter(&EMLXS_FCTAB_LOCK);

        if (sbp->iotag != 0) {
                port = &PPORT;

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                    "Pkt already registered! channel=%d iotag=%d sbp=%p",
                    sbp->channel, sbp->iotag, sbp);
        }

        iotag = 0;
        for (i = 0; i < hba->max_iotag; i++) {
                if (!hba->fc_iotag || hba->fc_iotag >= hba->max_iotag) {
                        hba->fc_iotag = 1;
                }
                iotag = hba->fc_iotag++;

                if (hba->fc_table[iotag] == 0 ||
                    hba->fc_table[iotag] == STALE_PACKET) {
                        hba->io_count++;
                        hba->fc_table[iotag] = sbp;

                        sbp->iotag = iotag;
                        sbp->channel = cp;

                        break;
                }
                iotag = 0;
        }

        mutex_exit(&EMLXS_FCTAB_LOCK);

        /*
         * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
         *    "register_pkt: channel=%d iotag=%d sbp=%p",
         *    cp->channelno, iotag, sbp);
         */

        return (iotag);

} /* emlxs_register_pkt() */



extern emlxs_buf_t *
emlxs_unregister_pkt(CHANNEL *cp, uint16_t iotag, uint32_t forced)
{
        emlxs_hba_t *hba;
        emlxs_buf_t *sbp;

        sbp = NULL;
        hba = cp->hba;

        /* Check the iotag range */
        if ((iotag == 0) || (iotag >= hba->max_iotag)) {
                return (NULL);
        }

        /* Remove the sbp from the table */
        mutex_enter(&EMLXS_FCTAB_LOCK);
        sbp = hba->fc_table[iotag];

        if (!sbp || (sbp == STALE_PACKET)) {
                mutex_exit(&EMLXS_FCTAB_LOCK);
                return (sbp);
        }

        hba->fc_table[iotag] = ((forced) ? STALE_PACKET : NULL);
        hba->io_count--;
        sbp->iotag = 0;

        mutex_exit(&EMLXS_FCTAB_LOCK);


        /* Clean up the sbp */
        mutex_enter(&sbp->mtx);

        if (sbp->pkt_flags & PACKET_IN_TXQ) {
                sbp->pkt_flags &= ~PACKET_IN_TXQ;
                hba->channel_tx_count--;
        }

        if (sbp->pkt_flags & PACKET_IN_CHIPQ) {
                sbp->pkt_flags &= ~PACKET_IN_CHIPQ;
        }

        if (sbp->bmp) {
                emlxs_mem_put(hba, MEM_BPL, (void *)sbp->bmp);
                sbp->bmp = 0;
        }

        mutex_exit(&sbp->mtx);

        return (sbp);

} /* emlxs_unregister_pkt() */



/* Flush all IO's to all nodes for a given IO Channel */
extern uint32_t
emlxs_tx_channel_flush(emlxs_hba_t *hba, CHANNEL *cp, emlxs_buf_t *fpkt)
{
        emlxs_port_t *port = &PPORT;
        emlxs_buf_t *sbp;
        IOCBQ *iocbq;
        IOCBQ *next;
        IOCB *iocb;
        uint32_t channelno;
        Q abort;
        NODELIST *ndlp;
        IOCB *icmd;
        MATCHMAP *mp;
        uint32_t i;
        uint8_t flag[MAX_CHANNEL];

        channelno = cp->channelno;
        bzero((void *)&abort, sizeof (Q));
        bzero((void *)flag, MAX_CHANNEL * sizeof (uint8_t));

        mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

        /* While a node needs servicing */
        while (cp->nodeq.q_first) {
                ndlp = (NODELIST *) cp->nodeq.q_first;

                /* Check if priority queue is not empty */
                if (ndlp->nlp_ptx[channelno].q_first) {
                        /* Transfer all iocb's to local queue */
                        if (abort.q_first == 0) {
                                abort.q_first =
                                    ndlp->nlp_ptx[channelno].q_first;
                        } else {
                                ((IOCBQ *)abort.q_last)->next =
                                    (IOCBQ *)ndlp->nlp_ptx[channelno].q_first;
                        }
                        flag[channelno] = 1;

                        abort.q_last = ndlp->nlp_ptx[channelno].q_last;
                        abort.q_cnt += ndlp->nlp_ptx[channelno].q_cnt;
                }

                /* Check if tx queue is not empty */
                if (ndlp->nlp_tx[channelno].q_first) {
                        /* Transfer all iocb's to local queue */
                        if (abort.q_first == 0) {
                                abort.q_first = ndlp->nlp_tx[channelno].q_first;
                        } else {
                                ((IOCBQ *)abort.q_last)->next =
                                    (IOCBQ *)ndlp->nlp_tx[channelno].q_first;
                        }

                        abort.q_last = ndlp->nlp_tx[channelno].q_last;
                        abort.q_cnt += ndlp->nlp_tx[channelno].q_cnt;
                }

                /* Clear the queue pointers */
                ndlp->nlp_ptx[channelno].q_first = NULL;
                ndlp->nlp_ptx[channelno].q_last = NULL;
                ndlp->nlp_ptx[channelno].q_cnt = 0;

                ndlp->nlp_tx[channelno].q_first = NULL;
                ndlp->nlp_tx[channelno].q_last = NULL;
                ndlp->nlp_tx[channelno].q_cnt = 0;

                /* Remove node from service queue */

                /* If this is the last node on list */
                if (cp->nodeq.q_last == (void *)ndlp) {
                        cp->nodeq.q_last = NULL;
                        cp->nodeq.q_first = NULL;
                        cp->nodeq.q_cnt = 0;
                } else {
                        /* Remove node from head */
                        cp->nodeq.q_first = ndlp->nlp_next[channelno];
                        ((NODELIST *)cp->nodeq.q_last)->nlp_next[channelno] =
                            cp->nodeq.q_first;
                        cp->nodeq.q_cnt--;
                }

                /* Clear node */
                ndlp->nlp_next[channelno] = NULL;
        }

        /* First cleanup the iocb's while still holding the lock */
        iocbq = (IOCBQ *) abort.q_first;
        while (iocbq) {
                /* Free the IoTag and the bmp */
                iocb = &iocbq->iocb;

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        sbp = iocbq->sbp;
                        if (sbp) {
                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
                        }
                } else {
                        sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
                            iocb->ULPIOTAG, 0);
                }

                if (sbp && (sbp != STALE_PACKET)) {
                        mutex_enter(&sbp->mtx);

                        sbp->pkt_flags |= PACKET_IN_FLUSH;
                        /*
                         * If the fpkt is already set, then we will leave it
                         * alone. This ensures that this pkt is only accounted
                         * for on one fpkt->flush_count
                         */
                        if (!sbp->fpkt && fpkt) {
                                mutex_enter(&fpkt->mtx);
                                sbp->fpkt = fpkt;
                                fpkt->flush_count++;
                                mutex_exit(&fpkt->mtx);
                        }

                        mutex_exit(&sbp->mtx);
                }

                iocbq = (IOCBQ *)iocbq->next;
        }       /* end of while */

        mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

        /* Now abort the iocb's */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Get the pkt */
                sbp = (emlxs_buf_t *)iocbq->sbp;

                if (sbp) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_flush_msg,
                            "tx: sbp=%p node=%p", sbp, sbp->node);

                        if (hba->state >= FC_LINK_UP) {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_ABORT_REQUESTED, 1);
                        } else {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_LINK_DOWN, 1);
                        }

                }
                /* Free the iocb and its associated buffers */
                else {
                        icmd = &iocbq->iocb;

                        /* SLI3 */
                        if (icmd->ULPCOMMAND == CMD_QUE_RING_BUF64_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_BUF_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_LIST64_CN) {
                                if ((hba->flag &
                                    (FC_ONLINE_MODE | FC_ONLINING_MODE)) == 0) {
                                        /* HBA is detaching or offlining */
                                        if (icmd->ULPCOMMAND !=
                                            CMD_QUE_RING_LIST64_CN) {
                                                void    *tmp;
                                                RING *rp;

                                                rp = &hba->sli.sli3.
                                                    ring[channelno];
                                                for (i = 0;
                                                    i < icmd->ULPBDECOUNT;
                                                    i++) {
                                                        mp = EMLXS_GET_VADDR(
                                                            hba, rp, icmd);

                                                        tmp = (void *)mp;
                                                        if (mp) {
                                                        emlxs_mem_put(
                                                            hba, MEM_BUF, tmp);
                                                        }
                                                }
                                        }

                                        emlxs_mem_put(hba, MEM_IOCB,
                                            (void *)iocbq);
                                } else {
                                        /* repost the unsolicited buffer */
                                        EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp,
                                            iocbq);
                                }
                        } else if (icmd->ULPCOMMAND == CMD_CLOSE_XRI_CN ||
                            icmd->ULPCOMMAND == CMD_CLOSE_XRI_CX) {

                                emlxs_tx_put(iocbq, 1);
                        }
                }

                iocbq = next;

        }       /* end of while */

        /* Now trigger channel service */
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                if (!flag[channelno]) {
                        continue;
                }

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[channelno], 0);
        }

        return (abort.q_cnt);

} /* emlxs_tx_channel_flush() */


/* Flush all IO's on all or a given ring for a given node */
extern uint32_t
emlxs_tx_node_flush(emlxs_port_t *port, NODELIST *ndlp, CHANNEL *chan,
    uint32_t shutdown, emlxs_buf_t *fpkt)
{
        emlxs_hba_t *hba = HBA;
        emlxs_buf_t *sbp;
        uint32_t channelno;
        CHANNEL *cp;
        IOCB *icmd;
        IOCBQ *iocbq;
        NODELIST *prev;
        IOCBQ *next;
        IOCB *iocb;
        Q abort;
        uint32_t i;
        MATCHMAP *mp;
        uint8_t flag[MAX_CHANNEL];

        bzero((void *)&abort, sizeof (Q));

        /* Flush all I/O's on tx queue to this target */
        mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

        if (!ndlp->nlp_base && shutdown) {
                ndlp->nlp_active = 0;
        }

        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                cp = &hba->chan[channelno];

                if (chan && cp != chan) {
                        continue;
                }

                if (!ndlp->nlp_base || shutdown) {
                        /* Check if priority queue is not empty */
                        if (ndlp->nlp_ptx[channelno].q_first) {
                                /* Transfer all iocb's to local queue */
                                if (abort.q_first == 0) {
                                        abort.q_first =
                                            ndlp->nlp_ptx[channelno].q_first;
                                } else {
                                        ((IOCBQ *)(abort.q_last))->next =
                                            (IOCBQ *)ndlp->nlp_ptx[channelno].
                                            q_first;
                                }

                                flag[channelno] = 1;

                                abort.q_last = ndlp->nlp_ptx[channelno].q_last;
                                abort.q_cnt += ndlp->nlp_ptx[channelno].q_cnt;
                        }
                }

                /* Check if tx queue is not empty */
                if (ndlp->nlp_tx[channelno].q_first) {

                        /* Transfer all iocb's to local queue */
                        if (abort.q_first == 0) {
                                abort.q_first = ndlp->nlp_tx[channelno].q_first;
                        } else {
                                ((IOCBQ *)abort.q_last)->next =
                                    (IOCBQ *)ndlp->nlp_tx[channelno].q_first;
                        }

                        abort.q_last = ndlp->nlp_tx[channelno].q_last;
                        abort.q_cnt += ndlp->nlp_tx[channelno].q_cnt;
                }

                /* Clear the queue pointers */
                ndlp->nlp_ptx[channelno].q_first = NULL;
                ndlp->nlp_ptx[channelno].q_last = NULL;
                ndlp->nlp_ptx[channelno].q_cnt = 0;

                ndlp->nlp_tx[channelno].q_first = NULL;
                ndlp->nlp_tx[channelno].q_last = NULL;
                ndlp->nlp_tx[channelno].q_cnt = 0;

                /* If this node was on the channel queue, remove it */
                if (ndlp->nlp_next[channelno]) {
                        /* If this is the only node on list */
                        if (cp->nodeq.q_first == (void *)ndlp &&
                            cp->nodeq.q_last == (void *)ndlp) {
                                cp->nodeq.q_last = NULL;
                                cp->nodeq.q_first = NULL;
                                cp->nodeq.q_cnt = 0;
                        } else if (cp->nodeq.q_first == (void *)ndlp) {
                                cp->nodeq.q_first = ndlp->nlp_next[channelno];
                                ((NODELIST *) cp->nodeq.q_last)->
                                    nlp_next[channelno] = cp->nodeq.q_first;
                                cp->nodeq.q_cnt--;
                        } else {
                                /*
                                 * This is a little more difficult find the
                                 * previous node in the circular channel queue
                                 */
                                prev = ndlp;
                                while (prev->nlp_next[channelno] != ndlp) {
                                        prev = prev->nlp_next[channelno];
                                }

                                prev->nlp_next[channelno] =
                                    ndlp->nlp_next[channelno];

                                if (cp->nodeq.q_last == (void *)ndlp) {
                                        cp->nodeq.q_last = (void *)prev;
                                }
                                cp->nodeq.q_cnt--;

                        }

                        /* Clear node */
                        ndlp->nlp_next[channelno] = NULL;
                }

        }

        /* First cleanup the iocb's while still holding the lock */
        iocbq = (IOCBQ *) abort.q_first;
        while (iocbq) {
                /* Free the IoTag and the bmp */
                iocb = &iocbq->iocb;

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        sbp = iocbq->sbp;
                        if (sbp) {
                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
                        }
                } else {
                        sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
                            iocb->ULPIOTAG, 0);
                }

                if (sbp && (sbp != STALE_PACKET)) {
                        mutex_enter(&sbp->mtx);
                        sbp->pkt_flags |= PACKET_IN_FLUSH;
                        /*
                         * If the fpkt is already set, then we will leave it
                         * alone. This ensures that this pkt is only accounted
                         * for on one fpkt->flush_count
                         */
                        if (!sbp->fpkt && fpkt) {
                                mutex_enter(&fpkt->mtx);
                                sbp->fpkt = fpkt;
                                fpkt->flush_count++;
                                mutex_exit(&fpkt->mtx);
                        }

                        mutex_exit(&sbp->mtx);
                }

                iocbq = (IOCBQ *) iocbq->next;

        }       /* end of while */

        mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

        /* Now abort the iocb's outside the locks */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Get the pkt */
                sbp = (emlxs_buf_t *)iocbq->sbp;

                if (sbp) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_flush_msg,
                            "tx: sbp=%p node=%p", sbp, sbp->node);

                        if (hba->state >= FC_LINK_UP) {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_ABORT_REQUESTED, 1);
                        } else {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_LINK_DOWN, 1);
                        }

                }
                /* Free the iocb and its associated buffers */
                else {
                        /* CMD_CLOSE_XRI_CN should also free the memory */
                        icmd = &iocbq->iocb;

                        /* SLI3 */
                        if (icmd->ULPCOMMAND == CMD_QUE_RING_BUF64_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_BUF_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_LIST64_CN) {
                                if ((hba->flag &
                                    (FC_ONLINE_MODE | FC_ONLINING_MODE)) == 0) {
                                        /* HBA is detaching or offlining */
                                        if (icmd->ULPCOMMAND !=
                                            CMD_QUE_RING_LIST64_CN) {
                                                void    *tmp;
                                                RING *rp;
                                                int ch;

                                                ch = ((CHANNEL *)
                                                    iocbq->channel)->channelno;
                                                rp = &hba->sli.sli3.ring[ch];
                                                for (i = 0;
                                                    i < icmd->ULPBDECOUNT;
                                                    i++) {
                                                        mp = EMLXS_GET_VADDR(
                                                            hba, rp, icmd);

                                                        tmp = (void *)mp;
                                                        if (mp) {
                                                        emlxs_mem_put(
                                                            hba, MEM_BUF, tmp);
                                                        }
                                                }
                                        }

                                        emlxs_mem_put(hba, MEM_IOCB,
                                            (void *)iocbq);
                                } else {
                                        /* repost the unsolicited buffer */
                                        EMLXS_SLI_ISSUE_IOCB_CMD(hba,
                                            (CHANNEL *)iocbq->channel, iocbq);
                                }
                        } else if (icmd->ULPCOMMAND == CMD_CLOSE_XRI_CN ||
                            icmd->ULPCOMMAND == CMD_CLOSE_XRI_CX) {
                                /*
                                 * Resend the abort iocbq if any
                                 */
                                emlxs_tx_put(iocbq, 1);
                        }
                }

                iocbq = next;

        }       /* end of while */

        /* Now trigger channel service */
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                if (!flag[channelno]) {
                        continue;
                }

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[channelno], 0);
        }

        return (abort.q_cnt);

} /* emlxs_tx_node_flush() */


/* Check for IO's on all or a given ring for a given node */
extern uint32_t
emlxs_tx_node_check(emlxs_port_t *port, NODELIST *ndlp, CHANNEL *chan)
{
        emlxs_hba_t *hba = HBA;
        uint32_t channelno;
        CHANNEL *cp;
        uint32_t count;

        count = 0;

        /* Flush all I/O's on tx queue to this target */
        mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                cp = &hba->chan[channelno];

                if (chan && cp != chan) {
                        continue;
                }

                /* Check if priority queue is not empty */
                if (ndlp->nlp_ptx[channelno].q_first) {
                        count += ndlp->nlp_ptx[channelno].q_cnt;
                }

                /* Check if tx queue is not empty */
                if (ndlp->nlp_tx[channelno].q_first) {
                        count += ndlp->nlp_tx[channelno].q_cnt;
                }

        }

        mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

        return (count);

} /* emlxs_tx_node_check() */



/* Flush all IO's on the any ring for a given node's lun */
extern uint32_t
emlxs_tx_lun_flush(emlxs_port_t *port, NODELIST *ndlp, uint32_t lun,
    emlxs_buf_t *fpkt)
{
        emlxs_hba_t *hba = HBA;
        emlxs_buf_t *sbp;
        uint32_t channelno;
        IOCBQ *iocbq;
        IOCBQ *prev;
        IOCBQ *next;
        IOCB *iocb;
        IOCB *icmd;
        Q abort;
        uint32_t i;
        MATCHMAP *mp;
        uint8_t flag[MAX_CHANNEL];

        if (lun == EMLXS_LUN_NONE) {
                return (0);
        }

        bzero((void *)&abort, sizeof (Q));

        /* Flush I/O's on txQ to this target's lun */
        mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

        for (channelno = 0; channelno < hba->chan_count; channelno++) {

                /* Scan the priority queue first */
                prev = NULL;
                iocbq = (IOCBQ *) ndlp->nlp_ptx[channelno].q_first;

                while (iocbq) {
                        next = (IOCBQ *)iocbq->next;
                        iocb = &iocbq->iocb;
                        sbp = (emlxs_buf_t *)iocbq->sbp;

                        /* Check if this IO is for our lun */
                        if (sbp && (sbp->lun == lun)) {
                                /* Remove iocb from the node's ptx queue */
                                if (next == 0) {
                                        ndlp->nlp_ptx[channelno].q_last =
                                            (uint8_t *)prev;
                                }

                                if (prev == 0) {
                                        ndlp->nlp_ptx[channelno].q_first =
                                            (uint8_t *)next;
                                } else {
                                        prev->next = next;
                                }

                                iocbq->next = NULL;
                                ndlp->nlp_ptx[channelno].q_cnt--;

                                /*
                                 * Add this iocb to our local abort Q
                                 */
                                if (abort.q_first) {
                                        ((IOCBQ *)abort.q_last)->next = iocbq;
                                        abort.q_last = (uint8_t *)iocbq;
                                        abort.q_cnt++;
                                } else {
                                        abort.q_first = (uint8_t *)iocbq;
                                        abort.q_last = (uint8_t *)iocbq;
                                        abort.q_cnt = 1;
                                }
                                iocbq->next = NULL;
                                flag[channelno] = 1;

                        } else {
                                prev = iocbq;
                        }

                        iocbq = next;

                }       /* while (iocbq) */


                /* Scan the regular queue */
                prev = NULL;
                iocbq = (IOCBQ *)ndlp->nlp_tx[channelno].q_first;

                while (iocbq) {
                        next = (IOCBQ *)iocbq->next;
                        iocb = &iocbq->iocb;
                        sbp = (emlxs_buf_t *)iocbq->sbp;

                        /* Check if this IO is for our lun */
                        if (sbp && (sbp->lun == lun)) {
                                /* Remove iocb from the node's tx queue */
                                if (next == 0) {
                                        ndlp->nlp_tx[channelno].q_last =
                                            (uint8_t *)prev;
                                }

                                if (prev == 0) {
                                        ndlp->nlp_tx[channelno].q_first =
                                            (uint8_t *)next;
                                } else {
                                        prev->next = next;
                                }

                                iocbq->next = NULL;
                                ndlp->nlp_tx[channelno].q_cnt--;

                                /*
                                 * Add this iocb to our local abort Q
                                 */
                                if (abort.q_first) {
                                        ((IOCBQ *) abort.q_last)->next = iocbq;
                                        abort.q_last = (uint8_t *)iocbq;
                                        abort.q_cnt++;
                                } else {
                                        abort.q_first = (uint8_t *)iocbq;
                                        abort.q_last = (uint8_t *)iocbq;
                                        abort.q_cnt = 1;
                                }
                                iocbq->next = NULL;
                        } else {
                                prev = iocbq;
                        }

                        iocbq = next;

                }       /* while (iocbq) */
        }       /* for loop */

        /* First cleanup the iocb's while still holding the lock */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Free the IoTag and the bmp */
                iocb = &iocbq->iocb;

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        sbp = iocbq->sbp;
                        if (sbp) {
                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
                        }
                } else {
                        sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
                            iocb->ULPIOTAG, 0);
                }

                if (sbp && (sbp != STALE_PACKET)) {
                        mutex_enter(&sbp->mtx);
                        sbp->pkt_flags |= PACKET_IN_FLUSH;
                        /*
                         * If the fpkt is already set, then we will leave it
                         * alone. This ensures that this pkt is only accounted
                         * for on one fpkt->flush_count
                         */
                        if (!sbp->fpkt && fpkt) {
                                mutex_enter(&fpkt->mtx);
                                sbp->fpkt = fpkt;
                                fpkt->flush_count++;
                                mutex_exit(&fpkt->mtx);
                        }

                        mutex_exit(&sbp->mtx);
                }

                iocbq = (IOCBQ *) iocbq->next;

        }       /* end of while */

        mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

        /* Now abort the iocb's outside the locks */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Get the pkt */
                sbp = (emlxs_buf_t *)iocbq->sbp;

                if (sbp) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_flush_msg,
                            "tx: sbp=%p node=%p", sbp, sbp->node);

                        if (hba->state >= FC_LINK_UP) {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_ABORT_REQUESTED, 1);
                        } else {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_LINK_DOWN, 1);
                        }
                }

                /* Free the iocb and its associated buffers */
                else {
                        /* Should never happen! */
                        icmd = &iocbq->iocb;

                        /* SLI3 */
                        if (icmd->ULPCOMMAND == CMD_QUE_RING_BUF64_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_BUF_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_LIST64_CN) {
                                if ((hba->flag &
                                    (FC_ONLINE_MODE | FC_ONLINING_MODE)) == 0) {
                                        /* HBA is detaching or offlining */
                                        if (icmd->ULPCOMMAND !=
                                            CMD_QUE_RING_LIST64_CN) {
                                                void    *tmp;
                                                RING *rp;
                                                int ch;

                                                ch = ((CHANNEL *)
                                                    iocbq->channel)->channelno;
                                                rp = &hba->sli.sli3.ring[ch];
                                                for (i = 0;
                                                    i < icmd->ULPBDECOUNT;
                                                    i++) {
                                                        mp = EMLXS_GET_VADDR(
                                                            hba, rp, icmd);

                                                        tmp = (void *)mp;
                                                        if (mp) {
                                                        emlxs_mem_put(
                                                            hba, MEM_BUF, tmp);
                                                        }
                                                }
                                        }

                                        emlxs_mem_put(hba, MEM_IOCB,
                                            (void *)iocbq);
                                } else {
                                        /* repost the unsolicited buffer */
                                        EMLXS_SLI_ISSUE_IOCB_CMD(hba,
                                            (CHANNEL *)iocbq->channel, iocbq);
                                }
                        } else if (icmd->ULPCOMMAND == CMD_CLOSE_XRI_CN ||
                            icmd->ULPCOMMAND == CMD_CLOSE_XRI_CX) {
                                /*
                                 * Resend the abort iocbq if any
                                 */
                                emlxs_tx_put(iocbq, 1);
                        }
                }

                iocbq = next;

        }       /* end of while */

        /* Now trigger channel service */
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                if (!flag[channelno]) {
                        continue;
                }

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[channelno], 0);
        }

        return (abort.q_cnt);

} /* emlxs_tx_lun_flush() */


extern void
emlxs_tx_put(IOCBQ *iocbq, uint32_t lock)
{
        emlxs_hba_t *hba;
        emlxs_port_t *port;
        uint32_t channelno;
        NODELIST *nlp;
        CHANNEL *cp;
        emlxs_buf_t *sbp;

        port = (emlxs_port_t *)iocbq->port;
        hba = HBA;
        cp = (CHANNEL *)iocbq->channel;
        nlp = (NODELIST *)iocbq->node;
        channelno = cp->channelno;
        sbp = (emlxs_buf_t *)iocbq->sbp;

        if (nlp == NULL) {
                /* Set node to base node by default */
                nlp = &port->node_base;

                iocbq->node = (void *)nlp;

                if (sbp) {
                        sbp->node = (void *)nlp;
                }
        }

        if (lock) {
                mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
        }

        if (!nlp->nlp_active || (sbp && (sbp->pkt_flags & PACKET_IN_ABORT))) {
                if (sbp) {
                        mutex_enter(&sbp->mtx);
                        sbp->pkt_flags |= PACKET_IN_FLUSH;
                        mutex_exit(&sbp->mtx);

                        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
                        } else {
                                (void) emlxs_unregister_pkt(cp, sbp->iotag, 0);
                        }

                        if (lock) {
                                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
                        }

                        if (hba->state >= FC_LINK_UP) {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_ABORT_REQUESTED, 1);
                        } else {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_LINK_DOWN, 1);
                        }
                        return;
                } else {
                        if (lock) {
                                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
                        }

                        emlxs_mem_put(hba, MEM_IOCB, (void *)iocbq);
                }

                return;
        }

        if (sbp) {

                mutex_enter(&sbp->mtx);

                if (sbp->pkt_flags &
                    (PACKET_IN_COMPLETION | PACKET_IN_CHIPQ | PACKET_IN_TXQ)) {
                        mutex_exit(&sbp->mtx);
                        if (lock) {
                                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
                        }
                        return;
                }

                sbp->pkt_flags |= PACKET_IN_TXQ;
                hba->channel_tx_count++;

                mutex_exit(&sbp->mtx);
        }


        /* Check iocbq priority */
        /* Some IOCB has the high priority like reset/close xri etc */
        if (iocbq->flag & IOCB_PRIORITY) {
                /* Add the iocb to the bottom of the node's ptx queue */
                if (nlp->nlp_ptx[channelno].q_first) {
                        ((IOCBQ *)nlp->nlp_ptx[channelno].q_last)->next = iocbq;
                        nlp->nlp_ptx[channelno].q_last = (uint8_t *)iocbq;
                        nlp->nlp_ptx[channelno].q_cnt++;
                } else {
                        nlp->nlp_ptx[channelno].q_first = (uint8_t *)iocbq;
                        nlp->nlp_ptx[channelno].q_last = (uint8_t *)iocbq;
                        nlp->nlp_ptx[channelno].q_cnt = 1;
                }

                iocbq->next = NULL;
        } else {        /* Normal priority */


                /* Add the iocb to the bottom of the node's tx queue */
                if (nlp->nlp_tx[channelno].q_first) {
                        ((IOCBQ *)nlp->nlp_tx[channelno].q_last)->next = iocbq;
                        nlp->nlp_tx[channelno].q_last = (uint8_t *)iocbq;
                        nlp->nlp_tx[channelno].q_cnt++;
                } else {
                        nlp->nlp_tx[channelno].q_first = (uint8_t *)iocbq;
                        nlp->nlp_tx[channelno].q_last = (uint8_t *)iocbq;
                        nlp->nlp_tx[channelno].q_cnt = 1;
                }

                iocbq->next = NULL;
        }


        /*
         * Check if the node is not already on channel queue and
         * (is not closed or  is a priority request)
         */
        if (!nlp->nlp_next[channelno] &&
            (!(nlp->nlp_flag[channelno] & NLP_CLOSED) ||
            (iocbq->flag & IOCB_PRIORITY))) {
                /* If so, then add it to the channel queue */
                if (cp->nodeq.q_first) {
                        ((NODELIST *)cp->nodeq.q_last)->nlp_next[channelno] =
                            (uint8_t *)nlp;
                        nlp->nlp_next[channelno] = cp->nodeq.q_first;

                        /*
                         * If this is not the base node then add it
                         * to the tail
                         */
                        if (!nlp->nlp_base) {
                                cp->nodeq.q_last = (uint8_t *)nlp;
                        } else {        /* Otherwise, add it to the head */

                                /* The command node always gets priority */
                                cp->nodeq.q_first = (uint8_t *)nlp;
                        }

                        cp->nodeq.q_cnt++;
                } else {
                        cp->nodeq.q_first = (uint8_t *)nlp;
                        cp->nodeq.q_last = (uint8_t *)nlp;
                        nlp->nlp_next[channelno] = nlp;
                        cp->nodeq.q_cnt = 1;
                }
        }

        HBASTATS.IocbTxPut[channelno]++;

        /* Adjust the channel timeout timer */
        cp->timeout = hba->timer_tics + 5;

        if (lock) {
                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
        }

        return;

} /* emlxs_tx_put() */


extern IOCBQ *
emlxs_tx_get(CHANNEL *cp, uint32_t lock)
{
        emlxs_hba_t *hba;
        uint32_t channelno;
        IOCBQ *iocbq;
        NODELIST *nlp;
        emlxs_buf_t *sbp;

        hba = cp->hba;
        channelno = cp->channelno;

        if (lock) {
                mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
        }

begin:

        iocbq = NULL;

        /* Check if a node needs servicing */
        if (cp->nodeq.q_first) {
                nlp = (NODELIST *)cp->nodeq.q_first;

                /* Get next iocb from node's priority queue */

                if (nlp->nlp_ptx[channelno].q_first) {
                        iocbq = (IOCBQ *)nlp->nlp_ptx[channelno].q_first;

                        /* Check if this is last entry */
                        if (nlp->nlp_ptx[channelno].q_last == (void *)iocbq) {
                                nlp->nlp_ptx[channelno].q_first = NULL;
                                nlp->nlp_ptx[channelno].q_last = NULL;
                                nlp->nlp_ptx[channelno].q_cnt = 0;
                        } else {
                                /* Remove iocb from head */
                                nlp->nlp_ptx[channelno].q_first =
                                    (void *)iocbq->next;
                                nlp->nlp_ptx[channelno].q_cnt--;
                        }

                        iocbq->next = NULL;
                }

                /* Get next iocb from node tx queue if node not closed */
                else if (nlp->nlp_tx[channelno].q_first &&
                    !(nlp->nlp_flag[channelno] & NLP_CLOSED)) {
                        iocbq = (IOCBQ *)nlp->nlp_tx[channelno].q_first;

                        /* Check if this is last entry */
                        if (nlp->nlp_tx[channelno].q_last == (void *)iocbq) {
                                nlp->nlp_tx[channelno].q_first = NULL;
                                nlp->nlp_tx[channelno].q_last = NULL;
                                nlp->nlp_tx[channelno].q_cnt = 0;
                        } else {
                                /* Remove iocb from head */
                                nlp->nlp_tx[channelno].q_first =
                                    (void *)iocbq->next;
                                nlp->nlp_tx[channelno].q_cnt--;
                        }

                        iocbq->next = NULL;
                }

                /* Now deal with node itself */

                /* Check if node still needs servicing */
                if ((nlp->nlp_ptx[channelno].q_first) ||
                    (nlp->nlp_tx[channelno].q_first &&
                    !(nlp->nlp_flag[channelno] & NLP_CLOSED))) {

                        /*
                         * If this is the base node, then don't shift the
                         * pointers. We want to drain the base node before
                         * moving on
                         */
                        if (!nlp->nlp_base) {
                                /*
                                 * Just shift channel queue pointers to next
                                 * node
                                 */
                                cp->nodeq.q_last = (void *)nlp;
                                cp->nodeq.q_first = nlp->nlp_next[channelno];
                        }
                } else {
                        /* Remove node from channel queue */

                        /* If this is the last node on list */
                        if (cp->nodeq.q_last == (void *)nlp) {
                                cp->nodeq.q_last = NULL;
                                cp->nodeq.q_first = NULL;
                                cp->nodeq.q_cnt = 0;
                        } else {
                                /* Remove node from head */
                                cp->nodeq.q_first = nlp->nlp_next[channelno];
                                ((NODELIST *)cp->nodeq.q_last)->
                                    nlp_next[channelno] = cp->nodeq.q_first;
                                cp->nodeq.q_cnt--;

                        }

                        /* Clear node */
                        nlp->nlp_next[channelno] = NULL;
                }

                /*
                 * If no iocbq was found on this node, then it will have
                 * been removed. So try again.
                 */
                if (!iocbq) {
                        goto begin;
                }

                sbp = (emlxs_buf_t *)iocbq->sbp;

                if (sbp) {
                        /*
                         * Check flags before we enter mutex in case this
                         * has been flushed and destroyed
                         */
                        if ((sbp->pkt_flags &
                            (PACKET_IN_COMPLETION | PACKET_IN_CHIPQ)) ||
                            !(sbp->pkt_flags & PACKET_IN_TXQ)) {
                                goto begin;
                        }

                        mutex_enter(&sbp->mtx);

                        if ((sbp->pkt_flags &
                            (PACKET_IN_COMPLETION | PACKET_IN_CHIPQ)) ||
                            !(sbp->pkt_flags & PACKET_IN_TXQ)) {
                                mutex_exit(&sbp->mtx);
                                goto begin;
                        }

                        sbp->pkt_flags &= ~PACKET_IN_TXQ;
                        hba->channel_tx_count--;

                        mutex_exit(&sbp->mtx);
                }
        }

        if (iocbq) {
                HBASTATS.IocbTxGet[channelno]++;
        }

        /* Adjust the ring timeout timer */
        cp->timeout = (cp->nodeq.q_first) ? (hba->timer_tics + 5) : 0;

        if (lock) {
                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
        }

        return (iocbq);

} /* emlxs_tx_get() */


/*
 * Remove all cmd from from_rp's txq to to_rp's txq for ndlp.
 * The old IoTag has to be released, the new one has to be
 * allocated.  Others no change
 * TX_CHANNEL lock is held
 */
extern void
emlxs_tx_move(NODELIST *ndlp, CHANNEL *from_chan, CHANNEL *to_chan,
    uint32_t cmd, emlxs_buf_t *fpkt, uint32_t lock)
{
        emlxs_hba_t *hba;
        emlxs_port_t *port;
        uint32_t fchanno, tchanno, i;

        IOCBQ *iocbq;
        IOCBQ *prev;
        IOCBQ *next;
        IOCB *iocb, *icmd;
        Q tbm;          /* To Be Moved Q */
        MATCHMAP *mp;

        NODELIST *nlp = ndlp;
        emlxs_buf_t *sbp;

        NODELIST *n_prev = NULL;
        NODELIST *n_next = NULL;
        uint16_t count = 0;

        hba = from_chan->hba;
        port = &PPORT;
        cmd = cmd; /* To pass lint */

        fchanno = from_chan->channelno;
        tchanno = to_chan->channelno;

        if (lock) {
                mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
        }

        bzero((void *)&tbm, sizeof (Q));

        /* Scan the ndlp's fchanno txq to get the iocb of fcp cmd */
        prev = NULL;
        iocbq = (IOCBQ *)nlp->nlp_tx[fchanno].q_first;

        while (iocbq) {
                next = (IOCBQ *)iocbq->next;
                /* Check if this iocb is fcp cmd */
                iocb = &iocbq->iocb;

                switch (iocb->ULPCOMMAND) {
                /* FCP commands */
                case CMD_FCP_ICMND_CR:
                case CMD_FCP_ICMND_CX:
                case CMD_FCP_IREAD_CR:
                case CMD_FCP_IREAD_CX:
                case CMD_FCP_IWRITE_CR:
                case CMD_FCP_IWRITE_CX:
                case CMD_FCP_ICMND64_CR:
                case CMD_FCP_ICMND64_CX:
                case CMD_FCP_IREAD64_CR:
                case CMD_FCP_IREAD64_CX:
                case CMD_FCP_IWRITE64_CR:
                case CMD_FCP_IWRITE64_CX:
                        /* We found a fcp cmd */
                        break;
                default:
                        /* this is not fcp cmd continue */
                        prev = iocbq;
                        iocbq = next;
                        continue;
                }

                /* found a fcp cmd iocb in fchanno txq, now deque it */
                if (next == NULL) {
                        /* This is the last iocbq */
                        nlp->nlp_tx[fchanno].q_last =
                            (uint8_t *)prev;
                }

                if (prev == NULL) {
                        /* This is the first one then remove it from head */
                        nlp->nlp_tx[fchanno].q_first =
                            (uint8_t *)next;
                } else {
                        prev->next = next;
                }

                iocbq->next = NULL;
                nlp->nlp_tx[fchanno].q_cnt--;

                /* Add this iocb to our local toberemovedq */
                /* This way we donot hold the TX_CHANNEL lock too long */

                if (tbm.q_first) {
                        ((IOCBQ *)tbm.q_last)->next = iocbq;
                        tbm.q_last = (uint8_t *)iocbq;
                        tbm.q_cnt++;
                } else {
                        tbm.q_first = (uint8_t *)iocbq;
                        tbm.q_last = (uint8_t *)iocbq;
                        tbm.q_cnt = 1;
                }

                iocbq = next;

        }       /* While (iocbq) */

        if ((tchanno == hba->channel_fcp) && (tbm.q_cnt != 0)) {

                /* from_chan->nodeq.q_first must be non NULL */
                if (from_chan->nodeq.q_first) {

                        /* nodeq is not empty, now deal with the node itself */
                        if ((nlp->nlp_tx[fchanno].q_first)) {

                                if (!nlp->nlp_base) {
                                        from_chan->nodeq.q_last =
                                            (void *)nlp;
                                        from_chan->nodeq.q_first =
                                            nlp->nlp_next[fchanno];
                                }

                        } else {
                                n_prev = (NODELIST *)from_chan->nodeq.q_first;
                                count = from_chan->nodeq.q_cnt;

                                if (n_prev == nlp) {

                                        /* If this is the only node on list */
                                        if (from_chan->nodeq.q_last ==
                                            (void *)nlp) {
                                                from_chan->nodeq.q_last =
                                                    NULL;
                                                from_chan->nodeq.q_first =
                                                    NULL;
                                                from_chan->nodeq.q_cnt = 0;
                                        } else {
                                                from_chan->nodeq.q_first =
                                                    nlp->nlp_next[fchanno];
                                                ((NODELIST *)from_chan->
                                                    nodeq.q_last)->
                                                    nlp_next[fchanno] =
                                                    from_chan->nodeq.q_first;
                                                from_chan->nodeq.q_cnt--;
                                        }
                                        /* Clear node */
                                        nlp->nlp_next[fchanno] = NULL;
                                } else {
                                        count--;
                                        do {
                                                n_next =
                                                    n_prev->nlp_next[fchanno];
                                                if (n_next == nlp) {
                                                        break;
                                                }
                                                n_prev = n_next;
                                        } while (count--);

                                        if (count != 0) {

                                                if (n_next ==
                                                    (NODELIST *)from_chan->
                                                    nodeq.q_last) {
                                                        n_prev->
                                                            nlp_next[fchanno]
                                                            =
                                                            ((NODELIST *)
                                                            from_chan->
                                                            nodeq.q_last)->
                                                            nlp_next
                                                            [fchanno];
                                                        from_chan->nodeq.q_last
                                                            = (uint8_t *)n_prev;
                                                } else {

                                                        n_prev->
                                                            nlp_next[fchanno]
                                                            =
                                                            n_next-> nlp_next
                                                            [fchanno];
                                                }
                                                from_chan->nodeq.q_cnt--;
                                                /* Clear node */
                                                nlp->nlp_next[fchanno] =
                                                    NULL;
                                        }
                                }
                        }
                }
        }

        /* Now cleanup the iocb's */
        prev = NULL;
        iocbq = (IOCBQ *)tbm.q_first;

        while (iocbq) {

                next = (IOCBQ *)iocbq->next;

                /* Free the IoTag and the bmp */
                iocb = &iocbq->iocb;

                if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                        sbp = iocbq->sbp;
                        if (sbp) {
                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
                        }
                } else {
                        sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
                            iocb->ULPIOTAG, 0);
                }

                if (sbp && (sbp != STALE_PACKET)) {
                        mutex_enter(&sbp->mtx);
                        sbp->pkt_flags |= PACKET_IN_FLUSH;

                        /*
                         * If the fpkt is already set, then we will leave it
                         * alone. This ensures that this pkt is only accounted
                         * for on one fpkt->flush_count
                         */
                        if (!sbp->fpkt && fpkt) {
                                mutex_enter(&fpkt->mtx);
                                sbp->fpkt = fpkt;
                                fpkt->flush_count++;
                                mutex_exit(&fpkt->mtx);
                        }
                        mutex_exit(&sbp->mtx);
                }
                iocbq = next;

        }       /* end of while */

        iocbq = (IOCBQ *)tbm.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Get the pkt */
                sbp = (emlxs_buf_t *)iocbq->sbp;

                if (sbp) {
                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_flush_msg,
                        "tx: sbp=%p node=%p", sbp, sbp->node);

                        if (hba->state >= FC_LINK_UP) {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_ABORT_REQUESTED, 1);
                        } else {
                                emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
                                    IOERR_LINK_DOWN, 1);
                        }

                }
                /* Free the iocb and its associated buffers */
                else {
                        icmd = &iocbq->iocb;

                        /* SLI3 */
                        if (icmd->ULPCOMMAND == CMD_QUE_RING_BUF64_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_BUF_CN ||
                            icmd->ULPCOMMAND == CMD_QUE_RING_LIST64_CN) {
                                if ((hba->flag &
                                    (FC_ONLINE_MODE | FC_ONLINING_MODE)) == 0) {
                                        /* HBA is detaching or offlining */
                                        if (icmd->ULPCOMMAND !=
                                            CMD_QUE_RING_LIST64_CN) {
                                                void *tmp;
                                                RING *rp;
                                                int ch;

                                                ch = from_chan->channelno;
                                                rp = &hba->sli.sli3.ring[ch];

                                                for (i = 0;
                                                    i < icmd->ULPBDECOUNT;
                                                    i++) {
                                                        mp = EMLXS_GET_VADDR(
                                                            hba, rp, icmd);

                                                        tmp = (void *)mp;
                                                        if (mp) {
                                                        emlxs_mem_put(
                                                            hba,
                                                            MEM_BUF,
                                                            tmp);
                                                        }
                                                }

                                        }

                                        emlxs_mem_put(hba, MEM_IOCB,
                                            (void *)iocbq);
                                } else {
                                        /* repost the unsolicited buffer */
                                        EMLXS_SLI_ISSUE_IOCB_CMD(hba,
                                            from_chan, iocbq);
                                }
                        }
                }

                iocbq = next;

        }       /* end of while */

        /* Now flush the chipq if any */
        if (!(nlp->nlp_flag[fchanno] & NLP_CLOSED)) {

                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

                (void) emlxs_chipq_node_flush(port, from_chan, nlp, 0);

                mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
        }

        if (lock) {
                mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
        }

        return;

} /* emlxs_tx_move */


extern uint32_t
emlxs_chipq_node_flush(emlxs_port_t *port, CHANNEL *chan, NODELIST *ndlp,
    emlxs_buf_t *fpkt)
{
        emlxs_hba_t *hba = HBA;
        emlxs_buf_t *sbp;
        IOCBQ *iocbq;
        IOCBQ *next;
        Q abort;
        CHANNEL *cp;
        uint32_t channelno;
        uint8_t flag[MAX_CHANNEL];
        uint32_t iotag;

        bzero((void *)&abort, sizeof (Q));
        bzero((void *)flag, sizeof (flag));

        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                cp = &hba->chan[channelno];

                if (chan && cp != chan) {
                        continue;
                }

                mutex_enter(&EMLXS_FCTAB_LOCK);

                for (iotag = 1; iotag < hba->max_iotag; iotag++) {
                        sbp = hba->fc_table[iotag];

                        if (sbp && (sbp != STALE_PACKET) &&
                            (sbp->pkt_flags & PACKET_IN_CHIPQ) &&
                            (sbp->node == ndlp) &&
                            (sbp->channel == cp) &&
                            !(sbp->pkt_flags & PACKET_XRI_CLOSED)) {
                                emlxs_sbp_abort_add(port, sbp, &abort, flag,
                                    fpkt);
                        }

                }
                mutex_exit(&EMLXS_FCTAB_LOCK);

        }       /* for */

        /* Now put the iocb's on the tx queue */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Send this iocbq */
                emlxs_tx_put(iocbq, 1);

                iocbq = next;
        }

        /* Now trigger channel service */
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                if (!flag[channelno]) {
                        continue;
                }

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[channelno], 0);
        }

        return (abort.q_cnt);

} /* emlxs_chipq_node_flush() */


/* Flush all IO's left on all iotag lists */
extern uint32_t
emlxs_iotag_flush(emlxs_hba_t *hba)
{
        emlxs_port_t *port = &PPORT;
        emlxs_buf_t *sbp;
        IOCBQ *iocbq;
        IOCB *iocb;
        Q abort;
        CHANNEL *cp;
        uint32_t channelno;
        uint32_t iotag;
        uint32_t count;

        count = 0;
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                cp = &hba->chan[channelno];

                bzero((void *)&abort, sizeof (Q));

                mutex_enter(&EMLXS_FCTAB_LOCK);

                for (iotag = 1; iotag < hba->max_iotag; iotag++) {
                        sbp = hba->fc_table[iotag];

                        /* Check if the slot is empty */
                        if (!sbp || (sbp == STALE_PACKET)) {
                                continue;
                        }

                        /* We are building an abort list per channel */
                        if (sbp->channel != cp) {
                                continue;
                        }

                        hba->fc_table[iotag] = STALE_PACKET;
                        hba->io_count--;

                        /* Check if IO is valid */
                        if (!(sbp->pkt_flags & PACKET_VALID) ||
                            (sbp->pkt_flags & (PACKET_ULP_OWNED|
                            PACKET_COMPLETED|PACKET_IN_COMPLETION))) {
                                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_debug_msg,
                                    "iotag_flush: Invalid IO found. iotag=%d",
                                    iotag);

                                continue;
                        }

                        sbp->iotag = 0;

                        /* Set IOCB status */
                        iocbq = &sbp->iocbq;
                        iocb = &iocbq->iocb;

                        iocb->ULPSTATUS = IOSTAT_LOCAL_REJECT;
                        iocb->un.grsp.perr.statLocalError = IOERR_LINK_DOWN;
                        iocb->ULPLE = 1;
                        iocbq->next = NULL;

                        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                                if (sbp->xrip) {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_sli_debug_msg,
                                            "iotag_flush: iotag=%d sbp=%p "
                                            "xrip=%p state=%x flag=%x",
                                            iotag, sbp, sbp->xrip,
                                            sbp->xrip->state, sbp->xrip->flag);
                                } else {
                                        EMLXS_MSGF(EMLXS_CONTEXT,
                                            &emlxs_sli_debug_msg,
                                            "iotag_flush: iotag=%d sbp=%p "
                                            "xrip=NULL", iotag, sbp);
                                }

                                emlxs_sli4_free_xri(port, sbp, sbp->xrip, 0);
                        } else {
                                /* Clean up the sbp */
                                mutex_enter(&sbp->mtx);

                                if (sbp->pkt_flags & PACKET_IN_TXQ) {
                                        sbp->pkt_flags &= ~PACKET_IN_TXQ;
                                        hba->channel_tx_count --;
                                }

                                if (sbp->pkt_flags & PACKET_IN_CHIPQ) {
                                        sbp->pkt_flags &= ~PACKET_IN_CHIPQ;
                                }

                                if (sbp->bmp) {
                                        emlxs_mem_put(hba, MEM_BPL,
                                            (void *)sbp->bmp);
                                        sbp->bmp = 0;
                                }

                                mutex_exit(&sbp->mtx);
                        }

                        /* At this point all nodes are assumed destroyed */
                        mutex_enter(&sbp->mtx);
                        sbp->node = 0;
                        mutex_exit(&sbp->mtx);

                        /* Add this iocb to our local abort Q */
                        if (abort.q_first) {
                                ((IOCBQ *)abort.q_last)->next = iocbq;
                                abort.q_last = (uint8_t *)iocbq;
                                abort.q_cnt++;
                        } else {
                                abort.q_first = (uint8_t *)iocbq;
                                abort.q_last = (uint8_t *)iocbq;
                                abort.q_cnt = 1;
                        }
                }

                mutex_exit(&EMLXS_FCTAB_LOCK);

                /* Trigger deferred completion */
                if (abort.q_first) {
                        mutex_enter(&cp->rsp_lock);
                        if (cp->rsp_head == NULL) {
                                cp->rsp_head = (IOCBQ *)abort.q_first;
                                cp->rsp_tail = (IOCBQ *)abort.q_last;
                        } else {
                                cp->rsp_tail->next = (IOCBQ *)abort.q_first;
                                cp->rsp_tail = (IOCBQ *)abort.q_last;
                        }
                        mutex_exit(&cp->rsp_lock);

                        emlxs_thread_trigger2(&cp->intr_thread,
                            emlxs_proc_channel, cp);

                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_debug_msg,
                            "iotag_flush: channel=%d count=%d",
                            channelno, abort.q_cnt);

                        count += abort.q_cnt;
                }
        }

        return (count);

} /* emlxs_iotag_flush() */



/* Checks for IO's on all or a given channel for a given node */
extern uint32_t
emlxs_chipq_node_check(emlxs_port_t *port, CHANNEL *chan, NODELIST *ndlp)
{
        emlxs_hba_t *hba = HBA;
        emlxs_buf_t *sbp;
        CHANNEL *cp;
        uint32_t channelno;
        uint32_t count;
        uint32_t iotag;

        count = 0;

        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                cp = &hba->chan[channelno];

                if (chan && cp != chan) {
                        continue;
                }

                mutex_enter(&EMLXS_FCTAB_LOCK);

                for (iotag = 1; iotag < hba->max_iotag; iotag++) {
                        sbp = hba->fc_table[iotag];

                        if (sbp && (sbp != STALE_PACKET) &&
                            (sbp->pkt_flags & PACKET_IN_CHIPQ) &&
                            (sbp->node == ndlp) &&
                            (sbp->channel == cp) &&
                            !(sbp->pkt_flags & PACKET_XRI_CLOSED)) {
                                count++;
                        }

                }
                mutex_exit(&EMLXS_FCTAB_LOCK);

        }       /* for */

        return (count);

} /* emlxs_chipq_node_check() */



/* Flush all IO's for a given node's lun (on any channel) */
extern uint32_t
emlxs_chipq_lun_flush(emlxs_port_t *port, NODELIST *ndlp,
    uint32_t lun, emlxs_buf_t *fpkt)
{
        emlxs_hba_t *hba = HBA;
        emlxs_buf_t *sbp;
        IOCBQ *iocbq;
        IOCBQ *next;
        Q abort;
        uint32_t iotag;
        uint8_t flag[MAX_CHANNEL];
        uint32_t channelno;

        if (lun == EMLXS_LUN_NONE) {
                return (0);
        }

        bzero((void *)flag, sizeof (flag));
        bzero((void *)&abort, sizeof (Q));

        mutex_enter(&EMLXS_FCTAB_LOCK);
        for (iotag = 1; iotag < hba->max_iotag; iotag++) {
                sbp = hba->fc_table[iotag];

                if (sbp && (sbp != STALE_PACKET) &&
                    sbp->pkt_flags & PACKET_IN_CHIPQ &&
                    sbp->node == ndlp &&
                    sbp->lun == lun &&
                    !(sbp->pkt_flags & PACKET_XRI_CLOSED)) {
                        emlxs_sbp_abort_add(port, sbp,
                            &abort, flag, fpkt);
                }
        }
        mutex_exit(&EMLXS_FCTAB_LOCK);

        /* Now put the iocb's on the tx queue */
        iocbq = (IOCBQ *)abort.q_first;
        while (iocbq) {
                /* Save the next iocbq for now */
                next = (IOCBQ *)iocbq->next;

                /* Unlink this iocbq */
                iocbq->next = NULL;

                /* Send this iocbq */
                emlxs_tx_put(iocbq, 1);

                iocbq = next;
        }

        /* Now trigger channel service */
        for (channelno = 0; channelno < hba->chan_count; channelno++) {
                if (!flag[channelno]) {
                        continue;
                }

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[channelno], 0);
        }

        return (abort.q_cnt);

} /* emlxs_chipq_lun_flush() */



/*
 * Issue an ABORT_XRI_CN iocb command to abort an FCP command already issued.
 * This must be called while holding the EMLXS_FCTAB_LOCK
 */
extern IOCBQ *
emlxs_create_abort_xri_cn(emlxs_port_t *port, NODELIST *ndlp,
    uint16_t iotag, CHANNEL *cp, uint8_t class, int32_t flag)
{
        emlxs_hba_t *hba = HBA;
        IOCBQ *iocbq;
        IOCB *iocb;
        emlxs_wqe_t *wqe;
        emlxs_buf_t *sbp;
        uint16_t abort_iotag;

        if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
                return (NULL);
        }

        iocbq->channel = (void *)cp;
        iocbq->port = (void *)port;
        iocbq->node = (void *)ndlp;
        iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);

        /*
         * set up an iotag using special Abort iotags
         */
        if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
                hba->fc_oor_iotag = hba->max_iotag;
        }
        abort_iotag = hba->fc_oor_iotag++;


        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                wqe = &iocbq->wqe;
                sbp = hba->fc_table[iotag];

                /* Try to issue abort by XRI if possible */
                if (sbp == NULL || sbp == STALE_PACKET || sbp->xrip == NULL) {
                        wqe->un.Abort.Criteria = ABORT_REQ_TAG;
                        wqe->AbortTag = iotag;
                } else {
                        wqe->un.Abort.Criteria = ABORT_XRI_TAG;
                        wqe->AbortTag = sbp->xrip->XRI;
                }
                wqe->un.Abort.IA = 0;
                wqe->RequestTag = abort_iotag;
                wqe->Command = CMD_ABORT_XRI_CX;
                wqe->Class = CLASS3;
                wqe->CQId = (uint16_t)0xffff;  /* default CQ for response */
                wqe->CmdType = WQE_TYPE_ABORT;
        } else {
                iocb = &iocbq->iocb;
                iocb->ULPIOTAG = abort_iotag;
                iocb->un.acxri.abortType = flag;
                iocb->un.acxri.abortContextTag = ndlp->nlp_Rpi;
                iocb->un.acxri.abortIoTag = iotag;
                iocb->ULPLE = 1;
                iocb->ULPCLASS = class;
                iocb->ULPCOMMAND = CMD_ABORT_XRI_CN;
                iocb->ULPOWNER = OWN_CHIP;
        }

        return (iocbq);

} /* emlxs_create_abort_xri_cn() */


/* This must be called while holding the EMLXS_FCTAB_LOCK */
extern IOCBQ *
emlxs_create_abort_xri_cx(emlxs_port_t *port, NODELIST *ndlp, uint16_t xid,
    CHANNEL *cp, uint8_t class, int32_t flag)
{
        emlxs_hba_t *hba = HBA;
        IOCBQ *iocbq;
        IOCB *iocb;
        emlxs_wqe_t *wqe;
        uint16_t abort_iotag;

        if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
                return (NULL);
        }

        iocbq->channel = (void *)cp;
        iocbq->port = (void *)port;
        iocbq->node = (void *)ndlp;
        iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);

        /*
         * set up an iotag using special Abort iotags
         */
        if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
                hba->fc_oor_iotag = hba->max_iotag;
        }
        abort_iotag = hba->fc_oor_iotag++;

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                wqe = &iocbq->wqe;
                wqe->un.Abort.Criteria = ABORT_XRI_TAG;
                wqe->un.Abort.IA = 0;
                wqe->RequestTag = abort_iotag;
                wqe->AbortTag = xid;
                wqe->Command = CMD_ABORT_XRI_CX;
                wqe->Class = CLASS3;
                wqe->CQId = (uint16_t)0xffff;  /* default CQ for response */
                wqe->CmdType = WQE_TYPE_ABORT;
        } else {
                iocb = &iocbq->iocb;
                iocb->ULPCONTEXT = xid;
                iocb->ULPIOTAG = abort_iotag;
                iocb->un.acxri.abortType = flag;
                iocb->ULPLE = 1;
                iocb->ULPCLASS = class;
                iocb->ULPCOMMAND = CMD_ABORT_XRI_CX;
                iocb->ULPOWNER = OWN_CHIP;
        }

        return (iocbq);

} /* emlxs_create_abort_xri_cx() */



/* This must be called while holding the EMLXS_FCTAB_LOCK */
extern IOCBQ *
emlxs_create_close_xri_cn(emlxs_port_t *port, NODELIST *ndlp,
    uint16_t iotag, CHANNEL *cp)
{
        emlxs_hba_t *hba = HBA;
        IOCBQ *iocbq;
        IOCB *iocb;
        emlxs_wqe_t *wqe;
        emlxs_buf_t *sbp;
        uint16_t abort_iotag;

        if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
                return (NULL);
        }

        iocbq->channel = (void *)cp;
        iocbq->port = (void *)port;
        iocbq->node = (void *)ndlp;
        iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);

        /*
         * set up an iotag using special Abort iotags
         */
        if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
                hba->fc_oor_iotag = hba->max_iotag;
        }
        abort_iotag = hba->fc_oor_iotag++;

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                wqe = &iocbq->wqe;
                sbp = hba->fc_table[iotag];

                /* Try to issue close by XRI if possible */
                if (sbp == NULL || sbp == STALE_PACKET || sbp->xrip == NULL) {
                        wqe->un.Abort.Criteria = ABORT_REQ_TAG;
                        wqe->AbortTag = iotag;
                } else {
                        wqe->un.Abort.Criteria = ABORT_XRI_TAG;
                        wqe->AbortTag = sbp->xrip->XRI;
                }
                wqe->un.Abort.IA = 1;
                wqe->RequestTag = abort_iotag;
                wqe->Command = CMD_ABORT_XRI_CX;
                wqe->Class = CLASS3;
                wqe->CQId = (uint16_t)0xffff;  /* default CQ for response */
                wqe->CmdType = WQE_TYPE_ABORT;
        } else {
                iocb = &iocbq->iocb;
                iocb->ULPIOTAG = abort_iotag;
                iocb->un.acxri.abortType = 0;
                iocb->un.acxri.abortContextTag = ndlp->nlp_Rpi;
                iocb->un.acxri.abortIoTag = iotag;
                iocb->ULPLE = 1;
                iocb->ULPCLASS = 0;
                iocb->ULPCOMMAND = CMD_CLOSE_XRI_CN;
                iocb->ULPOWNER = OWN_CHIP;
        }

        return (iocbq);

} /* emlxs_create_close_xri_cn() */


/* This must be called while holding the EMLXS_FCTAB_LOCK */
extern IOCBQ *
emlxs_create_close_xri_cx(emlxs_port_t *port, NODELIST *ndlp, uint16_t xid,
    CHANNEL *cp)
{
        emlxs_hba_t *hba = HBA;
        IOCBQ *iocbq;
        IOCB *iocb;
        emlxs_wqe_t *wqe;
        uint16_t abort_iotag;

        if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
                return (NULL);
        }

        iocbq->channel = (void *)cp;
        iocbq->port = (void *)port;
        iocbq->node = (void *)ndlp;
        iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);

        /*
         * set up an iotag using special Abort iotags
         */
        if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
                hba->fc_oor_iotag = hba->max_iotag;
        }
        abort_iotag = hba->fc_oor_iotag++;

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                wqe = &iocbq->wqe;
                wqe->un.Abort.Criteria = ABORT_XRI_TAG;
                wqe->un.Abort.IA = 1;
                wqe->RequestTag = abort_iotag;
                wqe->AbortTag = xid;
                wqe->Command = CMD_ABORT_XRI_CX;
                wqe->Class = CLASS3;
                wqe->CQId = (uint16_t)0xffff;  /* default CQ for response */
                wqe->CmdType = WQE_TYPE_ABORT;
        } else {
                iocb = &iocbq->iocb;
                iocb->ULPCONTEXT = xid;
                iocb->ULPIOTAG = abort_iotag;
                iocb->ULPLE = 1;
                iocb->ULPCLASS = 0;
                iocb->ULPCOMMAND = CMD_CLOSE_XRI_CX;
                iocb->ULPOWNER = OWN_CHIP;
        }

        return (iocbq);

} /* emlxs_create_close_xri_cx() */


void
emlxs_close_els_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
        CHANNEL *cp;
        IOCBQ *iocbq;
        IOCB *iocb;

        if (rxid == 0 || rxid == 0xFFFF) {
                return;
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
                    "Closing ELS exchange: xid=%x", rxid);

                if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
                        return;
                }
        }

        cp = &hba->chan[hba->channel_els];

        mutex_enter(&EMLXS_FCTAB_LOCK);

        /* Create the abort IOCB */
        iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);

        mutex_exit(&EMLXS_FCTAB_LOCK);

        if (iocbq) {
                iocb = &iocbq->iocb;
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
                    "Closing ELS exchange: xid=%x iotag=%d", rxid,
                    iocb->ULPIOTAG);

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
        }

} /* emlxs_close_els_exchange() */


void
emlxs_abort_els_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
        CHANNEL *cp;
        IOCBQ *iocbq;
        IOCB *iocb;

        if (rxid == 0 || rxid == 0xFFFF) {
                return;
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {

                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
                    "Aborting ELS exchange: xid=%x", rxid);

                if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
                        /* We have no way to abort unsolicited exchanges */
                        /* that we have not responded to at this time */
                        /* So we will return for now */
                        return;
                }
        }

        cp = &hba->chan[hba->channel_els];

        mutex_enter(&EMLXS_FCTAB_LOCK);

        /* Create the abort IOCB */
        if (hba->state >= FC_LINK_UP) {
                iocbq = emlxs_create_abort_xri_cx(port, NULL, rxid, cp,
                    CLASS3, ABORT_TYPE_ABTS);
        } else {
                iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);
        }

        mutex_exit(&EMLXS_FCTAB_LOCK);

        if (iocbq) {
                iocb = &iocbq->iocb;
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
                    "Aborting ELS exchange: xid=%x iotag=%d", rxid,
                    iocb->ULPIOTAG);

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
        }

} /* emlxs_abort_els_exchange() */


void
emlxs_abort_ct_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
        CHANNEL *cp;
        IOCBQ *iocbq;
        IOCB *iocb;

        if (rxid == 0 || rxid == 0xFFFF) {
                return;
        }

        if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_ct_msg,
                    "Aborting CT exchange: xid=%x", rxid);

                if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
                        /* We have no way to abort unsolicited exchanges */
                        /* that we have not responded to at this time */
                        /* So we will return for now */
                        return;
                }
        }

        cp = &hba->chan[hba->channel_ct];

        mutex_enter(&EMLXS_FCTAB_LOCK);

        /* Create the abort IOCB */
        if (hba->state >= FC_LINK_UP) {
                iocbq = emlxs_create_abort_xri_cx(port, NULL, rxid, cp,
                    CLASS3, ABORT_TYPE_ABTS);
        } else {
                iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);
        }

        mutex_exit(&EMLXS_FCTAB_LOCK);

        if (iocbq) {
                iocb = &iocbq->iocb;
                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
                    "Aborting CT exchange: xid=%x iotag=%d", rxid,
                    iocb->ULPIOTAG);

                EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
        }

} /* emlxs_abort_ct_exchange() */


/* This must be called while holding the EMLXS_FCTAB_LOCK */
static void
emlxs_sbp_abort_add(emlxs_port_t *port, emlxs_buf_t *sbp, Q *abort,
    uint8_t *flag, emlxs_buf_t *fpkt)
{
        emlxs_hba_t *hba = HBA;
        IOCBQ *iocbq;
        CHANNEL *cp;
        NODELIST *ndlp;

        cp = (CHANNEL *)sbp->channel;
        ndlp = sbp->node;

        /* Create the close XRI IOCB */
        if (hba->state >= FC_LINK_UP) {
                iocbq = emlxs_create_abort_xri_cn(port, ndlp, sbp->iotag, cp,
                    CLASS3, ABORT_TYPE_ABTS);
        } else {
                iocbq = emlxs_create_close_xri_cn(port, ndlp, sbp->iotag, cp);
        }
        /*
         * Add this iocb to our local abort Q
         * This way we don't hold the CHIPQ lock too long
         */
        if (iocbq) {
                if (abort->q_first) {
                        ((IOCBQ *)abort->q_last)->next = iocbq;
                        abort->q_last = (uint8_t *)iocbq;
                        abort->q_cnt++;
                } else {
                        abort->q_first = (uint8_t *)iocbq;
                        abort->q_last = (uint8_t *)iocbq;
                        abort->q_cnt = 1;
                }
                iocbq->next = NULL;
        }

        /* set the flags */
        mutex_enter(&sbp->mtx);

        sbp->pkt_flags |= (PACKET_IN_FLUSH | PACKET_XRI_CLOSED);

        sbp->ticks = hba->timer_tics + 10;
        sbp->abort_attempts++;

        flag[cp->channelno] = 1;

        /*
         * If the fpkt is already set, then we will leave it alone
         * This ensures that this pkt is only accounted for on one
         * fpkt->flush_count
         */
        if (!sbp->fpkt && fpkt) {
                mutex_enter(&fpkt->mtx);
                sbp->fpkt = fpkt;
                fpkt->flush_count++;
                mutex_exit(&fpkt->mtx);
        }

        mutex_exit(&sbp->mtx);

        return;

}       /* emlxs_sbp_abort_add() */