view usr/src/uts/common/io/fibre-channel/fca/emlxs/emlxs_clock.c @ 13576:fcde6b8bbfd2

1890 Updated emlxs driver from Emulex Reviewed by: Dan McDonald <danmcd@nexenta.com> Reviewed by: Jean-Yves.Chevallier <Jean-Yves.Chevallier@Emulex.Com> Approved by: Gordon Ross <gwr@nexenta.com>
author Garrett D'Amore <garrett@damore.org>
date Wed, 25 Jan 2012 22:01:17 -0800
parents b23a4dab3d50
children
line wrap: on
line source

/*
 * 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 2011 Emulex.  All rights reserved.
 * Use is subject to license terms.
 */


#include <emlxs.h>

/* Timer period in seconds */
#define	EMLXS_TIMER_PERIOD		1	/* secs */
#define	EMLXS_PKT_PERIOD		5	/* secs */
#define	EMLXS_UB_PERIOD			60	/* secs */

EMLXS_MSG_DEF(EMLXS_CLOCK_C);


static void emlxs_timer_check_loopback(emlxs_hba_t *hba);

#ifdef DHCHAP_SUPPORT
static void emlxs_timer_check_dhchap(emlxs_port_t *port);
#endif /* DHCHAP_SUPPORT */

static void	emlxs_timer(void *arg);
static void	emlxs_timer_check_fw_update(emlxs_hba_t *hba);
static void	emlxs_timer_check_heartbeat(emlxs_hba_t *hba);
static uint32_t	emlxs_timer_check_pkts(emlxs_hba_t *hba, uint8_t *flag);
static void	emlxs_timer_check_nodes(emlxs_port_t *port, uint8_t *flag);
static void	emlxs_timer_check_linkup(emlxs_hba_t *hba);
static void	emlxs_timer_check_discovery(emlxs_port_t *port);
static void	emlxs_timer_check_ub(emlxs_port_t *port);
static void	emlxs_timer_check_channels(emlxs_hba_t *hba, uint8_t *flag);
static uint32_t	emlxs_pkt_chip_timeout(emlxs_port_t *port, emlxs_buf_t *sbp,
			Q *abortq, uint8_t *flag);

#ifdef TX_WATCHDOG
static void	emlxs_tx_watchdog(emlxs_hba_t *hba);
#endif /* TX_WATCHDOG */

extern clock_t
emlxs_timeout(emlxs_hba_t *hba, uint32_t timeout)
{
	emlxs_config_t *cfg = &CFG;
	clock_t time;

	/* Set thread timeout */
	if (cfg[CFG_TIMEOUT_ENABLE].current) {
		(void) drv_getparm(LBOLT, &time);
		time += (timeout * drv_usectohz(1000000));
	} else {
		time = -1;
	}

	return (time);

} /* emlxs_timeout() */


static void
emlxs_timer(void *arg)
{
	emlxs_hba_t *hba = (emlxs_hba_t *)arg;

	if (!hba->timer_id) {
		return;
	}

	mutex_enter(&EMLXS_TIMER_LOCK);

	/* Only one timer thread is allowed */
	if (hba->timer_flags & EMLXS_TIMER_BUSY) {
		mutex_exit(&EMLXS_TIMER_LOCK);
		return;
	}

	/* Check if a kill request has been made */
	if (hba->timer_flags & EMLXS_TIMER_KILL) {
		hba->timer_id = 0;
		hba->timer_flags |= EMLXS_TIMER_ENDED;

		mutex_exit(&EMLXS_TIMER_LOCK);
		return;
	}

	hba->timer_flags |= (EMLXS_TIMER_BUSY | EMLXS_TIMER_STARTED);
	hba->timer_tics = DRV_TIME;

	mutex_exit(&EMLXS_TIMER_LOCK);

	EMLXS_SLI_POLL_ERRATT(hba);

	/* Perform standard checks */
	emlxs_timer_checks(hba);

	/* Restart the timer */
	mutex_enter(&EMLXS_TIMER_LOCK);

	hba->timer_flags &= ~EMLXS_TIMER_BUSY;

	/* If timer is still enabled, restart it */
	if (!(hba->timer_flags & EMLXS_TIMER_KILL)) {
		hba->timer_id =
		    timeout(emlxs_timer, (void *)hba,
		    (EMLXS_TIMER_PERIOD * drv_usectohz(1000000)));
	} else {
		hba->timer_id = 0;
		hba->timer_flags |= EMLXS_TIMER_ENDED;
	}

	mutex_exit(&EMLXS_TIMER_LOCK);

	return;

} /* emlxs_timer() */


