--- old/usr/src/uts/common/io/comstar/port/fct/fct.c	Mon Aug  5 17:27:19 2019
+++ new/usr/src/uts/common/io/comstar/port/fct/fct.c	Mon Aug  5 17:27:19 2019
@@ -113,7 +113,23 @@
 static fct_i_local_port_t *fct_iport_list = NULL;
 static kmutex_t fct_global_mutex;
 uint32_t fct_rscn_options = RSCN_OPTION_VERIFY;
+/*
+ * This is to keep fibre channel from hanging if syseventd is
+ * not working correctly and the queue fills. It is a tunable
+ * to allow the user to force event logging to always happen
+ * which is the default.
+ */
+static uint8_t fct_force_log = 0;  /* use DDI_SLEEP on ddi_log_sysevent */
 
+/*
+ * For use during core examination. These counts are normally really low
+ * since they're bumped during port operations. If a customer core shows
+ * really high values without having an uptime of a year something is most
+ * likely wrong with their environment.
+ */
+int fct_els_cnt = 0;
+int fct_abort_cnt = 0;
+
 int
 _init(void)
 {
@@ -1188,7 +1204,11 @@
 	iport->iport_cached_ncmds = 0;
 	port->port_fca_fcp_cmd_size =
 	    (port->port_fca_fcp_cmd_size + 7) & ~7;
-	iport->iport_cached_cmdlist = NULL;
+	list_create(&iport->iport_cached_cmdlist, sizeof (fct_i_cmd_t),
+	    offsetof(fct_i_cmd_t, icmd_node));
+	list_create(&iport->iport_abort_queue, sizeof (fct_i_cmd_t),
+	    offsetof(fct_i_cmd_t, icmd_node));
+
 	mutex_init(&iport->iport_cached_cmd_lock, NULL, MUTEX_DRIVER, NULL);
 
 	/* Initialize cmd slots */
@@ -1277,7 +1297,7 @@
 fct_deregister_local_port(fct_local_port_t *port)
 {
 	fct_i_local_port_t	*iport;
-	fct_i_cmd_t		*icmd, *next_icmd;
+	fct_i_cmd_t		*icmd;
 	int			ndx;
 
 	iport = (fct_i_local_port_t *)port->port_fct_private;
@@ -1322,9 +1342,9 @@
 	 * I/Os, so we can just release resources.
 	 */
 	ASSERT(iport->iport_total_alloced_ncmds == iport->iport_cached_ncmds);
-	for (icmd = iport->iport_cached_cmdlist; icmd; icmd = next_icmd) {
-		next_icmd = icmd->icmd_next;
-		fct_free(icmd->icmd_cmd);
+	while (!list_is_empty(&iport->iport_cached_cmdlist)) {
+		icmd = list_remove_head(&iport->iport_cached_cmdlist);
+		fct_free(icmd);
 	}
 	mutex_destroy(&iport->iport_cached_cmd_lock);
 	kmem_free(iport->iport_cmd_slots, port->port_max_xchges *
@@ -1477,7 +1497,7 @@
 		logging_out = 0;
 		goto ilo_done;
 	}
-	if ((irp->irp_els_list == NULL) && (irp->irp_deregister_timer)) {
+	if (list_is_empty(&irp->irp_els_list) && (irp->irp_deregister_timer)) {
 		if (force_implicit && irp->irp_nonfcp_xchg_count) {
 			logging_out = 0;
 		} else {
@@ -1485,10 +1505,11 @@
 		}
 		goto ilo_done;
 	}
-	if (irp->irp_els_list) {
+	if (!list_is_empty(&irp->irp_els_list)) {
 		fct_i_cmd_t *icmd;
 		/* Last session affecting ELS should be a LOGO */
-		for (icmd = irp->irp_els_list; icmd; icmd = icmd->icmd_next) {
+		for (icmd = list_head(&irp->irp_els_list); icmd;
+		    icmd = list_next(&irp->irp_els_list, icmd)) {
 			uint8_t op = (ICMD_TO_ELS(icmd))->els_req_payload[0];
 			if (op == ELS_OP_LOGO) {
 				if (force_implicit) {
@@ -1612,7 +1633,8 @@
 
 fct_cmd_t *
 fct_scsi_task_alloc(fct_local_port_t *port, uint16_t rp_handle,
-    uint32_t rportid, uint8_t *lun, uint16_t cdb_length, uint16_t task_ext)
+    uint32_t rportid, uint8_t *lun, uint16_t cdb_length,
+    uint16_t task_ext)
 {
 	fct_cmd_t *cmd;
 	fct_i_cmd_t *icmd;
@@ -1660,8 +1682,8 @@
 	}
 
 	mutex_enter(&iport->iport_cached_cmd_lock);
-	if ((icmd = iport->iport_cached_cmdlist) != NULL) {
-		iport->iport_cached_cmdlist = icmd->icmd_next;
+	if (!list_is_empty(&iport->iport_cached_cmdlist)) {
+		icmd = list_remove_head(&iport->iport_cached_cmdlist);
 		iport->iport_cached_ncmds--;
 		cmd = icmd->icmd_cmd;
 	} else {
@@ -1680,7 +1702,7 @@
 		}
 
 		icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
-		icmd->icmd_next = NULL;
+		list_link_init(&icmd->icmd_node);
 		cmd->cmd_port = port;
 		atomic_inc_32(&iport->iport_total_alloced_ncmds);
 	}
@@ -1884,16 +1906,10 @@
 fct_post_to_discovery_queue(fct_i_local_port_t *iport,
     fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
 {
-	fct_i_cmd_t	**p;
-
 	ASSERT(!MUTEX_HELD(&iport->iport_worker_lock));
 	if (icmd) {
-		icmd->icmd_next = NULL;
-		for (p = &irp->irp_els_list; *p != NULL;
-		    p = &((*p)->icmd_next))
-			;
-
-		*p = icmd;
+		list_insert_tail(&irp->irp_els_list, icmd);
+		fct_els_cnt++;
 		atomic_or_32(&icmd->icmd_flags, ICMD_IN_IRP_QUEUE);
 	}
 
@@ -2125,8 +2141,7 @@
 		if (iport->iport_cached_ncmds < max_cached_ncmds) {
 			icmd->icmd_flags = 0;
 			mutex_enter(&iport->iport_cached_cmd_lock);
-			icmd->icmd_next = iport->iport_cached_cmdlist;
-			iport->iport_cached_cmdlist = icmd;
+			list_insert_head(&iport->iport_cached_cmdlist, icmd);
 			iport->iport_cached_ncmds++;
 			mutex_exit(&iport->iport_cached_cmd_lock);
 		} else {
@@ -2829,8 +2844,8 @@
 	/* For non FCP Rest of the work is done by the terminator */
 	/* For FCP stuff just call stmf */
 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
-		stmf_task_lport_aborted((scsi_task_t *)cmd->cmd_specific,
-		    s, STMF_IOF_LPORT_DONE);
+		stmf_task_lport_aborted_unlocked(
+		    (scsi_task_t *)cmd->cmd_specific, s, STMF_IOF_LPORT_DONE);
 	}
 }
 
@@ -2903,7 +2918,52 @@
 	p[10] = p[11] = 0xff;
 }
 
+/*
+ * fct_cmd_unlink_els -- remove icmd from ELS queue
+ *
+ * The commands are found via the slot array of active commands and will be
+ * terminated shortly after being removed.
+ */
 void
+fct_cmd_unlink_els(fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
+{
+	ASSERT(rw_write_held(&irp->irp_lock));
+	if (icmd->icmd_node.list_next) {
+		/*
+		 * Command is on two queues. Determine which queue and
+		 * handle appropriately.
+		 */
+		if (icmd->icmd_flags & ICMD_IN_IRP_QUEUE) {
+			/*
+			 * If the command is active on the IRP queue it
+			 * will be freed during command termination
+			 * processing. Unfortuntely the ELS processing will
+			 * peek at the command and possibly panic if it's
+			 * been freed already. Remove it from the ELS
+			 * queue to avoid that.
+			 */
+			if (icmd->icmd_flags & ICMD_SESSION_AFFECTING)
+				atomic_dec_16(&irp->irp_sa_elses_count);
+			else
+				atomic_dec_16(&irp->irp_nsa_elses_count);
+			atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_IRP_QUEUE);
+			list_remove(&irp->irp_els_list, icmd);
+		}
+		/*
+		 * There's an else case here, but the processing is handled
+		 * in fct_check_solcmd_queue(). In this case the command
+		 * is on the solicited queue and will be marked as aborted.
+		 * During command termination processing the command will be
+		 * marked as complete, but not freed. The freeing of the memory
+		 * is done in fct_check_solcmd_queue(). If that routine, which
+		 * holds the appropriate lock, is run first it will remove the
+		 * command from the abort queue so that no memory access
+		 * is done after the command has been freed.
+		 */
+	}
+}
+
+void
 fct_handle_rcvd_abts(fct_cmd_t *cmd)
 {
 	char			info[FCT_INFO_LEN];
@@ -2912,10 +2972,11 @@
 	    (fct_i_local_port_t *)port->port_fct_private;
 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
 	fct_i_remote_port_t	*irp;
-	fct_cmd_t		*c = NULL;
+	fct_cmd_t		*c = NULL, *term_cmd;
 	fct_i_cmd_t		*ic = NULL;
 	int			found = 0;
 	int			i;
+	fct_status_t		term_val;
 
 	icmd->icmd_start_time = ddi_get_lbolt();
 	icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
@@ -2998,23 +3059,23 @@
 		return;
 	}
 
+	fct_cmd_unlink_els(irp, ic);
+
 	/* Check if this an abts retry */
 	if (c->cmd_link && (ic->icmd_flags & ICMD_ABTS_RECEIVED)) {
 		/* Kill this abts. */
-		fct_q_for_termination_lock_held(iport, icmd, FCT_ABORTED);
-		if (IS_WORKER_SLEEPING(iport))
-			cv_signal(&iport->iport_worker_cv);
-		mutex_exit(&iport->iport_worker_lock);
-		rw_exit(&irp->irp_lock);
-		rw_exit(&iport->iport_lock);
-		return;
+		term_cmd = icmd->icmd_cmd;
+		term_val = FCT_ABORTED;
+	} else {
+		c->cmd_link = cmd;
+		atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
+		cmd->cmd_link = c;
+		term_cmd = c;
+		term_val = FCT_ABTS_RECEIVED;
 	}
-	c->cmd_link = cmd;
-	atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
-	cmd->cmd_link = c;
 	mutex_exit(&iport->iport_worker_lock);
 	rw_exit(&irp->irp_lock);
-	fct_queue_cmd_for_termination(c, FCT_ABTS_RECEIVED);
+	fct_queue_cmd_for_termination(term_cmd, term_val);
 	rw_exit(&iport->iport_lock);
 }
 
@@ -3045,7 +3106,6 @@
     fct_status_t s)
 {
 	uint32_t old, new;
-	fct_i_cmd_t **ppicmd;
 
 	do {
 		old = icmd->icmd_flags;
@@ -3057,12 +3117,8 @@
 	icmd->icmd_start_time = ddi_get_lbolt();
 	icmd->icmd_cmd->cmd_comp_status = s;
 
-	icmd->icmd_next = NULL;
-	for (ppicmd = &(iport->iport_abort_queue); *ppicmd != NULL;
-	    ppicmd = &((*ppicmd)->icmd_next))
-		;
-
-	*ppicmd = icmd;
+	list_insert_tail(&iport->iport_abort_queue, icmd);
+	fct_abort_cnt++;
 }
 
 /*
@@ -3241,7 +3297,7 @@
 {
 	char			info[FCT_INFO_LEN];
 	clock_t			endtime;
-	fct_i_cmd_t		**ppicmd;
+	fct_i_cmd_t		*next;
 	fct_i_cmd_t		*icmd;
 	fct_cmd_t		*cmd;
 	fct_local_port_t	*port = iport->iport_port;
@@ -3256,10 +3312,10 @@
 
 	/* Start from where we left off last time */
 	if (iport->iport_ppicmd_term) {
-		ppicmd = iport->iport_ppicmd_term;
+		icmd = iport->iport_ppicmd_term;
 		iport->iport_ppicmd_term = NULL;
 	} else {
-		ppicmd = &iport->iport_abort_queue;
+		icmd = list_head(&iport->iport_abort_queue);
 	}
 
 	/*
@@ -3268,7 +3324,7 @@
 	 */
 	mutex_exit(&iport->iport_worker_lock);
 
-	while ((icmd = *ppicmd) != NULL) {
+	while (icmd) {
 		cmd = icmd->icmd_cmd;
 
 		/* Always remember that cmd->cmd_rp can be NULL */
@@ -3301,7 +3357,7 @@
 					    STMF_RFLAG_RESET, info);
 
 					mutex_enter(&iport->iport_worker_lock);
-					iport->iport_ppicmd_term = ppicmd;
+					iport->iport_ppicmd_term = icmd;
 					return (DISC_ACTION_DELAY_RESCAN);
 				}
 				atomic_and_32(&icmd->icmd_flags,
@@ -3329,9 +3385,10 @@
 			fct_done = 0;
 		if ((fca_done || cmd_implicit) && fct_done) {
 			mutex_enter(&iport->iport_worker_lock);
-			ASSERT(*ppicmd == icmd);
-			*ppicmd = (*ppicmd)->icmd_next;
+			next = list_next(&iport->iport_abort_queue, icmd);
+			list_remove(&iport->iport_abort_queue, icmd);
 			mutex_exit(&iport->iport_worker_lock);
+
 			if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) ||
 			    (cmd->cmd_type == FCT_CMD_RCVD_ABTS)) {
 				/* Free the cmd */
@@ -3407,17 +3464,21 @@
 				    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET,
 				    info);
 			}
-			ppicmd = &((*ppicmd)->icmd_next);
+			mutex_enter(&iport->iport_worker_lock);
+			next = list_next(&iport->iport_abort_queue, icmd);
+			mutex_exit(&iport->iport_worker_lock);
 		}
 
 		if (ddi_get_lbolt() > endtime) {
 			mutex_enter(&iport->iport_worker_lock);
-			iport->iport_ppicmd_term = ppicmd;
+			iport->iport_ppicmd_term = next;
 			return (DISC_ACTION_DELAY_RESCAN);
+		} else {
+			icmd = next;
 		}
 	}
 	mutex_enter(&iport->iport_worker_lock);
-	if (iport->iport_abort_queue)
+	if (!list_is_empty(&iport->iport_abort_queue))
 		return (DISC_ACTION_DELAY_RESCAN);
 	if (ret == DISC_ACTION_NO_WORK)
 		return (DISC_ACTION_RESCAN);
@@ -3432,6 +3493,7 @@
 {
 	nvlist_t *attr_list;
 	int port_instance;
+	int rc, sleep = DDI_SLEEP;
 
 	if (!fct_dip)
 		return;
@@ -3452,8 +3514,15 @@
 		goto error;
 	}
 
-	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
-	    subclass, attr_list, NULL, DDI_SLEEP);
+	if (fct_force_log == 0) {
+		sleep = DDI_NOSLEEP;
+	}
+	rc = ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
+	    subclass, attr_list, NULL, sleep);
+	if (rc != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "%s: queue full event lost", __func__);
+		goto error;
+	}
 
 	nvlist_free(attr_list);
 	return;
@@ -3471,6 +3540,7 @@
 {
 	nvlist_t *attr_list;
 	int port_instance;
+	int rc, sleep = DDI_SLEEP;
 
 	if (!fct_dip)
 		return;
@@ -3501,8 +3571,15 @@
 		goto error;
 	}
 
-	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
-	    subclass, attr_list, NULL, DDI_SLEEP);
+	if (fct_force_log == 0) {
+		sleep = DDI_NOSLEEP;
+	}
+	rc = ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
+	    subclass, attr_list, NULL, sleep);
+	if (rc != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "%s:event dropped", __func__);
+		goto error;
+	}
 
 	nvlist_free(attr_list);
 	return;
