#include <emlxs.h>
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
extern void
emlxs_handle_fcp_event(emlxs_hba_t *hba, CHANNEL *cp, IOCBQ *iocbq)
{
emlxs_port_t *port = &PPORT;
emlxs_config_t *cfg = &CFG;
IOCB *cmd;
emlxs_buf_t *sbp;
fc_packet_t *pkt = NULL;
#ifdef SAN_DIAG_SUPPORT
NODELIST *ndlp;
#endif
uint32_t iostat;
uint8_t localstat;
fcp_rsp_t *rsp;
uint32_t rsp_data_resid;
uint32_t check_underrun;
uint8_t asc;
uint8_t ascq;
uint8_t scsi_status;
uint8_t sense;
uint32_t did;
uint32_t fix_it;
uint8_t *scsi_cmd;
uint8_t scsi_opcode;
uint16_t scsi_dl;
uint32_t data_rx;
uint32_t length;
cmd = &iocbq->iocb;
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) {
HBASTATS.FcpStray++;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_stray_fcp_completion_msg,
"cmd=%x iotag=%d", cmd->ULPCOMMAND, cmd->ULPIOTAG);
return;
}
HBASTATS.FcpCompleted++;
#ifdef SAN_DIAG_SUPPORT
emlxs_update_sd_bucket(sbp);
#endif
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;
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;
cmd->un.fcpi.fcpi_parm = pkt->pkt_datalen - 512;
bzero((uint8_t *)pkt->pkt_data, 512);
bzero((uint8_t *)pkt->pkt_resp, pkt->pkt_rsplen);
}
#endif
}
mutex_enter(&sbp->mtx);
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
#ifdef FMA_SUPPORT
emlxs_check_dma(hba, sbp);
#endif
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
return;
}
if ((iostat != IOSTAT_FCP_RSP_ERROR) || (pkt->pkt_rsplen == 0)) {
goto done;
}
EMLXS_MPDATA_SYNC(pkt->pkt_resp_dma, 0, pkt->pkt_rsplen,
DDI_DMA_SYNC_FORKERNEL);
rsp = (fcp_rsp_t *)pkt->pkt_resp;
if (!rsp->fcp_u.fcp_status.resid_under &&
!rsp->fcp_u.fcp_status.resid_over) {
rsp->fcp_resid = 0;
}
if (!rsp->fcp_u.fcp_status.rsp_len_set) {
rsp->fcp_response_len = 0;
}
if (!rsp->fcp_u.fcp_status.sense_len_set) {
rsp->fcp_sense_len = 0;
}
length = sizeof (fcp_rsp_t) + LE_SWAP32(rsp->fcp_response_len) +
LE_SWAP32(rsp->fcp_sense_len);
if (length > pkt->pkt_rsplen) {
iostat = IOSTAT_RSP_INVALID;
pkt->pkt_data_resid = pkt->pkt_datalen;
goto done;
}
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
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;
}
if (scsi_status == SCSI_STAT_GOOD) {
check_underrun = 1;
}
else if (scsi_status == SCSI_STAT_CHECK_COND) {
check_underrun = 1;
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
}
else {
check_underrun = 0;
}
pkt->pkt_resp_resid = 0;
pkt->pkt_data_resid = 0;
if (pkt->pkt_datalen == 0) {
goto done;
}
rsp_data_resid = (rsp->fcp_u.fcp_status.resid_under) ?
LE_SWAP32(rsp->fcp_resid) : 0;
pkt->pkt_data_resid = rsp_data_resid;
if (pkt->pkt_tran_type == FC_PKT_FCP_READ) {
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
data_rx = pkt->pkt_datalen - pkt->pkt_data_resid;
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 ((scsi_status != SCSI_STAT_GOOD) && (data_rx == 0)) {
fix_it = 1;
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_fcp_completion_error_msg,
"Underrun(1). Fixed. "
"did=0x%06x sbp=%p cmd=%02x "
"dl=%d,%d rx=%d rsp=%d",
did, sbp, scsi_opcode,
pkt->pkt_datalen, scsi_dl,
(pkt->pkt_datalen -
pkt->pkt_data_resid),
rsp_data_resid);
}
}
#endif
#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 ((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);
}
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
if (fix_it) {
rsp->fcp_u.fcp_status.resid_under = 1;
rsp->fcp_resid =
LE_SWAP32(pkt->pkt_data_resid);
} else {
iostat = IOSTAT_DATA_UNDERRUN;
pkt->pkt_data_resid =
pkt->pkt_datalen;
}
}
else if (rsp_data_resid > pkt->pkt_data_resid) {
iostat = IOSTAT_DATA_OVERRUN;
pkt->pkt_data_resid = pkt->pkt_datalen;
}
} else if ((hba->sli_mode == EMLXS_HBA_SLI4_MODE) &&
(pkt->pkt_tran_type == FC_PKT_FCP_WRITE)) {
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
data_rx = pkt->pkt_datalen - pkt->pkt_data_resid;
if (check_underrun && (pkt->pkt_data_resid > rsp_data_resid)) {
scsi_dl = pkt->pkt_datalen;
#ifdef FCP_UNDERRUN_PATCH1
if (cfg[CFG_ENABLE_PATCH].current & FCP_UNDERRUN_PATCH1) {
if ((scsi_status != SCSI_STAT_GOOD) && (data_rx == 0)) {
fix_it = 1;
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_fcp_completion_error_msg,
"Underrun(1). Fixed. "
"did=0x%06x sbp=%p cmd=%02x "
"dl=%d,%d rx=%d rsp=%d",
did, sbp, scsi_opcode,
pkt->pkt_datalen, scsi_dl,
(pkt->pkt_datalen -
pkt->pkt_data_resid),
rsp_data_resid);
}
}
#endif
if (fix_it) {
rsp->fcp_u.fcp_status.resid_under = 1;
rsp->fcp_resid =
LE_SWAP32(pkt->pkt_data_resid);
} else {
iostat = IOSTAT_DATA_UNDERRUN;
pkt->pkt_data_resid =
pkt->pkt_datalen;
}
}
else if (rsp_data_resid > pkt->pkt_data_resid) {
iostat = IOSTAT_DATA_OVERRUN;
pkt->pkt_data_resid = pkt->pkt_datalen;
}
}
done:
switch (iostat) {
case IOSTAT_SUCCESS:
if (pkt->pkt_rsplen) {
bzero((uint8_t *)pkt->pkt_resp, pkt->pkt_rsplen);
}
break;
case IOSTAT_FCP_RSP_ERROR:
break;
case IOSTAT_REMOTE_STOP:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Remote Stop. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_LOCAL_REJECT:
localstat = cmd->un.grsp.perr.statLocalError;
switch (localstat) {
case IOERR_SEQUENCE_TIMEOUT:
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_fcp_completion_error_msg,
"Local reject. "
"%s did=0x%06x sbp=%p cmd=%02x tmo=%d ",
emlxs_error_xlate(localstat), did, sbp,
scsi_opcode, pkt->pkt_timeout);
break;
default:
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_fcp_completion_error_msg,
"Local reject. %s 0x%06x %p %02x (%x)(%x)",
emlxs_error_xlate(localstat), did, sbp,
scsi_opcode, (uint16_t)cmd->ULPIOTAG,
(uint16_t)cmd->ULPCONTEXT);
}
break;
case IOSTAT_NPORT_RJT:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Nport reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_FABRIC_RJT:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Fabric reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_NPORT_BSY:
#ifdef SAN_DIAG_SUPPORT
ndlp = (NODELIST *)iocbq->node;
emlxs_log_sd_fc_bsy_event(port, (HBA_WWN *)&ndlp->nlp_portname);
#endif
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Nport busy. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_FABRIC_BSY:
#ifdef SAN_DIAG_SUPPORT
ndlp = (NODELIST *)iocbq->node;
emlxs_log_sd_fc_bsy_event(port, NULL);
#endif
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Fabric busy. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_INTERMED_RSP:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Intermediate response. did=0x%06x sbp=%p cmd=%02x", did,
sbp, scsi_opcode);
break;
case IOSTAT_LS_RJT:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"LS Reject. did=0x%06x sbp=%p cmd=%02x", did, sbp,
scsi_opcode);
break;
case IOSTAT_DATA_UNDERRUN:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Underrun. did=0x%06x sbp=%p cmd=%02x "
"dl=%d,%d rx=%d rsp=%d (%02x,%02x,%02x,%02x)",
did, sbp, scsi_opcode, pkt->pkt_datalen, scsi_dl, data_rx,
rsp_data_resid, scsi_status, sense, asc, ascq);
break;
case IOSTAT_DATA_OVERRUN:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Overrun. did=0x%06x sbp=%p cmd=%02x "
"dl=%d,%d rx=%d rsp=%d (%02x,%02x,%02x,%02x)",
did, sbp, scsi_opcode, pkt->pkt_datalen, scsi_dl, data_rx,
rsp_data_resid, scsi_status, sense, asc, ascq);
break;
case IOSTAT_RSP_INVALID:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Rsp Invalid. did=0x%06x sbp=%p cmd=%02x dl=%d rl=%d"
"(%d, %d, %d)",
did, sbp, scsi_opcode, pkt->pkt_datalen, pkt->pkt_rsplen,
LE_SWAP32(rsp->fcp_resid),
LE_SWAP32(rsp->fcp_sense_len),
LE_SWAP32(rsp->fcp_response_len));
break;
default:
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fcp_completion_error_msg,
"Unknown status=%x reason=%x did=0x%06x sbp=%p cmd=%02x",
iostat, cmd->un.grsp.perr.statLocalError, did, sbp,
scsi_opcode);
break;
}
if (iostat == IOSTAT_SUCCESS) {
HBASTATS.FcpGood++;
} else {
HBASTATS.FcpError++;
}
mutex_exit(&sbp->mtx);
emlxs_pkt_complete(sbp, iostat, localstat, 0);
return;
}
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
else {
return (0);
}
while (cnt) {
if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == 0) {
rp->fc_missbufcnt = cnt;
return (cnt);
}
iocbq->channel = (void *)&hba->chan[rp->ringno];
iocbq->port = (void *)port;
iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);
icmd = &iocbq->iocb;
for (i = 0; i < maxqbuf; i++) {
if (cnt <= 0)
break;
if ((mp = (MATCHMAP *)emlxs_mem_get(hba, seg))
== 0) {
icmd->ULPBDECOUNT = i;
for (j = 0; j < i; j++) {
mp = EMLXS_GET_VADDR(hba, rp, icmd);
if (mp) {
emlxs_mem_put(hba, seg,
(void *)mp);
}
}
rp->fc_missbufcnt = cnt + i;
emlxs_mem_put(hba, MEM_IOCB, (void *)iocbq);
return (cnt + i);
}
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;
cnt--;
}
icmd->ULPIOTAG = tag;
icmd->ULPBDECOUNT = i;
icmd->ULPLE = 1;
icmd->ULPOWNER = OWN_CHIP;
iocbq->bp = (void *)mp;
EMLXS_SLI_ISSUE_IOCB_CMD(hba, &hba->chan[rp->ringno], iocbq);
}
rp->fc_missbufcnt = 0;
return (0);
}
static void
emlxs_fcp_tag_nodes(emlxs_port_t *port)
{
NODELIST *nlp;
int i;
rw_enter(&port->node_rwlock, RW_READER);
for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
nlp = port->node_table[i];
while (nlp != NULL) {
nlp->nlp_tag = 1;
nlp = nlp->nlp_list_next;
}
}
rw_exit(&port->node_rwlock);
}
static NODELIST *
emlxs_find_tagged_node(emlxs_port_t *port)
{
NODELIST *nlp;
NODELIST *tagged;
int i;
rw_enter(&port->node_rwlock, RW_READER);
tagged = 0;
for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
nlp = port->node_table[i];
while (nlp != NULL) {
if (!nlp->nlp_tag) {
nlp = nlp->nlp_list_next;
continue;
}
nlp->nlp_tag = 0;
if (nlp->nlp_Rpi == FABRIC_RPI) {
nlp = nlp->nlp_list_next;
continue;
}
tagged = nlp;
break;
}
if (tagged) {
break;
}
}
rw_exit(&port->node_rwlock);
return (tagged);
}
extern int
emlxs_port_offline(emlxs_port_t *port, uint32_t scope)
{
emlxs_hba_t *hba = HBA;
emlxs_config_t *cfg;
NODELIST *nlp;
fc_affected_id_t *aid;
uint32_t mask;
uint32_t aff_d_id;
uint32_t linkdown;
uint32_t vlinkdown;
uint32_t action;
int i;
uint32_t unreg_vpi;
uint32_t update;
uint32_t adisc_support;
uint32_t clear_all;
uint8_t format;
if ((port->mode == MODE_TARGET) && (scope != 0xffffffff) &&
(scope != 0xfeffffff) && (scope != 0xfdffffff)) {
return (0);
}
cfg = &CFG;
aid = (fc_affected_id_t *)&scope;
linkdown = 0;
vlinkdown = 0;
unreg_vpi = 0;
update = 0;
clear_all = 0;
if (!(port->flag & EMLXS_PORT_BOUND)) {
return (0);
}
format = aid->aff_format;
switch (format) {
case 0:
mask = 0x00ffffff;
break;
case 1:
mask = 0x00ffff00;
break;
case 2:
mask = 0x00ff0000;
break;
case 3:
mask = 0x00000000;
break;
#ifdef DHCHAP_SUPPORT
case 0xfe:
mask = 0x00000000;
vlinkdown = 1;
break;
#endif
case 0xff:
mask = 0x00000000;
linkdown = 1;
break;
case 0xfd:
default:
mask = 0x00000000;
linkdown = 1;
clear_all = 1;
break;
}
aff_d_id = aid->aff_d_id & mask;
if (linkdown) {
hba->flag &= ~FC_GPIO_LINK_UP;
mutex_enter(&EMLXS_PORT_LOCK);
port->flag &= EMLXS_PORT_LINKDOWN_MASK;
if (port->ulp_statec != FC_STATE_OFFLINE) {
port->ulp_statec = FC_STATE_OFFLINE;
port->prev_did = port->did;
port->did = 0;
port->rdid = 0;
bcopy(&port->fabric_sparam, &port->prev_fabric_sparam,
sizeof (SERV_PARM));
bzero(&port->fabric_sparam, sizeof (SERV_PARM));
update = 1;
}
mutex_exit(&EMLXS_PORT_LOCK);
emlxs_timer_cancel_clean_address(port);
if (update) {
if (port->flag & EMLXS_PORT_BOUND) {
if (port->vpi == 0) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_link_down_msg, NULL);
}
if (port->mode == MODE_INITIATOR) {
emlxs_fca_link_down(port);
}
#ifdef SFCT_SUPPORT
else if (port->mode == MODE_TARGET) {
emlxs_fct_link_down(port);
}
#endif
} else {
if (port->vpi == 0) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_link_down_msg, "*");
}
}
}
unreg_vpi = 1;
#ifdef DHCHAP_SUPPORT
emlxs_dhc_auth_stop(port, NULL);
#endif
(void) emlxs_tx_node_flush(port, &port->node_base, 0, 0, 0);
(void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0);
emlxs_ub_flush(port);
}
#ifdef DHCHAP_SUPPORT
else if (vlinkdown) {
mutex_enter(&EMLXS_PORT_LOCK);
if (port->ulp_statec != FC_STATE_OFFLINE) {
port->ulp_statec = FC_STATE_OFFLINE;
update = 1;
}
mutex_exit(&EMLXS_PORT_LOCK);
emlxs_timer_cancel_clean_address(port);
if (update) {
if (port->flag & EMLXS_PORT_BOUND) {
if (port->vpi == 0) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_link_down_msg,
"Switch authentication failed.");
}
if (port->mode == MODE_INITIATOR) {
emlxs_fca_link_down(port);
}
#ifdef SFCT_SUPPORT
else if (port->mode == MODE_TARGET) {
emlxs_fct_link_down(port);
}
#endif
} else {
if (port->vpi == 0) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_link_down_msg,
"Switch authentication failed. *");
}
}
}
(void) emlxs_tx_node_flush(port, &port->node_base, 0, 0, 0);
(void) emlxs_chipq_node_flush(port, 0, &port->node_base, 0);
}
#endif
else {
emlxs_timer_cancel_clean_address(port);
}
if (port->mode == MODE_TARGET) {
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
emlxs_fcp_tag_nodes(port);
unreg_vpi = 0;
while ((nlp = emlxs_find_tagged_node(port))) {
(void) emlxs_rpi_pause_notify(port,
nlp->rpip);
}
}
goto done;
}
emlxs_fcp_tag_nodes(port);
if (!clear_all && (hba->flag & FC_ONLINE_MODE)) {
adisc_support = cfg[CFG_ADISC_SUPPORT].current;
} else {
adisc_support = 0;
}
switch (adisc_support) {
case 0:
for (;;) {
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;
if ((nlp->nlp_DID & mask) == aff_d_id) {
if (linkdown) {
action = 1;
break;
} else {
action = 2;
break;
}
}
nlp = nlp->nlp_list_next;
}
if (action) {
break;
}
}
rw_exit(&port->node_rwlock);
if (action == 0) {
break;
} else if (action == 1) {
(void) EMLXS_SLI_UNREG_NODE(port, nlp,
NULL, NULL, NULL);
} else if (action == 2) {
EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);
#ifdef DHCHAP_SUPPORT
emlxs_dhc_auth_stop(port, nlp);
#endif
emlxs_node_close(port, nlp,
hba->channel_fcp, 60);
emlxs_node_close(port, nlp,
hba->channel_ip, 60);
(void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);
(void) emlxs_chipq_node_flush(port, 0, nlp, 0);
}
}
break;
case 1:
for (;;) {
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;
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;
}
else if ((nlp->nlp_DID & mask) ==
aff_d_id) {
if (linkdown) {
action = 1;
break;
} else {
action = 2;
break;
}
}
nlp = nlp->nlp_list_next;
}
if (action) {
break;
}
}
rw_exit(&port->node_rwlock);
if (action == 0) {
break;
} else if (action == 1) {
(void) EMLXS_SLI_UNREG_NODE(port, nlp,
NULL, NULL, NULL);
} else if (action == 2) {
EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);
#ifdef DHCHAP_SUPPORT
emlxs_dhc_auth_stop(port, nlp);
#endif
emlxs_node_close(port, nlp,
hba->channel_fcp, 60);
emlxs_node_close(port, nlp,
hba->channel_ip, 60);
(void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);
(void) emlxs_chipq_node_flush(port, 0, nlp, 0);
} else if (action == 3) {
EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);
unreg_vpi = 0;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
(void) emlxs_rpi_pause_notify(port,
nlp->rpip);
}
#ifdef DHCHAP_SUPPORT
emlxs_dhc_auth_stop(port, nlp);
#endif
emlxs_node_close(port, nlp,
hba->channel_fcp, -1);
emlxs_node_close(port, nlp, hba->channel_ip,
((linkdown) ? 0 : 60));
(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);
(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:
if (!linkdown && !vlinkdown) {
break;
}
for (;;) {
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;
if ((nlp-> nlp_fcp_info &
NLP_FCP_TGT_DEVICE) &&
(nlp->nlp_DID & mask) ==
aff_d_id) {
action = 3;
break;
}
else if ((nlp->nlp_DID & mask) ==
aff_d_id) {
if (linkdown) {
action = 1;
break;
} else {
action = 2;
break;
}
}
nlp = nlp->nlp_list_next;
}
if (action) {
break;
}
}
rw_exit(&port->node_rwlock);
if (action == 0) {
break;
} else if (action == 1) {
(void) EMLXS_SLI_UNREG_NODE(port, nlp,
NULL, NULL, NULL);
} else if (action == 2) {
EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);
emlxs_node_close(port, nlp,
hba->channel_fcp, 60);
emlxs_node_close(port, nlp,
hba->channel_ip, 60);
(void) emlxs_tx_node_flush(port, nlp, 0, 0, 0);
(void) emlxs_chipq_node_flush(port, 0, nlp, 0);
} else if (action == 3) {
EMLXS_SET_DFC_STATE(nlp, NODE_LIMBO);
unreg_vpi = 0;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
(void) emlxs_rpi_pause_notify(port,
nlp->rpip);
}
emlxs_node_close(port, nlp,
hba->channel_fcp, -1);
emlxs_node_close(port, nlp, hba->channel_ip,
((linkdown) ? 0 : 60));
(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);
(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;
}
done:
if (unreg_vpi) {
(void) emlxs_mb_unreg_vpi(port);
}
return (0);
}
extern void
emlxs_port_online(emlxs_port_t *vport)
{
emlxs_hba_t *hba = vport->hba;
emlxs_port_t *port = &PPORT;
NODELIST *nlp;
uint32_t state;
uint32_t update;
uint32_t npiv_linkup;
char topology[32];
char linkspeed[32];
char mode[32];
if ((vport->vpi > 0) &&
(!(hba->flag & FC_NPIV_ENABLED) ||
!(hba->flag & FC_NPIV_SUPPORTED))) {
return;
}
if (!(vport->flag & EMLXS_PORT_BOUND) ||
!(vport->flag & EMLXS_PORT_ENABLED)) {
return;
}
if (port->mode == MODE_TARGET) {
(void) strlcpy(mode, ", target", sizeof (mode));
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
emlxs_fcp_tag_nodes(vport);
while ((nlp = emlxs_find_tagged_node(vport))) {
(void) emlxs_rpi_resume_notify(vport,
nlp->rpip, 0);
}
}
} else if (port->mode == MODE_INITIATOR) {
(void) strlcpy(mode, ", initiator", sizeof (mode));
} else {
(void) strlcpy(mode, "unknown", sizeof (mode));
}
mutex_enter(&EMLXS_PORT_LOCK);
if (hba->topology == TOPOLOGY_LOOP) {
state = FC_STATE_LOOP;
(void) strlcpy(topology, ", loop", sizeof (topology));
} else {
state = FC_STATE_ONLINE;
(void) strlcpy(topology, ", fabric", sizeof (topology));
}
switch (hba->linkspeed) {
case 0:
(void) strlcpy(linkspeed, "Gb", sizeof (linkspeed));
state |= FC_STATE_1GBIT_SPEED;
break;
case LA_1GHZ_LINK:
(void) strlcpy(linkspeed, "1Gb", sizeof (linkspeed));
state |= FC_STATE_1GBIT_SPEED;
break;
case LA_2GHZ_LINK:
(void) strlcpy(linkspeed, "2Gb", sizeof (linkspeed));
state |= FC_STATE_2GBIT_SPEED;
break;
case LA_4GHZ_LINK:
(void) strlcpy(linkspeed, "4Gb", sizeof (linkspeed));
state |= FC_STATE_4GBIT_SPEED;
break;
case LA_8GHZ_LINK:
(void) strlcpy(linkspeed, "8Gb", sizeof (linkspeed));
state |= FC_STATE_8GBIT_SPEED;
break;
case LA_10GHZ_LINK:
(void) strlcpy(linkspeed, "10Gb", sizeof (linkspeed));
state |= FC_STATE_10GBIT_SPEED;
break;
case LA_16GHZ_LINK:
(void) strlcpy(linkspeed, "16Gb", sizeof (linkspeed));
state |= FC_STATE_16GBIT_SPEED;
break;
case LA_32GHZ_LINK:
(void) strlcpy(linkspeed, "32Gb", sizeof (linkspeed));
state |= FC_STATE_32GBIT_SPEED;
break;
default:
(void) snprintf(linkspeed, sizeof (linkspeed), "unknown(0x%x)",
hba->linkspeed);
break;
}
npiv_linkup = 0;
update = 0;
if ((hba->state >= FC_LINK_UP) &&
!(hba->flag & FC_LOOPBACK_MODE) && (vport->ulp_statec != state)) {
update = 1;
vport->ulp_statec = state;
if ((vport->vpi > 0) && !(hba->flag & FC_NPIV_LINKUP)) {
hba->flag |= FC_NPIV_LINKUP;
npiv_linkup = 1;
}
}
mutex_exit(&EMLXS_PORT_LOCK);
if (update) {
if (vport->flag & EMLXS_PORT_BOUND) {
if (vport->vpi == 0) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_up_msg,
"%s%s%s", linkspeed, topology, mode);
} else if (npiv_linkup) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_npiv_link_up_msg, "%s%s%s",
linkspeed, topology, mode);
}
if (vport->mode == MODE_INITIATOR) {
emlxs_fca_link_up(vport);
}
#ifdef SFCT_SUPPORT
else if (vport->mode == MODE_TARGET) {
emlxs_fct_link_up(vport);
}
#endif
} 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);
}
}
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);
}
emlxs_ub_flush(vport);
}
hba->flag |= FC_GPIO_LINK_UP;
return;
}
extern void
emlxs_linkdown(emlxs_hba_t *hba)
{
emlxs_port_t *port = &PPORT;
int i;
uint32_t scope;
mutex_enter(&EMLXS_PORT_LOCK);
if (hba->state > FC_LINK_DOWN) {
HBASTATS.LinkDown++;
EMLXS_STATE_CHANGE_LOCKED(hba, FC_LINK_DOWN);
}
scope = (hba->flag & FC_NEW_FABRIC)? 0xFDFFFFFF:0xFFFFFFFF;
hba->flag &= FC_LINKDOWN_MASK;
hba->discovery_timer = 0;
hba->linkup_timer = 0;
mutex_exit(&EMLXS_PORT_LOCK);
for (i = 0; i < MAX_VPORTS; i++) {
port = &VPORT(i);
if (!(port->flag & EMLXS_PORT_BOUND)) {
continue;
}
(void) emlxs_port_offline(port, scope);
}
emlxs_log_link_event(port);
return;
}
extern void
emlxs_linkup(emlxs_hba_t *hba)
{
emlxs_port_t *port = &PPORT;
emlxs_config_t *cfg = &CFG;
mutex_enter(&EMLXS_PORT_LOCK);
emlxs_mode_set(hba);
HBASTATS.LinkUp++;
EMLXS_STATE_CHANGE_LOCKED(hba, FC_LINK_UP);
#ifdef MENLO_SUPPORT
if (hba->flag & FC_MENLO_MODE) {
mutex_exit(&EMLXS_PORT_LOCK);
mutex_enter(&EMLXS_LINKUP_LOCK);
cv_broadcast(&EMLXS_LINKUP_CV);
mutex_exit(&EMLXS_LINKUP_LOCK);
emlxs_log_link_event(port);
return;
}
#endif
hba->linkup_timer = hba->timer_tics + cfg[CFG_LINKUP_TIMEOUT].current;
hba->discovery_timer =
hba->timer_tics + cfg[CFG_LINKUP_TIMEOUT].current +
cfg[CFG_DISC_TIMEOUT].current;
mutex_exit(&EMLXS_PORT_LOCK);
emlxs_log_link_event(port);
return;
}
extern int
emlxs_reset_link(emlxs_hba_t *hba, uint32_t linkup, uint32_t wait)
{
emlxs_port_t *port = &PPORT;
emlxs_config_t *cfg;
MAILBOXQ *mbq = NULL;
MAILBOX *mb = NULL;
int rval = 0;
int tmo;
int rc;
if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))
== NULL) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_failed_msg,
"Unable to allocate mailbox buffer.");
rval = 1;
goto reset_link_fail;
}
if (linkup) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
"Resetting link...");
} else {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
"Disabling link...");
}
mb = (MAILBOX *)mbq;
emlxs_mb_down_link(hba, mbq);
#define MBXERR_LINK_DOWN 0x33
if (wait) {
wait = MBX_WAIT;
} else {
wait = MBX_NOWAIT;
}
rc = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, wait, 0);
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS) &&
(rc != MBXERR_LINK_DOWN)) {
rval = 1;
goto reset_link_fail;
}
tmo = 120;
do {
delay(drv_usectohz(500000));
tmo--;
if (!tmo) {
rval = 1;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg,
"Linkdown timeout.");
goto reset_link_fail;
}
} while ((hba->state >= FC_LINK_UP) && (hba->state != FC_ERROR));
if (linkup) {
if (wait == MBX_NOWAIT) {
if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX))
== NULL) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_link_reset_failed_msg,
"Unable to allocate mailbox buffer.");
rval = 1;
goto reset_link_fail;
}
mb = (MAILBOX *)mbq;
} else {
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;
mutex_enter(&EMLXS_PORT_LOCK);
hba->flag &= ~FC_LOOPBACK_MODE;
hba->loopback_tics = 0;
mutex_exit(&EMLXS_PORT_LOCK);
rc = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, wait, 0);
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
rval = 1;
goto reset_link_fail;
}
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_link_reset_msg, NULL);
}
reset_link_fail:
if ((wait == MBX_WAIT) && mbq) {
emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
}
return (rval);
}
extern int
emlxs_online(emlxs_hba_t *hba)
{
emlxs_port_t *port = &PPORT;
int32_t rval = 0;
uint32_t i = 0;
while (i++ < 30) {
if (hba->flag & (FC_ONLINE_MODE | FC_ONLINING_MODE)) {
return (0);
}
mutex_enter(&EMLXS_PORT_LOCK);
if (hba->flag & (FC_ONLINE_MODE | FC_ONLINING_MODE)) {
mutex_exit(&EMLXS_PORT_LOCK);
return (0);
}
if (hba->flag & FC_OFFLINE_MODE) {
hba->flag &= ~FC_OFFLINE_MODE;
hba->flag |= FC_ONLINING_MODE;
mutex_exit(&EMLXS_PORT_LOCK);
break;
}
mutex_exit(&EMLXS_PORT_LOCK);
BUSYWAIT_MS(1000);
}
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_adapter_trans_msg,
"Going online...");
if (rval = EMLXS_SLI_ONLINE(hba)) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg, "status=%x",
rval);
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_offline_msg, NULL);
mutex_enter(&EMLXS_PORT_LOCK);
hba->flag |= FC_OFFLINE_MODE;
hba->flag &= ~FC_ONLINING_MODE;
mutex_exit(&EMLXS_PORT_LOCK);
return (rval);
}
emlxs_timer_start(hba);
mutex_enter(&EMLXS_PORT_LOCK);
hba->flag |= FC_ONLINE_MODE;
hba->flag &= ~FC_ONLINING_MODE;
mutex_exit(&EMLXS_PORT_LOCK);
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_online_msg, NULL);
#ifdef SFCT_SUPPORT
if (port->flag & EMLXS_TGT_ENABLED) {
(void) emlxs_fct_port_initialize(port);
}
#endif
return (rval);
}
extern int
emlxs_offline(emlxs_hba_t *hba, uint32_t reset_requested)
{
emlxs_port_t *port = &PPORT;
uint32_t i = 0;
int rval = 1;
while (i++ < 30) {
if (hba->flag & (FC_OFFLINE_MODE | FC_OFFLINING_MODE)) {
return (0);
}
mutex_enter(&EMLXS_PORT_LOCK);
if (hba->flag & (FC_OFFLINE_MODE | FC_OFFLINING_MODE)) {
mutex_exit(&EMLXS_PORT_LOCK);
return (0);
}
if (hba->flag & FC_ONLINE_MODE) {
hba->flag &= ~FC_ONLINE_MODE;
hba->flag |= FC_OFFLINING_MODE;
mutex_exit(&EMLXS_PORT_LOCK);
break;
}
mutex_exit(&EMLXS_PORT_LOCK);
BUSYWAIT_MS(1000);
}
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_adapter_trans_msg,
"Going offline...");
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
(void) emlxs_fcf_shutdown_notify(port, 1);
} else {
emlxs_linkdown(hba);
}
#ifdef SFCT_SUPPORT
if (port->flag & EMLXS_TGT_ENABLED) {
(void) emlxs_fct_port_shutdown(port);
}
#endif
if (hba->flag & FC_HARDWARE_ERROR) {
emlxs_mb_fini(hba, NULL, MBX_HARDWARE_ERROR);
}
delay(drv_usectohz(1000000));
emlxs_ffcleanup(hba);
if (hba->bus_type == SBUS_FC) {
WRITE_SBUS_CSR_REG(hba, FC_SHS_REG(hba), 0x9A);
#ifdef FMA_SUPPORT
EMLXS_CHK_ACC_HANDLE(hba, hba->sli.sli3.sbus_csr_handle);
#endif
}
emlxs_timer_stop(hba);
if (emlxs_iotag_flush(hba)) {
delay(drv_usectohz(1000));
}
while (hba->io_poll_count > 0) {
delay(drv_usectohz(2000000));
}
EMLXS_SLI_OFFLINE(hba, reset_requested);
mutex_enter(&EMLXS_PORT_LOCK);
hba->flag |= FC_OFFLINE_MODE;
hba->flag &= ~FC_OFFLINING_MODE;
mutex_exit(&EMLXS_PORT_LOCK);
rval = 0;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_offline_msg, NULL);
return (rval);
}
extern int
emlxs_power_down(emlxs_hba_t *hba)
{
#ifdef FMA_SUPPORT
emlxs_port_t *port = &PPORT;
#endif
int32_t rval = 0;
if ((rval = emlxs_offline(hba, 0))) {
return (rval);
}
EMLXS_SLI_HBA_RESET(hba, 1, 1, 0);
#ifdef FMA_SUPPORT
if (emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle)
!= DDI_FM_OK) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_invalid_access_handle_msg, NULL);
return (1);
}
#endif
return (0);
}
extern int
emlxs_power_up(emlxs_hba_t *hba)
{
#ifdef FMA_SUPPORT
emlxs_port_t *port = &PPORT;
#endif
int32_t rval = 0;
#ifdef FMA_SUPPORT
if (emlxs_fm_check_acc_handle(hba, hba->pci_acc_handle)
!= DDI_FM_OK) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_invalid_access_handle_msg, NULL);
return (1);
}
#endif
if ((rval = emlxs_online(hba))) {
if (hba->pci_cap_offset[PCI_CAP_ID_PM]) {
(void) ddi_put8(hba->pci_acc_handle,
(uint8_t *)(hba->pci_addr +
hba->pci_cap_offset[PCI_CAP_ID_PM] +
PCI_PMCSR),
(uint8_t)PCI_PMCSR_D3HOT);
}
return (rval);
}
return (rval);
}
extern void
emlxs_ffcleanup(emlxs_hba_t *hba)
{
emlxs_port_t *port = &PPORT;
uint32_t i;
EMLXS_SLI_DISABLE_INTR(hba, HC_MBINT_ENA);
for (i = 0; i < MAX_VPORTS; i++) {
port = &VPORT(i);
if (port->node_count) {
(void) EMLXS_SLI_UNREG_NODE(port, 0, 0, 0, 0);
}
}
EMLXS_SLI_DISABLE_INTR(hba, 0);
return;
}
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);
return (iotag);
}
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;
if ((iotag == 0) || (iotag >= hba->max_iotag)) {
return (NULL);
}
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);
mutex_enter(&sbp->mtx);
if (sbp->pkt_flags & PACKET_IN_TXQ) {
sbp->pkt_flags &= ~PACKET_IN_TXQ;
hba->channel_tx_count--;
}
if (sbp->pkt_flags & PACKET_IN_CHIPQ) {
sbp->pkt_flags &= ~PACKET_IN_CHIPQ;
}
if (sbp->bmp) {
emlxs_mem_put(hba, MEM_BPL, (void *)sbp->bmp);
sbp->bmp = 0;
}
mutex_exit(&sbp->mtx);
return (sbp);
}
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 (cp->nodeq.q_first) {
ndlp = (NODELIST *) cp->nodeq.q_first;
if (ndlp->nlp_ptx[channelno].q_first) {
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;
}
if (ndlp->nlp_tx[channelno].q_first) {
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;
}
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 (cp->nodeq.q_last == (void *)ndlp) {
cp->nodeq.q_last = NULL;
cp->nodeq.q_first = NULL;
cp->nodeq.q_cnt = 0;
} else {
cp->nodeq.q_first = ndlp->nlp_next[channelno];
((NODELIST *)cp->nodeq.q_last)->nlp_next[channelno] =
cp->nodeq.q_first;
cp->nodeq.q_cnt--;
}
ndlp->nlp_next[channelno] = NULL;
}
iocbq = (IOCBQ *) abort.q_first;
while (iocbq) {
iocb = &iocbq->iocb;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
sbp = iocbq->sbp;
if (sbp) {
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
}
} else {
sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
iocb->ULPIOTAG, 0);
}
if (sbp && (sbp != STALE_PACKET)) {
mutex_enter(&sbp->mtx);
sbp->pkt_flags |= PACKET_IN_FLUSH;
if (!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;
}
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
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);
}
}
else {
icmd = &iocbq->iocb;
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) {
if (icmd->ULPCOMMAND !=
CMD_QUE_RING_LIST64_CN) {
void *tmp;
RING *rp;
rp = &hba->sli.sli3.
ring[channelno];
for (i = 0;
i < icmd->ULPBDECOUNT;
i++) {
mp = EMLXS_GET_VADDR(
hba, rp, icmd);
tmp = (void *)mp;
if (mp) {
emlxs_mem_put(
hba, MEM_BUF, tmp);
}
}
}
emlxs_mem_put(hba, MEM_IOCB,
(void *)iocbq);
} else {
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;
}
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);
}
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));
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) {
if (ndlp->nlp_ptx[channelno].q_first) {
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;
}
}
if (ndlp->nlp_tx[channelno].q_first) {
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;
}
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 (ndlp->nlp_next[channelno]) {
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 {
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--;
}
ndlp->nlp_next[channelno] = NULL;
}
}
iocbq = (IOCBQ *) abort.q_first;
while (iocbq) {
iocb = &iocbq->iocb;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
sbp = iocbq->sbp;
if (sbp) {
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
}
} else {
sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
iocb->ULPIOTAG, 0);
}
if (sbp && (sbp != STALE_PACKET)) {
mutex_enter(&sbp->mtx);
sbp->pkt_flags |= PACKET_IN_FLUSH;
if (!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;
}
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
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);
}
}
else {
icmd = &iocbq->iocb;
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) {
if (icmd->ULPCOMMAND !=
CMD_QUE_RING_LIST64_CN) {
void *tmp;
RING *rp;
int ch;
ch = ((CHANNEL *)
iocbq->channel)->channelno;
rp = &hba->sli.sli3.ring[ch];
for (i = 0;
i < icmd->ULPBDECOUNT;
i++) {
mp = EMLXS_GET_VADDR(
hba, rp, icmd);
tmp = (void *)mp;
if (mp) {
emlxs_mem_put(
hba, MEM_BUF, tmp);
}
}
}
emlxs_mem_put(hba, MEM_IOCB,
(void *)iocbq);
} else {
EMLXS_SLI_ISSUE_IOCB_CMD(hba,
(CHANNEL *)iocbq->channel, iocbq);
}
} else if (icmd->ULPCOMMAND == CMD_CLOSE_XRI_CN ||
icmd->ULPCOMMAND == CMD_CLOSE_XRI_CX) {
emlxs_tx_put(iocbq, 1);
}
}
iocbq = next;
}
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);
}
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;
mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
for (channelno = 0; channelno < hba->chan_count; channelno++) {
cp = &hba->chan[channelno];
if (chan && cp != chan) {
continue;
}
if (ndlp->nlp_ptx[channelno].q_first) {
count += ndlp->nlp_ptx[channelno].q_cnt;
}
if (ndlp->nlp_tx[channelno].q_first) {
count += ndlp->nlp_tx[channelno].q_cnt;
}
}
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
return (count);
}
extern uint32_t
emlxs_tx_lun_flush(emlxs_port_t *port, NODELIST *ndlp, uint32_t lun,
emlxs_buf_t *fpkt)
{
emlxs_hba_t *hba = HBA;
emlxs_buf_t *sbp;
uint32_t channelno;
IOCBQ *iocbq;
IOCBQ *prev;
IOCBQ *next;
IOCB *iocb;
IOCB *icmd;
Q abort;
uint32_t i;
MATCHMAP *mp;
uint8_t flag[MAX_CHANNEL];
if (lun == EMLXS_LUN_NONE) {
return (0);
}
bzero((void *)&abort, sizeof (Q));
mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
for (channelno = 0; channelno < hba->chan_count; channelno++) {
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;
if (sbp && (sbp->lun == lun)) {
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--;
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;
}
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;
if (sbp && (sbp->lun == lun)) {
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--;
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;
}
}
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
iocb = &iocbq->iocb;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
sbp = iocbq->sbp;
if (sbp) {
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
}
} else {
sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
iocb->ULPIOTAG, 0);
}
if (sbp && (sbp != STALE_PACKET)) {
mutex_enter(&sbp->mtx);
sbp->pkt_flags |= PACKET_IN_FLUSH;
if (!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;
}
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
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);
}
}
else {
icmd = &iocbq->iocb;
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) {
if (icmd->ULPCOMMAND !=
CMD_QUE_RING_LIST64_CN) {
void *tmp;
RING *rp;
int ch;
ch = ((CHANNEL *)
iocbq->channel)->channelno;
rp = &hba->sli.sli3.ring[ch];
for (i = 0;
i < icmd->ULPBDECOUNT;
i++) {
mp = EMLXS_GET_VADDR(
hba, rp, icmd);
tmp = (void *)mp;
if (mp) {
emlxs_mem_put(
hba, MEM_BUF, tmp);
}
}
}
emlxs_mem_put(hba, MEM_IOCB,
(void *)iocbq);
} else {
EMLXS_SLI_ISSUE_IOCB_CMD(hba,
(CHANNEL *)iocbq->channel, iocbq);
}
} else if (icmd->ULPCOMMAND == CMD_CLOSE_XRI_CN ||
icmd->ULPCOMMAND == CMD_CLOSE_XRI_CX) {
emlxs_tx_put(iocbq, 1);
}
}
iocbq = next;
}
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);
}
extern void
emlxs_tx_put(IOCBQ *iocbq, uint32_t lock)
{
emlxs_hba_t *hba;
emlxs_port_t *port;
uint32_t channelno;
NODELIST *nlp;
CHANNEL *cp;
emlxs_buf_t *sbp;
port = (emlxs_port_t *)iocbq->port;
hba = HBA;
cp = (CHANNEL *)iocbq->channel;
nlp = (NODELIST *)iocbq->node;
channelno = cp->channelno;
sbp = (emlxs_buf_t *)iocbq->sbp;
if (nlp == NULL) {
nlp = &port->node_base;
iocbq->node = (void *)nlp;
if (sbp) {
sbp->node = (void *)nlp;
}
}
if (lock) {
mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
}
if (!nlp->nlp_active || (sbp && (sbp->pkt_flags & PACKET_IN_ABORT))) {
if (sbp) {
mutex_enter(&sbp->mtx);
sbp->pkt_flags |= PACKET_IN_FLUSH;
mutex_exit(&sbp->mtx);
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
} else {
(void) emlxs_unregister_pkt(cp, sbp->iotag, 0);
}
if (lock) {
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
}
if (hba->state >= FC_LINK_UP) {
emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
IOERR_ABORT_REQUESTED, 1);
} else {
emlxs_pkt_complete(sbp, IOSTAT_LOCAL_REJECT,
IOERR_LINK_DOWN, 1);
}
return;
} else {
if (lock) {
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
}
emlxs_mem_put(hba, MEM_IOCB, (void *)iocbq);
}
return;
}
if (sbp) {
mutex_enter(&sbp->mtx);
if (sbp->pkt_flags &
(PACKET_IN_COMPLETION | PACKET_IN_CHIPQ | PACKET_IN_TXQ)) {
mutex_exit(&sbp->mtx);
if (lock) {
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
}
return;
}
sbp->pkt_flags |= PACKET_IN_TXQ;
hba->channel_tx_count++;
mutex_exit(&sbp->mtx);
}
if (iocbq->flag & IOCB_PRIORITY) {
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 {
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;
}
if (!nlp->nlp_next[channelno] &&
(!(nlp->nlp_flag[channelno] & NLP_CLOSED) ||
(iocbq->flag & IOCB_PRIORITY))) {
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 (!nlp->nlp_base) {
cp->nodeq.q_last = (uint8_t *)nlp;
} else {
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]++;
cp->timeout = hba->timer_tics + 5;
if (lock) {
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
}
return;
}
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;
if (cp->nodeq.q_first) {
nlp = (NODELIST *)cp->nodeq.q_first;
if (nlp->nlp_ptx[channelno].q_first) {
iocbq = (IOCBQ *)nlp->nlp_ptx[channelno].q_first;
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 {
nlp->nlp_ptx[channelno].q_first =
(void *)iocbq->next;
nlp->nlp_ptx[channelno].q_cnt--;
}
iocbq->next = NULL;
}
else if (nlp->nlp_tx[channelno].q_first &&
!(nlp->nlp_flag[channelno] & NLP_CLOSED)) {
iocbq = (IOCBQ *)nlp->nlp_tx[channelno].q_first;
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 {
nlp->nlp_tx[channelno].q_first =
(void *)iocbq->next;
nlp->nlp_tx[channelno].q_cnt--;
}
iocbq->next = NULL;
}
if ((nlp->nlp_ptx[channelno].q_first) ||
(nlp->nlp_tx[channelno].q_first &&
!(nlp->nlp_flag[channelno] & NLP_CLOSED))) {
if (!nlp->nlp_base) {
cp->nodeq.q_last = (void *)nlp;
cp->nodeq.q_first = nlp->nlp_next[channelno];
}
} else {
if (cp->nodeq.q_last == (void *)nlp) {
cp->nodeq.q_last = NULL;
cp->nodeq.q_first = NULL;
cp->nodeq.q_cnt = 0;
} else {
cp->nodeq.q_first = nlp->nlp_next[channelno];
((NODELIST *)cp->nodeq.q_last)->
nlp_next[channelno] = cp->nodeq.q_first;
cp->nodeq.q_cnt--;
}
nlp->nlp_next[channelno] = NULL;
}
if (!iocbq) {
goto begin;
}
sbp = (emlxs_buf_t *)iocbq->sbp;
if (sbp) {
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]++;
}
cp->timeout = (cp->nodeq.q_first) ? (hba->timer_tics + 5) : 0;
if (lock) {
mutex_exit(&EMLXS_TX_CHANNEL_LOCK);
}
return (iocbq);
}
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;
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;
fchanno = from_chan->channelno;
tchanno = to_chan->channelno;
if (lock) {
mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
}
bzero((void *)&tbm, sizeof (Q));
prev = NULL;
iocbq = (IOCBQ *)nlp->nlp_tx[fchanno].q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocb = &iocbq->iocb;
switch (iocb->ULPCOMMAND) {
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:
break;
default:
prev = iocbq;
iocbq = next;
continue;
}
if (next == NULL) {
nlp->nlp_tx[fchanno].q_last =
(uint8_t *)prev;
}
if (prev == NULL) {
nlp->nlp_tx[fchanno].q_first =
(uint8_t *)next;
} else {
prev->next = next;
}
iocbq->next = NULL;
nlp->nlp_tx[fchanno].q_cnt--;
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;
}
if ((tchanno == hba->channel_fcp) && (tbm.q_cnt != 0)) {
if (from_chan->nodeq.q_first) {
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 (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--;
}
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--;
nlp->nlp_next[fchanno] =
NULL;
}
}
}
}
}
prev = NULL;
iocbq = (IOCBQ *)tbm.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocb = &iocbq->iocb;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
sbp = iocbq->sbp;
if (sbp) {
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 1);
}
} else {
sbp = emlxs_unregister_pkt((CHANNEL *)iocbq->channel,
iocb->ULPIOTAG, 0);
}
if (sbp && (sbp != STALE_PACKET)) {
mutex_enter(&sbp->mtx);
sbp->pkt_flags |= PACKET_IN_FLUSH;
if (!sbp->fpkt && fpkt) {
mutex_enter(&fpkt->mtx);
sbp->fpkt = fpkt;
fpkt->flush_count++;
mutex_exit(&fpkt->mtx);
}
mutex_exit(&sbp->mtx);
}
iocbq = next;
}
iocbq = (IOCBQ *)tbm.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
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);
}
}
else {
icmd = &iocbq->iocb;
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) {
if (icmd->ULPCOMMAND !=
CMD_QUE_RING_LIST64_CN) {
void *tmp;
RING *rp;
int ch;
ch = from_chan->channelno;
rp = &hba->sli.sli3.ring[ch];
for (i = 0;
i < icmd->ULPBDECOUNT;
i++) {
mp = EMLXS_GET_VADDR(
hba, rp, icmd);
tmp = (void *)mp;
if (mp) {
emlxs_mem_put(
hba,
MEM_BUF,
tmp);
}
}
}
emlxs_mem_put(hba, MEM_IOCB,
(void *)iocbq);
} else {
EMLXS_SLI_ISSUE_IOCB_CMD(hba,
from_chan, iocbq);
}
}
}
iocbq = next;
}
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;
}
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);
}
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
emlxs_tx_put(iocbq, 1);
iocbq = next;
}
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);
}
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];
if (!sbp || (sbp == STALE_PACKET)) {
continue;
}
if (sbp->channel != cp) {
continue;
}
hba->fc_table[iotag] = STALE_PACKET;
hba->io_count--;
if (!(sbp->pkt_flags & PACKET_VALID) ||
(sbp->pkt_flags & (PACKET_ULP_OWNED|
PACKET_COMPLETED|PACKET_IN_COMPLETION))) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_debug_msg,
"iotag_flush: Invalid IO found. iotag=%d",
iotag);
continue;
}
sbp->iotag = 0;
iocbq = &sbp->iocbq;
iocb = &iocbq->iocb;
iocb->ULPSTATUS = IOSTAT_LOCAL_REJECT;
iocb->un.grsp.perr.statLocalError = IOERR_LINK_DOWN;
iocb->ULPLE = 1;
iocbq->next = NULL;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
if (sbp->xrip) {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_sli_debug_msg,
"iotag_flush: iotag=%d sbp=%p "
"xrip=%p state=%x flag=%x",
iotag, sbp, sbp->xrip,
sbp->xrip->state, sbp->xrip->flag);
} else {
EMLXS_MSGF(EMLXS_CONTEXT,
&emlxs_sli_debug_msg,
"iotag_flush: iotag=%d sbp=%p "
"xrip=NULL", iotag, sbp);
}
emlxs_sli4_free_xri(port, sbp, sbp->xrip, 0);
} else {
mutex_enter(&sbp->mtx);
if (sbp->pkt_flags & PACKET_IN_TXQ) {
sbp->pkt_flags &= ~PACKET_IN_TXQ;
hba->channel_tx_count --;
}
if (sbp->pkt_flags & PACKET_IN_CHIPQ) {
sbp->pkt_flags &= ~PACKET_IN_CHIPQ;
}
if (sbp->bmp) {
emlxs_mem_put(hba, MEM_BPL,
(void *)sbp->bmp);
sbp->bmp = 0;
}
mutex_exit(&sbp->mtx);
}
mutex_enter(&sbp->mtx);
sbp->node = 0;
mutex_exit(&sbp->mtx);
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);
if (abort.q_first) {
mutex_enter(&cp->rsp_lock);
if (cp->rsp_head == NULL) {
cp->rsp_head = (IOCBQ *)abort.q_first;
cp->rsp_tail = (IOCBQ *)abort.q_last;
} else {
cp->rsp_tail->next = (IOCBQ *)abort.q_first;
cp->rsp_tail = (IOCBQ *)abort.q_last;
}
mutex_exit(&cp->rsp_lock);
emlxs_thread_trigger2(&cp->intr_thread,
emlxs_proc_channel, cp);
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_debug_msg,
"iotag_flush: channel=%d count=%d",
channelno, abort.q_cnt);
count += abort.q_cnt;
}
}
return (count);
}
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);
}
return (count);
}
extern uint32_t
emlxs_chipq_lun_flush(emlxs_port_t *port, NODELIST *ndlp,
uint32_t lun, emlxs_buf_t *fpkt)
{
emlxs_hba_t *hba = HBA;
emlxs_buf_t *sbp;
IOCBQ *iocbq;
IOCBQ *next;
Q abort;
uint32_t iotag;
uint8_t flag[MAX_CHANNEL];
uint32_t channelno;
if (lun == EMLXS_LUN_NONE) {
return (0);
}
bzero((void *)flag, sizeof (flag));
bzero((void *)&abort, sizeof (Q));
mutex_enter(&EMLXS_FCTAB_LOCK);
for (iotag = 1; iotag < hba->max_iotag; iotag++) {
sbp = hba->fc_table[iotag];
if (sbp && (sbp != STALE_PACKET) &&
sbp->pkt_flags & PACKET_IN_CHIPQ &&
sbp->node == ndlp &&
sbp->lun == lun &&
!(sbp->pkt_flags & PACKET_XRI_CLOSED)) {
emlxs_sbp_abort_add(port, sbp,
&abort, flag, fpkt);
}
}
mutex_exit(&EMLXS_FCTAB_LOCK);
iocbq = (IOCBQ *)abort.q_first;
while (iocbq) {
next = (IOCBQ *)iocbq->next;
iocbq->next = NULL;
emlxs_tx_put(iocbq, 1);
iocbq = next;
}
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);
}
extern IOCBQ *
emlxs_create_abort_xri_cn(emlxs_port_t *port, NODELIST *ndlp,
uint16_t iotag, CHANNEL *cp, uint8_t class, int32_t flag)
{
emlxs_hba_t *hba = HBA;
IOCBQ *iocbq;
IOCB *iocb;
emlxs_wqe_t *wqe;
emlxs_buf_t *sbp;
uint16_t abort_iotag;
if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
return (NULL);
}
iocbq->channel = (void *)cp;
iocbq->port = (void *)port;
iocbq->node = (void *)ndlp;
iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);
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];
if (sbp == NULL || sbp == STALE_PACKET || sbp->xrip == NULL) {
wqe->un.Abort.Criteria = ABORT_REQ_TAG;
wqe->AbortTag = iotag;
} else {
wqe->un.Abort.Criteria = ABORT_XRI_TAG;
wqe->AbortTag = sbp->xrip->XRI;
}
wqe->un.Abort.IA = 0;
wqe->RequestTag = abort_iotag;
wqe->Command = CMD_ABORT_XRI_CX;
wqe->Class = CLASS3;
wqe->CQId = (uint16_t)0xffff;
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);
}
extern IOCBQ *
emlxs_create_abort_xri_cx(emlxs_port_t *port, NODELIST *ndlp, uint16_t xid,
CHANNEL *cp, uint8_t class, int32_t flag)
{
emlxs_hba_t *hba = HBA;
IOCBQ *iocbq;
IOCB *iocb;
emlxs_wqe_t *wqe;
uint16_t abort_iotag;
if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
return (NULL);
}
iocbq->channel = (void *)cp;
iocbq->port = (void *)port;
iocbq->node = (void *)ndlp;
iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);
if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
hba->fc_oor_iotag = hba->max_iotag;
}
abort_iotag = hba->fc_oor_iotag++;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
wqe = &iocbq->wqe;
wqe->un.Abort.Criteria = ABORT_XRI_TAG;
wqe->un.Abort.IA = 0;
wqe->RequestTag = abort_iotag;
wqe->AbortTag = xid;
wqe->Command = CMD_ABORT_XRI_CX;
wqe->Class = CLASS3;
wqe->CQId = (uint16_t)0xffff;
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);
}
extern IOCBQ *
emlxs_create_close_xri_cn(emlxs_port_t *port, NODELIST *ndlp,
uint16_t iotag, CHANNEL *cp)
{
emlxs_hba_t *hba = HBA;
IOCBQ *iocbq;
IOCB *iocb;
emlxs_wqe_t *wqe;
emlxs_buf_t *sbp;
uint16_t abort_iotag;
if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
return (NULL);
}
iocbq->channel = (void *)cp;
iocbq->port = (void *)port;
iocbq->node = (void *)ndlp;
iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);
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];
if (sbp == NULL || sbp == STALE_PACKET || sbp->xrip == NULL) {
wqe->un.Abort.Criteria = ABORT_REQ_TAG;
wqe->AbortTag = iotag;
} else {
wqe->un.Abort.Criteria = ABORT_XRI_TAG;
wqe->AbortTag = sbp->xrip->XRI;
}
wqe->un.Abort.IA = 1;
wqe->RequestTag = abort_iotag;
wqe->Command = CMD_ABORT_XRI_CX;
wqe->Class = CLASS3;
wqe->CQId = (uint16_t)0xffff;
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);
}
extern IOCBQ *
emlxs_create_close_xri_cx(emlxs_port_t *port, NODELIST *ndlp, uint16_t xid,
CHANNEL *cp)
{
emlxs_hba_t *hba = HBA;
IOCBQ *iocbq;
IOCB *iocb;
emlxs_wqe_t *wqe;
uint16_t abort_iotag;
if ((iocbq = (IOCBQ *)emlxs_mem_get(hba, MEM_IOCB)) == NULL) {
return (NULL);
}
iocbq->channel = (void *)cp;
iocbq->port = (void *)port;
iocbq->node = (void *)ndlp;
iocbq->flag |= (IOCB_PRIORITY | IOCB_SPECIAL);
if ((hba->fc_oor_iotag >= EMLXS_MAX_ABORT_TAG)) {
hba->fc_oor_iotag = hba->max_iotag;
}
abort_iotag = hba->fc_oor_iotag++;
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
wqe = &iocbq->wqe;
wqe->un.Abort.Criteria = ABORT_XRI_TAG;
wqe->un.Abort.IA = 1;
wqe->RequestTag = abort_iotag;
wqe->AbortTag = xid;
wqe->Command = CMD_ABORT_XRI_CX;
wqe->Class = CLASS3;
wqe->CQId = (uint16_t)0xffff;
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);
}
void
emlxs_close_els_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
CHANNEL *cp;
IOCBQ *iocbq;
IOCB *iocb;
if (rxid == 0 || rxid == 0xFFFF) {
return;
}
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
"Closing ELS exchange: xid=%x", rxid);
if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
return;
}
}
cp = &hba->chan[hba->channel_els];
mutex_enter(&EMLXS_FCTAB_LOCK);
iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);
mutex_exit(&EMLXS_FCTAB_LOCK);
if (iocbq) {
iocb = &iocbq->iocb;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
"Closing ELS exchange: xid=%x iotag=%d", rxid,
iocb->ULPIOTAG);
EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
}
}
void
emlxs_abort_els_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
CHANNEL *cp;
IOCBQ *iocbq;
IOCB *iocb;
if (rxid == 0 || rxid == 0xFFFF) {
return;
}
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
"Aborting ELS exchange: xid=%x", rxid);
if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
return;
}
}
cp = &hba->chan[hba->channel_els];
mutex_enter(&EMLXS_FCTAB_LOCK);
if (hba->state >= FC_LINK_UP) {
iocbq = emlxs_create_abort_xri_cx(port, NULL, rxid, cp,
CLASS3, ABORT_TYPE_ABTS);
} else {
iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);
}
mutex_exit(&EMLXS_FCTAB_LOCK);
if (iocbq) {
iocb = &iocbq->iocb;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
"Aborting ELS exchange: xid=%x iotag=%d", rxid,
iocb->ULPIOTAG);
EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
}
}
void
emlxs_abort_ct_exchange(emlxs_hba_t *hba, emlxs_port_t *port, uint32_t rxid)
{
CHANNEL *cp;
IOCBQ *iocbq;
IOCB *iocb;
if (rxid == 0 || rxid == 0xFFFF) {
return;
}
if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_ct_msg,
"Aborting CT exchange: xid=%x", rxid);
if (emlxs_sli4_unreserve_xri(port, rxid, 1) == 0) {
return;
}
}
cp = &hba->chan[hba->channel_ct];
mutex_enter(&EMLXS_FCTAB_LOCK);
if (hba->state >= FC_LINK_UP) {
iocbq = emlxs_create_abort_xri_cx(port, NULL, rxid, cp,
CLASS3, ABORT_TYPE_ABTS);
} else {
iocbq = emlxs_create_close_xri_cx(port, NULL, rxid, cp);
}
mutex_exit(&EMLXS_FCTAB_LOCK);
if (iocbq) {
iocb = &iocbq->iocb;
EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_unsol_els_msg,
"Aborting CT exchange: xid=%x iotag=%d", rxid,
iocb->ULPIOTAG);
EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, iocbq);
}
}
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;
if (hba->state >= FC_LINK_UP) {
iocbq = emlxs_create_abort_xri_cn(port, ndlp, sbp->iotag, cp,
CLASS3, ABORT_TYPE_ABTS);
} else {
iocbq = emlxs_create_close_xri_cn(port, ndlp, sbp->iotag, cp);
}
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;
}
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 (!sbp->fpkt && fpkt) {
mutex_enter(&fpkt->mtx);
sbp->fpkt = fpkt;
fpkt->flush_count++;
mutex_exit(&fpkt->mtx);
}
mutex_exit(&sbp->mtx);
return;
}