extern void
emlxs_timer_checks(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	uint8_t flag[MAX_CHANNEL];
	uint32_t i;
	uint32_t rc;

	/* Exit if we are still initializing */
	if (hba->state < FC_LINK_DOWN) {
		return;
	}

	bzero((void *)flag, sizeof (flag));

	/* Check SLI level timeouts */
	EMLXS_SLI_TIMER(hba);

	/* Check event queue */
	emlxs_timer_check_events(hba);

	/* Check heartbeat timer */
	emlxs_timer_check_heartbeat(hba);

	/* Check fw update timer */
	emlxs_timer_check_fw_update(hba);

#ifdef IDLE_TIMER
	emlxs_pm_idle_timer(hba);
#endif /* IDLE_TIMER */

	/* Check for loopback timeouts */
	emlxs_timer_check_loopback(hba);

	/* Check for packet timeouts */
	rc = emlxs_timer_check_pkts(hba, flag);

	if (rc) {
		/* Link or adapter is being reset */
		return;
	}

	/* Check for linkup timeout */
	emlxs_timer_check_linkup(hba);

	/* Check the ports */
	for (i = 0; i < MAX_VPORTS; i++) {
		port = &VPORT(i);

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

		/* Check for node gate timeouts */
		emlxs_timer_check_nodes(port, flag);

		/* Check for tape discovery timeout */
		emlxs_timer_check_discovery(port);

		/* Check for UB timeouts */
		emlxs_timer_check_ub(port);

#ifdef DHCHAP_SUPPORT
		/* Check for DHCHAP authentication timeouts */
		emlxs_timer_check_dhchap(port);
#endif /* DHCHAP_SUPPORT */

	}

	/* Check for IO channel service timeouts */
	/* Always do this last */
	emlxs_timer_check_channels(hba, flag);

	return;

} /* emlxs_timer_checks() */


extern void
emlxs_timer_start(emlxs_hba_t *hba)
{
	if (hba->timer_id) {
		return;
	}

	/* Restart the timer */
	mutex_enter(&EMLXS_TIMER_LOCK);
	if (!hba->timer_id) {
		hba->timer_flags = 0;
		hba->timer_id =
		    timeout(emlxs_timer, (void *)hba, drv_usectohz(1000000));
	}
	mutex_exit(&EMLXS_TIMER_LOCK);

} /* emlxs_timer_start() */


extern void
emlxs_timer_stop(emlxs_hba_t *hba)
{
	if (!hba->timer_id) {
		return;
	}

	mutex_enter(&EMLXS_TIMER_LOCK);
	hba->timer_flags |= EMLXS_TIMER_KILL;

	while (hba->timer_id) {
		mutex_exit(&EMLXS_TIMER_LOCK);
		delay(drv_usectohz(500000));
		mutex_enter(&EMLXS_TIMER_LOCK);
	}
	mutex_exit(&EMLXS_TIMER_LOCK);

	return;

} /* emlxs_timer_stop() */


