Skip to main content

Source code file content

Revision: 13149

6973228 Cannot download firmware 2.103.x.x on Emulex FCoE HBAs 6960289 fiber side of emulex cna does not connect to the storage
» Project Revision History

» Checkout URL

on-src / usr / src / uts / common / io / fibre-channel / fca / emlxs / emlxs_fcp.c

Size: 94932 bytes, 1 line
/*
 * 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 usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * 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 2010 Emulex.  All rights reserved.
 * Use is subject to license terms.
 */


#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;

	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=%x", 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 provided */
	if ((iostat == IOSTAT_FCP_RSP_ERROR) && pkt->pkt_rsplen) {
		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;

		/* 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;
		}

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

		/* Set the pkt resp_resid field */
		pkt->pkt_resp_resid = 0;

		/* Set the pkt data_resid field */
		if (pkt->pkt_datalen &&
		    (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 -
					    cmd->un.fcpi.fcpi_parm),
					    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 {	/* pkt->pkt_datalen==0 or FC_PKT_FCP_WRITE */

			/* Report whatever the target reported */
			pkt->pkt_data_resid = rsp_data_resid;
		}
	}

	/* 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 did=0x%06x sbp=%p cmd=%02x",
			    emlxs_error_xlate(localstat), did, sbp,
			    scsi_opcode);
		}

		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;

	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;
	}

done:

	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)) == 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, 1))
			    == 0) {
				icmd->ULPBDECOUNT = i;
				for (j = 0; j < i; j++) {
					mp = EMLXS_GET_VADDR(hba, rp, icmd);
					if (mp) {
						(void) emlxs_mem_put(hba, seg,
						    (uint8_t *)mp);
					}
				}

				rp->fc_missbufcnt = cnt + i;

				(void) emlxs_mem_put(hba, MEM_IOCB,
				    (uint8_t *)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 = (uint8_t *)mp;

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

	rp->fc_missbufcnt = 0;

	return (0);

} /* emlxs_post_buffer() */


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;
	uint8_t format;

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

	cfg = &CFG;
	aid = (fc_affected_id_t *)&scope;
	linkdown = 0;
	vlinkdown = 0;
	unreg_vpi = 0;
	update = 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;

	}

	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) {
		mutex_enter(&EMLXS_PORT_LOCK);

		port->flag &= EMLXS_PORT_LINKDOWN_MASK;
		port->prev_did = port->did;
		port->did = 0;

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

		mutex_exit(&EMLXS_PORT_LOCK);

		/* 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->ini_mode) {
					port->ulp_statec_cb(port->ulp_handle,
					    FC_STATE_OFFLINE);
				}
#ifdef SFCT_SUPPORT
				else if (port->tgt_mode) {
					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);

		/* 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.");
				}

#ifdef SFCT_SUPPORT
				if (port->tgt_mode) {
					emlxs_fct_link_down(port);

				} else if (port->ini_mode) {
					port->ulp_statec_cb(port->ulp_handle,
					    FC_STATE_OFFLINE);
				}
#else
				port->ulp_statec_cb(port->ulp_handle,
				    FC_STATE_OFFLINE);
#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 */

	if (port->tgt_mode) {
		goto done;
	}

	/* Set the node tags */
	/* We will process all nodes with this tag */
	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);

	if (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_mb_unreg_did 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_mb_unreg_did(port, nlp->nlp_DID,
				    NULL, NULL, NULL);
			} else if (action == 2) {
#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_mb_unreg_did 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_mb_unreg_did(port, nlp->nlp_DID,
				    NULL, NULL, NULL);
			} else if (action == 2) {
#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 */
				unreg_vpi = 0;

#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_mb_unreg_did 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_mb_unreg_did(port, nlp->nlp_DID,
				    NULL, NULL, NULL);
			} else if (action == 2) {
				/*
				 * 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 */
				unreg_vpi = 0;

				/*
				 * 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 (hba->sli_mode != EMLXS_HBA_SLI4_MODE) {
		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;
	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_ENABLE)) {
		return;
	}

	mutex_enter(&EMLXS_PORT_LOCK);

	/* Check for mode */
	if (port->tgt_mode) {
		(void) strcpy(mode, ", target");
	} else if (port->ini_mode) {
		(void) strcpy(mode, ", initiator");
	} else {
		(void) strcpy(mode, "");
	}

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

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

	case LA_1GHZ_LINK:
		(void) strcpy(linkspeed, "1Gb");
		state |= FC_STATE_1GBIT_SPEED;
		break;
	case LA_2GHZ_LINK:
		(void) strcpy(linkspeed, "2Gb");
		state |= FC_STATE_2GBIT_SPEED;
		break;
	case LA_4GHZ_LINK:
		(void) strcpy(linkspeed, "4Gb");
		state |= FC_STATE_4GBIT_SPEED;
		break;
	case LA_8GHZ_LINK:
		(void) strcpy(linkspeed, "8Gb");
		state |= FC_STATE_8GBIT_SPEED;
		break;
	case LA_10GHZ_LINK:
		(void) strcpy(linkspeed, "10Gb");
		state |= FC_STATE_10GBIT_SPEED;
		break;
	default:
		(void) sprintf(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);


	/*
	 * EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_up_msg,
	 *    "linkup_callback: update=%d vpi=%d flag=%d fc_flag=%x state=%x"
	 *    "statec=%x", update, vport->vpi, npiv_linkup, hba->flag,
	 *    hba->state, vport->ulp_statec);
	 */

	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->ini_mode) {
				vport->ulp_statec_cb(vport->ulp_handle,
				    state);
			}