static uint32_t
emlxs_timer_check_pkts(emlxs_hba_t *hba, uint8_t *flag)
{
	emlxs_port_t *port = &PPORT;
	emlxs_config_t *cfg = &CFG;
	Q tmo;
	int32_t channelno;
	CHANNEL *cp;
	NODELIST *nlp;
	IOCBQ *prev;
	IOCBQ *next;
	IOCB *iocb;
	IOCBQ *iocbq;
	emlxs_buf_t *sbp;
	fc_packet_t *pkt;
	Q abort;
	uint32_t iotag;
	uint32_t rc;

	if (!cfg[CFG_TIMEOUT_ENABLE].current) {
		return (0);
	}

	if (hba->pkt_timer > hba->timer_tics) {
		return (0);
	}

	hba->pkt_timer = hba->timer_tics + EMLXS_PKT_PERIOD;


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

	/*
	 * We must hold the locks here because we never know when an iocb
	 * will be removed out from under us
	 */

	mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

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

		/* Scan the tx queues for each active node on the channel */

		/* Get the first node */
		nlp = (NODELIST *)cp->nodeq.q_first;

		while (nlp) {
			/* Scan the node's priority tx queue */
			prev = NULL;
			iocbq = (IOCBQ *)nlp->nlp_ptx[channelno].q_first;

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

				/* Check if iocb has timed out */
				if (sbp && hba->timer_tics >= sbp->ticks) {
					/* iocb timed out, now deque it */
					if (next == NULL) {
						nlp->nlp_ptx[channelno].q_last =
						    (uint8_t *)prev;
					}

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

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

					/* Add this iocb to our local */
					/* timout queue */

					/*
					 * This way we don't hold the TX_CHANNEL
					 * lock too long
					 */

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

				} else {
					prev = iocbq;
				}

				iocbq = next;

			}	/* while (iocbq) */


			/* Scan the node's tx queue */
			prev = NULL;
			iocbq = (IOCBQ *)nlp->nlp_tx[channelno].q_first;

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

				/* Check if iocb has timed out */
				if (sbp && hba->timer_tics >= sbp->ticks) {
					/* iocb timed out, now deque it */
					if (next == NULL) {
						nlp->nlp_tx[channelno].q_last =
						    (uint8_t *)prev;
					}

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

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

					/* Add this iocb to our local */
					/* timout queue */

					/*
					 * This way we don't hold the TX_CHANNEL
					 * lock too long
					 */

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

				} else {
					prev = iocbq;
				}

				iocbq = next;

			}	/* while (iocbq) */

			if (nlp == (NODELIST *)cp->nodeq.q_last) {
				nlp = NULL;
			} else {
				nlp = nlp->nlp_next[channelno];
			}

		}	/* while (nlp) */

	}	/* end of for */

	/* Now cleanup the iocb's */
	iocbq = (IOCBQ *)tmo.q_first;
	while (iocbq) {
		/* Free the IoTag and the bmp */
		iocb = &iocbq->iocb;
		channelno = ((CHANNEL *)iocbq->channel)->channelno;
		sbp = iocbq->sbp;
		if (sbp && (sbp != STALE_PACKET)) {
			if (hba->sli_mode == EMLXS_HBA_SLI4_MODE) {
				emlxs_sli4_free_xri(hba, sbp, sbp->xrip, 1);
			} else {
				(void) emlxs_unregister_pkt(
				    (CHANNEL *)iocbq->channel,
				    iocb->ULPIOTAG, 0);
			}

			mutex_enter(&sbp->mtx);
			sbp->pkt_flags |= PACKET_IN_TIMEOUT;
			mutex_exit(&sbp->mtx);
		}

		iocbq = (IOCBQ *)iocbq->next;

	}	/* end of while */

	mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

	/* Now complete the transmit timeouts outside the locks */
	iocbq = (IOCBQ *)tmo.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) {
			/* Warning: Some FCT sbp's don't have */
			/* fc_packet objects */
			pkt = PRIV2PKT(sbp);

			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
			    "TXQ abort: sbp=%p iotag=%x tmo=%d", sbp,
			    sbp->iotag, (pkt) ? pkt->pkt_timeout : 0);

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

		}

		iocbq = next;

	}	/* end of while */



	/* Now check the chip */
	bzero((void *)&abort, sizeof (Q));

	/* Check the HBA for outstanding IOs */
	rc = 0;
	mutex_enter(&EMLXS_FCTAB_LOCK);
	for (iotag = 1; iotag < hba->max_iotag; iotag++) {
		sbp = hba->fc_table[iotag];

		if (!sbp || (sbp == STALE_PACKET)) {
			continue;
		}

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

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

		if ((sbp->pkt_flags & PACKET_IN_CHIPQ) &&
		    (hba->timer_tics >= sbp->ticks)) {
			rc = emlxs_pkt_chip_timeout(sbp->iocbq.port,
			    sbp, &abort, flag);

			if (rc) {
				break;
			}
		}
	}
	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 IO channel service to send these abort iocbq */
	for (channelno = 0; channelno < hba->chan_count; channelno++) {
		if (!flag[channelno]) {
			continue;
		}
		cp = &hba->chan[channelno];

		EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, 0);
	}

	if (rc == 1) {
		/* Spawn a thread to reset the link */
		emlxs_thread_spawn(hba, emlxs_reset_link_thread, NULL, NULL);
	} else if (rc == 2) {
		/* Spawn a thread to reset the adapter */
		emlxs_thread_spawn(hba, emlxs_restart_thread, NULL, NULL);
	}

	return (rc);

} /* emlxs_timer_check_pkts() */


static void
emlxs_timer_check_channels(emlxs_hba_t *hba, uint8_t *flag)
{
	emlxs_port_t *port = &PPORT;
	emlxs_config_t *cfg = &CFG;
	int32_t channelno;
	CHANNEL *cp;
	uint32_t logit;

	if (!cfg[CFG_TIMEOUT_ENABLE].current) {
		return;
	}

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

		logit = 0;

		/* Check for channel timeout now */
		mutex_enter(&EMLXS_TX_CHANNEL_LOCK);
		if (cp->timeout && (hba->timer_tics >= cp->timeout)) {
			/* Check if there is work to do on channel and */
			/* the link is still up */
			if (cp->nodeq.q_first) {
				flag[channelno] = 1;
				cp->timeout = hba->timer_tics + 10;

				if (hba->state >= FC_LINK_UP) {
					logit = 1;
				}
			} else {
				cp->timeout = 0;
			}
		}
		mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

		if (logit) {
			EMLXS_MSGF(EMLXS_CONTEXT,
			    &emlxs_chan_watchdog_msg,
			    "IO Channel %d cnt=%d,%d",
			    channelno,
			    hba->channel_tx_count,
			    hba->io_count);
		}

		/*
		 * If IO channel flag is set, request iocb servicing
		 * here to send any iocb's that may still be queued
		 */
		if (flag[channelno]) {
			EMLXS_SLI_ISSUE_IOCB_CMD(hba, cp, 0);
		}
	}

	return;

} /* emlxs_timer_check_channels() */


static void
emlxs_timer_check_nodes(emlxs_port_t *port, uint8_t *flag)
{
	emlxs_hba_t *hba = HBA;
	uint32_t found;
	uint32_t i;
	NODELIST *nlp;
	int32_t channelno;

	for (;;) {
		/* Check node gate flag for expiration */
		found = 0;

		/*
		 * We need to lock, scan, and unlock because we can't hold the
		 * lock while we call node_open
		 */
		rw_enter(&port->node_rwlock, RW_READER);
		for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
			nlp = port->node_table[i];
			while (nlp != NULL) {
				for (channelno = 0;
				    channelno < hba->chan_count;
				    channelno++) {
					/* Check if the node timer is active */
					/* and if timer has expired */
					if (nlp->nlp_tics[channelno] &&
					    (hba->timer_tics >=
					    nlp->nlp_tics[channelno])) {
						/* If so, set the flag and */
						/* break out */
						found = 1;
						flag[channelno] = 1;
						break;
					}
				}

				if (nlp->nlp_force_rscn &&
				    (hba->timer_tics >= nlp->nlp_force_rscn)) {
					nlp->nlp_force_rscn = 0;
					/*
					 * Generate an RSCN to
					 * wakeup ULP
					 */
					(void) emlxs_generate_rscn(port,
					    nlp->nlp_DID);
				}

				if (found) {
					break;
				}

				nlp = nlp->nlp_list_next;
			}

			if (found) {
				break;
			}

		}
		rw_exit(&port->node_rwlock);

		if (!found) {
			break;
		}

		emlxs_node_timeout(port, nlp, channelno);
	}

} /* emlxs_timer_check_nodes() */