#ifdef SFCT_SUPPORT
			else if (vport->tgt_mode) {
				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);
	}

	return;

} /* emlxs_port_online() */


extern void
emlxs_linkdown(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	RPIobj_t *rp;
	int i;

	mutex_enter(&EMLXS_PORT_LOCK);

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

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

	if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
		rp = hba->sli.sli4.RPIp;
		for (i = 0; i < hba->sli.sli4.RPICount; i++) {
			if (rp->state & RESOURCE_ALLOCATED) {
				rp->state |= RESOURCE_RPI_PAUSED;
			}
			rp++;
		}
	}

	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, 0xffffffff);

	}

	return;

} /* emlxs_linkdown() */


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

	mutex_enter(&EMLXS_PORT_LOCK);

	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);

		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);

	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 rc;

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

	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;
	}

	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
	    "Disabling link...");

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

		if (wait == MBX_NOWAIT) {
			if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX, 1))
			    == 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) {
		(void) emlxs_mem_put(hba, MEM_MBOX, (uint8_t *)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);

		DELAYMS(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);
		emlxs_diag_state = DDI_OFFDI;
		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);
	emlxs_diag_state = DDI_ONDI;
	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
	(void) emlxs_fct_port_initialize(port);
#endif /* SFCT_SUPPORT */

	return (rval);

} /* emlxs_online() */


extern int
emlxs_offline(emlxs_hba_t *hba)
{
	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);

		DELAYMS(1000);
	}

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

	if (port->ini_mode) {
		/* Flush all IO */
		emlxs_linkdown(hba);
	}
#ifdef SFCT_SUPPORT
	else {
		(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);

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

	rval = 0;

	EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_offline_msg, NULL);

done:

	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;
	uint32_t *ptr;
	uint32_t i;

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

	/* Save pci config space */
	ptr = (uint32_t *)hba->pm_config;
	for (i = 0; i < PCI_CONFIG_SIZE; i += 4, ptr++) {
		*ptr =
		    ddi_get32(hba->pci_acc_handle,
		    (uint32_t *)(hba->pci_addr + i));
	}

	/* Put chip in D3 state */
	(void) ddi_put8(hba->pci_acc_handle,
	    (uint8_t *)(hba->pci_addr + PCI_PM_CONTROL_REGISTER),
	    (uint8_t)PCI_PM_D3_STATE);

#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;
	uint32_t *ptr;
	uint32_t i;


	/* Take chip out of D3 state */
	(void) ddi_put8(hba->pci_acc_handle,
	    (uint8_t *)(hba->pci_addr + PCI_PM_CONTROL_REGISTER),
	    (uint8_t)PCI_PM_D0_STATE);

	/* Must have at least 10 ms delay here */
	DELAYMS(100);

	/* Restore pci config space */
	ptr = (uint32_t *)hba->pm_config;
	for (i = 0; i < PCI_CONFIG_SIZE; i += 4, ptr++) {
		(void) ddi_put32(hba->pci_acc_handle,
		    (uint32_t *)(hba->pci_addr + i), *ptr);
	}