static void
emlxs_timer_check_loopback(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	emlxs_config_t *cfg = &CFG;
	int32_t reset = 0;

	if (!cfg[CFG_TIMEOUT_ENABLE].current) {
		return;
	}

	/* Check the loopback timer for expiration */
	mutex_enter(&EMLXS_PORT_LOCK);

	if (!hba->loopback_tics || (hba->timer_tics < hba->loopback_tics)) {
		mutex_exit(&EMLXS_PORT_LOCK);
		return;
	}

	hba->loopback_tics = 0;

	if (hba->flag & FC_LOOPBACK_MODE) {
		reset = 1;
	}

	mutex_exit(&EMLXS_PORT_LOCK);

	if (reset) {
		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_dfc_debug_msg,
		    "LOOPBACK_MODE: Expired. Resetting...");
		(void) emlxs_reset(port, FC_FCA_LINK_RESET);
	}

	return;

} /* emlxs_timer_check_loopback() */


static void
emlxs_timer_check_linkup(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	uint32_t linkup;

	/* Check if all mbox commands from previous activity are processed */
	if (hba->model_info.sli_mask & EMLXS_SLI4_MASK) {
		mutex_enter(&EMLXS_MBOX_LOCK);
		if (hba->mbox_queue.q_first) {
			mutex_exit(&EMLXS_MBOX_LOCK);
			return;
		}
		mutex_exit(&EMLXS_MBOX_LOCK);
	}

	/* Check the linkup timer for expiration */
	mutex_enter(&EMLXS_PORT_LOCK);
	linkup = 0;
	if (hba->linkup_timer && (hba->timer_tics >= hba->linkup_timer)) {
		hba->linkup_timer = 0;

		/* Make sure link is still ready */
		if (hba->state >= FC_LINK_UP) {
			linkup = 1;
		}
	}
	mutex_exit(&EMLXS_PORT_LOCK);

	/* Make the linkup callback */
	if (linkup) {
		emlxs_port_online(port);
	}
	return;

} /* emlxs_timer_check_linkup() */


static void
emlxs_timer_check_heartbeat(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	MAILBOXQ *mbq;
	emlxs_config_t *cfg = &CFG;
	int rc;

	if (!cfg[CFG_HEARTBEAT_ENABLE].current) {
		return;
	}

	if (hba->timer_tics < hba->heartbeat_timer) {
		return;
	}

	hba->heartbeat_timer = hba->timer_tics + 5;

	/* Return if adapter interrupts have occurred */
	if (hba->heartbeat_flag) {
		hba->heartbeat_flag = 0;
		return;
	}
	/* No adapter interrupts have occured for 5 seconds now */

	/* Return if mailbox is busy */
	/* This means the mailbox timer routine is watching for problems */
	if (hba->mbox_timer) {
		return;
	}

	/* Return if heartbeat is still outstanding */
	if (hba->heartbeat_active) {
		return;
	}

	if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX, 1)) == 0) {
		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
		    "Unable to allocate heartbeat mailbox.");
		return;
	}

	emlxs_mb_heartbeat(hba, mbq);
	hba->heartbeat_active = 1;

	rc =  EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbq, MBX_NOWAIT, 0);
	if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
		emlxs_mem_put(hba, MEM_MBOX, (void *)mbq);
	}

	return;

} /* emlxs_timer_check_heartbeat() */


static void
emlxs_timer_check_fw_update(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;

	if (!(hba->fw_flag & FW_UPDATE_NEEDED)) {
		hba->fw_timer = 0;
		return;
	}

	if (hba->timer_tics < hba->fw_timer) {
		return;
	}

	if (hba->tgt_mode) {
		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fw_update_msg,
		    "A manual HBA reset or link reset (using emlxadm) "
		    "is required.");
	} else {
		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_fw_update_msg,
		    "A manual HBA reset or link reset (using luxadm or fcadm) "
		    "is required.");
	}

	/* Set timer for 24 hours */
	hba->fw_timer = hba->timer_tics + (60 * 60 * 24);

	return;

} /* emlxs_timer_check_fw_update() */


static void
emlxs_timer_check_discovery(emlxs_port_t *port)
{
	emlxs_hba_t *hba = HBA;
	emlxs_config_t *cfg = &CFG;
	int32_t send_clear_la;
	uint32_t found;
	uint32_t i;
	NODELIST *nlp;
	MAILBOXQ *mbox;
	int rc;

	if (!cfg[CFG_TIMEOUT_ENABLE].current) {
		return;
	}

	/* Check the discovery timer for expiration */
	send_clear_la = 0;
	mutex_enter(&EMLXS_PORT_LOCK);
	while (hba->discovery_timer &&
	    (hba->timer_tics >= hba->discovery_timer) &&
	    (hba->state == FC_LINK_UP)) {
		send_clear_la = 1;

		/* Perform a flush on fcp2 nodes that are still closed */
		found = 0;
		rw_enter(&port->node_rwlock, RW_READER);
		for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
			nlp = port->node_table[i];
			while (nlp != NULL) {
				if ((nlp->nlp_fcp_info & NLP_FCP_2_DEVICE) &&
				    (nlp->nlp_flag[hba->channel_fcp] &
				    NLP_CLOSED)) {
					found = 1;
					break;

				}
				nlp = nlp->nlp_list_next;
			}

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

		if (!found) {
			break;
		}

		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_node_missing_msg,
		    "FCP2 device (did=%06x) missing. Flushing...",
		    nlp->nlp_DID);

		mutex_exit(&EMLXS_PORT_LOCK);

		(void) emlxs_mb_unreg_node(port, nlp, NULL, NULL, NULL);

		mutex_enter(&EMLXS_PORT_LOCK);

	}
	mutex_exit(&EMLXS_PORT_LOCK);

	/* Try to send clear link attention, if needed */
	if ((hba->sli_mode < EMLXS_HBA_SLI4_MODE) && (send_clear_la == 1) &&
	    (mbox = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX, 1))) {
		mutex_enter(&EMLXS_PORT_LOCK);

		/*
		 * If state is not FC_LINK_UP, then either the link has gone
		 * down or a FC_CLEAR_LA has already been issued
		 */
		if (hba->state != FC_LINK_UP) {
			mutex_exit(&EMLXS_PORT_LOCK);
			emlxs_mem_put(hba, MEM_MBOX, (void *)mbox);
		} else {
			/* Change state and clear discovery timer */
			EMLXS_STATE_CHANGE_LOCKED(hba, FC_CLEAR_LA);

			hba->discovery_timer = 0;

			mutex_exit(&EMLXS_PORT_LOCK);

			/* Prepare and send the CLEAR_LA command */
			emlxs_mb_clear_la(hba, mbox);

			rc = EMLXS_SLI_ISSUE_MBOX_CMD(hba, mbox, MBX_NOWAIT, 0);
			if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
				emlxs_mem_put(hba, MEM_MBOX, (void *)mbox);
			}
		}
	}

	return;

} /* emlxs_timer_check_discovery()  */


static void
emlxs_timer_check_ub(emlxs_port_t *port)
{
	emlxs_hba_t *hba = HBA;
	emlxs_unsol_buf_t *ulistp;
	fc_unsol_buf_t *ubp;
	emlxs_ub_priv_t *ub_priv;
	uint32_t i;

	if (port->ub_timer > hba->timer_tics) {
		return;
	}

	port->ub_timer = hba->timer_tics + EMLXS_UB_PERIOD;

	/* Check the unsolicited buffers */
	mutex_enter(&EMLXS_UB_LOCK);

	ulistp = port->ub_pool;
	while (ulistp) {
		/* Check buffers in this pool */
		for (i = 0; i < ulistp->pool_nentries; i++) {
			ubp = (fc_unsol_buf_t *)&ulistp->fc_ubufs[i];
			ub_priv = ubp->ub_fca_private;

			if (!(ub_priv->flags & EMLXS_UB_IN_USE)) {
				continue;
			}

			/* If buffer has timed out, print message and */
			/* increase timeout */
			if ((ub_priv->time + ub_priv->timeout) <=
			    hba->timer_tics) {
				ub_priv->flags |= EMLXS_UB_TIMEOUT;

				EMLXS_MSGF(EMLXS_CONTEXT,
				    &emlxs_sfs_debug_msg,
				    "Stale UB buffer detected (%d mins): "
				    "buffer=%p (%x,%x,%x,%x)",
				    (ub_priv->timeout / 60), ubp,
				    ubp->ub_frame.type, ubp->ub_frame.s_id,
				    ubp->ub_frame.ox_id, ubp->ub_frame.rx_id);

				/* Increase timeout period */

				/* If timeout was 5 mins or less, */
				/* increase it to 10 mins */
				if (ub_priv->timeout <= (5 * 60)) {
					ub_priv->timeout = (10 * 60);
				}
				/* If timeout was 10 mins or less, */
				/* increase it to 30 mins */
				else if (ub_priv->timeout <= (10 * 60)) {
					ub_priv->timeout = (30 * 60);
				}
				/* Otherwise double it. */
				else {
					ub_priv->timeout *= 2;
				}
			}
		}

		ulistp = ulistp->pool_next;
	}

	mutex_exit(&EMLXS_UB_LOCK);

	return;

} /* emlxs_timer_check_ub()  */