#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))) {
		(void) ddi_put8(hba->pci_acc_handle,
		    (uint8_t *)(hba->pci_addr + PCI_PM_CONTROL_REGISTER),
		    (uint8_t)PCI_PM_D3_STATE);

		return (rval);
	}

	return (rval);

} /* End 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) {
			if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
				(void) emlxs_sli4_unreg_all_rpi_by_port(port);
			} else {
				(void) emlxs_mb_unreg_rpi(port, 0xffff, 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,
	 *    "emlxs_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) {
		(void) emlxs_mem_put(hba, MEM_BPL, (uint8_t *)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) {
				hba->fc_table[sbp->iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			}
		} 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) {
						uint8_t	*tmp;
						RING *rp;

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

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

					(void) emlxs_mem_put(hba, MEM_IOCB,
					    (uint8_t *)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) {
				hba->fc_table[sbp->iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			}
		} 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) {
						uint8_t	*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 = (uint8_t *)mp;
							if (mp) {
							(void) emlxs_mem_put(
							    hba, MEM_BUF, tmp);
							}
						}
					}

					(void) emlxs_mem_put(hba, MEM_IOCB,
					    (uint8_t *)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;
	CHANNEL *cp;
	CHANNEL *channel;
	uint8_t flag[MAX_CHANNEL];

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

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

	channel = &hba->chan[hba->channel_fcp];

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

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

		/* 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) {
				hba->fc_table[sbp->iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			}
		} 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) {
						uint8_t	*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 = (uint8_t *)mp;
							if (mp) {
							(void) emlxs_mem_put(
							    hba, MEM_BUF, tmp);
							}
						}
					}

					(void) emlxs_mem_put(hba, MEM_IOCB,
					    (uint8_t *)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;

	/* under what cases, nlp is NULL */
	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) {
				hba->fc_table[sbp->iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			} 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);
			}

			(void) emlxs_mem_put(hba, MEM_IOCB, (uint8_t *)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) {
				hba->fc_table[sbp->iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			}
		} 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) {
						uint8_t *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 = (uint8_t *)mp;
							if (mp) {
							(void) emlxs_mem_put(
							    hba,
							    MEM_BUF,
							    tmp);
							}
						}

					}

					(void) emlxs_mem_put(hba, MEM_IOCB,
					    (uint8_t *)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;
			}

			/* 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) {
				hba->fc_table[iotag] = NULL;
				emlxs_sli4_free_xri(hba, sbp, sbp->xp);
			} else {
				hba->fc_table[iotag] = STALE_PACKET;
				hba->io_count --;
				sbp->iotag = 0;

				/* 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) {
					(void) emlxs_mem_put(hba, MEM_BPL,
					    (uint8_t *)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_err_msg,
			    "Forced iotag completion. 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;

	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, 0)) == 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->xp == NULL) {
			wqe->un.Abort.Criteria = ABORT_REQ_TAG;
			wqe->AbortTag = iotag;
		} else {
			wqe->un.Abort.Criteria = ABORT_XRI_TAG;
			wqe->AbortTag = sbp->xp->XRI;
		}
		wqe->un.Abort.IA = 0;
		wqe->RequestTag = abort_iotag;
		wqe->Command = CMD_ABORT_XRI_CX;
		wqe->Class = CLASS3;
		wqe->CQId = 0x3ff;
		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, 0)) == 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 = 0x3ff;
		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, 0)) == 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->xp == NULL) {
			wqe->un.Abort.Criteria = ABORT_REQ_TAG;
			wqe->AbortTag = iotag;
		} else {
			wqe->un.Abort.Criteria = ABORT_XRI_TAG;
			wqe->AbortTag = sbp->xp->XRI;
		}
		wqe->un.Abort.IA = 1;
		wqe->RequestTag = abort_iotag;
		wqe->Command = CMD_ABORT_XRI_CX;
		wqe->Class = CLASS3;
		wqe->CQId = 0x3ff;
		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, 0)) == 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 = 0x3ff;
		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() */


#ifdef SFCT_SUPPORT
void
emlxs_abort_fct_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_fct_detail_msg,
		    "Aborting FCT exchange: xid=%x", rxid);

		if (emlxs_sli4_unreserve_xri(hba, rxid) == 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_fcp];

	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_fct_detail_msg,
		    "Aborting FCT exchange: xid=%x iotag=%x", rxid,
		    iocb->ULPIOTAG);

		EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
	}

} /* emlxs_abort_fct_exchange() */
#endif /* SFCT_SUPPORT */


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(hba, rxid) == 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=%x", 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(hba, rxid) == 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=%x", 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 */
	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() */
 
 
Close
loading
Please Confirm
Close