/* EMLXS_FCTAB_LOCK must be held to call this */
static uint32_t
emlxs_pkt_chip_timeout(emlxs_port_t *port, emlxs_buf_t *sbp, Q *abortq,
    uint8_t *flag)
{
	emlxs_hba_t *hba = HBA;
	CHANNEL *cp = (CHANNEL *)sbp->channel;
	IOCBQ *iocbq = NULL;
	fc_packet_t *pkt;
	uint32_t rc = 0;

	mutex_enter(&sbp->mtx);

	/* Warning: Some FCT sbp's don't have fc_packet objects */
	pkt = PRIV2PKT(sbp);

	switch (sbp->abort_attempts) {
	case 0:

		/* Create the abort IOCB */
		if (hba->state >= FC_LINK_UP) {
			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
			    "chipQ: 1:Aborting. sbp=%p iotag=%x tmo=%d "
			    "flags=%x",
			    sbp, sbp->iotag,
			    (pkt) ? pkt->pkt_timeout : 0, sbp->pkt_flags);

			iocbq =
			    emlxs_create_abort_xri_cn(port, sbp->node,
			    sbp->iotag, cp, sbp->class, ABORT_TYPE_ABTS);

			/* The adapter will make 2 attempts to send ABTS */
			/* with 2*ratov timeout each time */
			sbp->ticks =
			    hba->timer_tics + (4 * hba->fc_ratov) + 10;
		} else {
			EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
			    "chipQ: 1:Closing. sbp=%p iotag=%x tmo=%d "
			    "flags=%x",
			    sbp, sbp->iotag,
			    (pkt) ? pkt->pkt_timeout : 0, sbp->pkt_flags);

			iocbq =
			    emlxs_create_close_xri_cn(port, sbp->node,
			    sbp->iotag, cp);

			sbp->ticks = hba->timer_tics + 30;
		}

		/* set the flags */
		sbp->pkt_flags |= (PACKET_IN_TIMEOUT | PACKET_XRI_CLOSED);

		flag[cp->channelno] = 1;
		rc = 0;

		break;

	case 1:

		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
		    "chipQ: 2:Closing. sbp=%p iotag=%x", sbp, sbp->iotag);

		iocbq =
		    emlxs_create_close_xri_cn(port, sbp->node, sbp->iotag,
		    cp);

		sbp->ticks = hba->timer_tics + 30;

		flag[cp->channelno] = 1;
		rc = 0;

		break;

	case 2:

		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
		    "chipQ: 3:Resetting link. sbp=%p iotag=%x", sbp,
		    sbp->iotag);

		sbp->ticks = hba->timer_tics + 60;
		rc = 1;

		break;

	default:

		EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_pkt_timeout_msg,
		    "chipQ: %d:Resetting adapter. sbp=%p iotag=%x",
		    sbp->abort_attempts, sbp, sbp->iotag);

		sbp->ticks = hba->timer_tics + 60;
		rc = 2;

		break;
	}

	sbp->abort_attempts++;
	mutex_exit(&sbp->mtx);

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

	return (rc);

} /* emlxs_pkt_chip_timeout() */


#ifdef TX_WATCHDOG

static void
emlxs_tx_watchdog(emlxs_hba_t *hba)
{
	emlxs_port_t *port = &PPORT;
	NODELIST *nlp;
	uint32_t channelno;
	CHANNEL *cp;
	IOCBQ *next;
	IOCBQ *iocbq;
	IOCB *iocb;
	uint32_t found;
	MATCHMAP *bmp;
	Q abort;
	uint32_t iotag;
	emlxs_buf_t *sbp;
	fc_packet_t *pkt = NULL;
	uint32_t cmd;
	uint32_t did;

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

	mutex_enter(&EMLXS_TX_CHANNEL_LOCK);

	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_TXQ)) {
			nlp = sbp->node;
			iocbq = &sbp->iocbq;

			channelno = (CHANNEL *)(sbp->channel)->channelno;
			if (iocbq->flag & IOCB_PRIORITY) {
				iocbq =
				    (IOCBQ *)nlp->nlp_ptx[channelno].
				    q_first;
			} else {
				iocbq =
				    (IOCBQ *)nlp->nlp_tx[channelno].
				    q_first;
			}

			/* Find a matching entry */
			found = 0;
			while (iocbq) {
				if (iocbq == &sbp->iocbq) {
					found = 1;
					break;
				}

				iocbq = (IOCBQ *)iocbq->next;
			}

			if (!found) {
				if (!(sbp->pkt_flags & PACKET_STALE)) {
					mutex_enter(&sbp->mtx);
					sbp->pkt_flags |=
					    PACKET_STALE;
					mutex_exit(&sbp->mtx);
				} else {
					if (abort.q_first == 0) {
						abort.q_first =
						    &sbp->iocbq;
					} else {
						((IOCBQ *)abort.
						    q_last)->next =
						    &sbp->iocbq;
					}

					abort.q_last = &sbp->iocbq;
					abort.q_cnt++;
				}

			} else {
				if ((sbp->pkt_flags & PACKET_STALE)) {
					mutex_enter(&sbp->mtx);
					sbp->pkt_flags &=
					    ~PACKET_STALE;
					mutex_exit(&sbp->mtx);
				}
			}
		}
	}
	mutex_exit(&EMLXS_FCTAB_LOCK);

	iocbq = (IOCBQ *)abort.q_first;
	while (iocbq) {
		next = (IOCBQ *)iocbq->next;
		iocbq->next = NULL;
		sbp = (emlxs_buf_t *)iocbq->sbp;

		pkt = PRIV2PKT(sbp);
		if (pkt) {
			did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id);
			cmd = *((uint32_t *)pkt->pkt_cmd);
			cmd = LE_SWAP32(cmd);
		}


		emlxs_tx_put(iocbq, 0);

		iocbq = next;

	}	/* end of while */

	mutex_exit(&EMLXS_TX_CHANNEL_LOCK);

	return;

} /* emlxs_tx_watchdog() */

#endif /* TX_WATCHDOG */


#ifdef DHCHAP_SUPPORT

static void
emlxs_timer_check_dhchap(emlxs_port_t *port)
{
	emlxs_hba_t *hba = HBA;
	uint32_t i;
	NODELIST *ndlp = NULL;

	for (i = 0; i < EMLXS_NUM_HASH_QUES; i++) {
		ndlp = port->node_table[i];

		if (!ndlp) {
			continue;
		}

		/* Check authentication response timeout */
		if (ndlp->node_dhc.nlp_authrsp_tmo &&
		    (hba->timer_tics >= ndlp->node_dhc.nlp_authrsp_tmo)) {
			/* Trigger authresp timeout handler */
			(void) emlxs_dhc_authrsp_timeout(port, ndlp, NULL);
		}

		/* Check reauthentication timeout */
		if (ndlp->node_dhc.nlp_reauth_tmo &&
		    (hba->timer_tics >= ndlp->node_dhc.nlp_reauth_tmo)) {
			/* Trigger reauth timeout handler */
			emlxs_dhc_reauth_timeout(port, NULL, ndlp);
		}
	}
	return;

} /* emlxs_timer_check_dhchap */

#endif /* DHCHAP_SUPPORT */