1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
24  */
25 
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/scsi/scsi.h>
32 #include <sys/scsi/impl/scsi_reset_notify.h>
33 #include <sys/disp.h>
34 #include <sys/byteorder.h>
35 #include <sys/varargs.h>
36 #include <sys/atomic.h>
37 #include <sys/sdt.h>
38 
39 #include <sys/stmf.h>
40 #include <sys/stmf_ioctl.h>
41 #include <sys/portif.h>
42 #include <sys/fct.h>
43 #include <sys/fctio.h>
44 
45 #include "fct_impl.h"
46 #include "discovery.h"
47 
48 static int fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
49 static int fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
50 static int fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
51     void **result);
52 static int fct_open(dev_t *devp, int flag, int otype, cred_t *credp);
53 static int fct_close(dev_t dev, int flag, int otype, cred_t *credp);
54 static int fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
55     cred_t *credp, int *rval);
56 static int fct_fctiocmd(intptr_t data, int mode);
57 void fct_init_kstats(fct_i_local_port_t *iport);
58 
59 static dev_info_t *fct_dip;
60 static struct cb_ops fct_cb_ops = {
61 	fct_open,			/* open */
62 	fct_close,			/* close */
63 	nodev,				/* strategy */
64 	nodev,				/* print */
65 	nodev,				/* dump */
66 	nodev,				/* read */
67 	nodev,				/* write */
68 	fct_ioctl,			/* ioctl */
69 	nodev,				/* devmap */
70 	nodev,				/* mmap */
71 	nodev,				/* segmap */
72 	nochpoll,			/* chpoll */
73 	ddi_prop_op,			/* cb_prop_op */
74 	0,				/* streamtab */
75 	D_NEW | D_MP,			/* cb_flag */
76 	CB_REV,				/* rev */
77 	nodev,				/* aread */
78 	nodev				/* awrite */
79 };
80 
81 static struct dev_ops fct_ops = {
82 	DEVO_REV,
83 	0,
84 	fct_getinfo,
85 	nulldev,		/* identify */
86 	nulldev,		/* probe */
87 	fct_attach,
88 	fct_detach,
89 	nodev,			/* reset */
90 	&fct_cb_ops,
91 	NULL,			/* bus_ops */
92 	NULL			/* power */
93 };
94 
95 #define	FCT_NAME	"COMSTAR FCT"
96 #define	FCT_MODULE_NAME	"fct"
97 
98 extern struct mod_ops mod_driverops;
99 static struct modldrv modldrv = {
100 	&mod_driverops,
101 	FCT_NAME,
102 	&fct_ops
103 };
104 
105 static struct modlinkage modlinkage = {
106 	MODREV_1,
107 	&modldrv,
108 	NULL
109 };
110 
111 static uint32_t	rportid_table_size = FCT_HASH_TABLE_SIZE;
112 static int max_cached_ncmds = FCT_MAX_CACHED_CMDS;
113 static fct_i_local_port_t *fct_iport_list = NULL;
114 static kmutex_t fct_global_mutex;
115 uint32_t fct_rscn_options = RSCN_OPTION_VERIFY;
116 
117 int
118 _init(void)
119 {
120 	int ret;
121 
122 	ret = mod_install(&modlinkage);
123 	if (ret)
124 		return (ret);
125 	/* XXX */
126 	mutex_init(&fct_global_mutex, NULL, MUTEX_DRIVER, NULL);
127 	return (ret);
128 }
129 
130 int
131 _fini(void)
132 {
133 	int ret;
134 
135 	ret = mod_remove(&modlinkage);
136 	if (ret)
137 		return (ret);
138 	/* XXX */
139 	mutex_destroy(&fct_global_mutex);
140 	return (ret);
141 }
142 
143 int
144 _info(struct modinfo *modinfop)
145 {
146 	return (mod_info(&modlinkage, modinfop));
147 }
148 
149 /* ARGSUSED */
150 static int
151 fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
152 {
153 	switch (cmd) {
154 	case DDI_INFO_DEVT2DEVINFO:
155 		*result = fct_dip;
156 		break;
157 	case DDI_INFO_DEVT2INSTANCE:
158 		*result = (void *)(uintptr_t)ddi_get_instance(fct_dip);
159 		break;
160 	default:
161 		return (DDI_FAILURE);
162 	}
163 
164 	return (DDI_SUCCESS);
165 }
166 
167 static int
168 fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
169 {
170 	switch (cmd) {
171 	case DDI_ATTACH:
172 		fct_dip = dip;
173 
174 		if (ddi_create_minor_node(dip, "admin", S_IFCHR, 0,
175 		    DDI_NT_STMF_PP, 0) != DDI_SUCCESS) {
176 			break;
177 		}
178 		ddi_report_dev(dip);
179 		return (DDI_SUCCESS);
180 	}
181 
182 	return (DDI_FAILURE);
183 }
184 
185 static int
186 fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
187 {
188 	switch (cmd) {
189 	case DDI_DETACH:
190 		ddi_remove_minor_node(dip, 0);
191 		return (DDI_SUCCESS);
192 	}
193 
194 	return (DDI_FAILURE);
195 }
196 
197 /* ARGSUSED */
198 static int
199 fct_open(dev_t *devp, int flag, int otype, cred_t *credp)
200 {
201 	if (otype != OTYP_CHR)
202 		return (EINVAL);
203 	return (0);
204 }
205 
206 /* ARGSUSED */
207 static int
208 fct_close(dev_t dev, int flag, int otype, cred_t *credp)
209 {
210 	return (0);
211 }
212 
213 /* ARGSUSED */
214 static int
215 fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
216     cred_t *credp, int *rval)
217 {
218 	int		ret = 0;
219 
220 	if ((cmd & 0xff000000) != FCT_IOCTL) {
221 		return (ENOTTY);
222 	}
223 
224 	if (drv_priv(credp) != 0) {
225 		return (EPERM);
226 	}
227 
228 	switch (cmd) {
229 	case FCTIO_CMD:
230 		ret = fct_fctiocmd(data, mode);
231 		break;
232 	default:
233 		ret = ENOTTY;
234 		break;
235 	}
236 
237 	return (ret);
238 }
239 
240 int
241 fct_copyin_iocdata(intptr_t data, int mode, fctio_t **fctio,
242     void **ibuf, void **abuf, void **obuf)
243 {
244 	int ret = 0;
245 
246 	*ibuf = NULL;
247 	*abuf = NULL;
248 	*obuf = NULL;
249 	*fctio = kmem_zalloc(sizeof (fctio_t), KM_SLEEP);
250 	if (ddi_copyin((void *)data, *fctio, sizeof (fctio_t), mode)) {
251 		ret = EFAULT;
252 		goto copyin_iocdata_done;
253 	}
254 
255 	if ((*fctio)->fctio_ilen) {
256 		*ibuf = kmem_zalloc((*fctio)->fctio_ilen, KM_SLEEP);
257 		if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_ibuf,
258 		    *ibuf, (*fctio)->fctio_ilen, mode)) {
259 			ret = EFAULT;
260 			goto copyin_iocdata_done;
261 		}
262 	}
263 	if ((*fctio)->fctio_alen) {
264 		*abuf = kmem_zalloc((*fctio)->fctio_alen, KM_SLEEP);
265 		if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_abuf,
266 		    *abuf, (*fctio)->fctio_alen, mode)) {
267 			ret = EFAULT;
268 			goto copyin_iocdata_done;
269 		}
270 	}
271 	if ((*fctio)->fctio_olen)
272 		*obuf = kmem_zalloc((*fctio)->fctio_olen, KM_SLEEP);
273 	if (ret == 0)
274 		return (0);
275 	ret = EFAULT;
276 copyin_iocdata_done:
277 	if (*obuf) {
278 		kmem_free(*obuf, (*fctio)->fctio_olen);
279 		*obuf = NULL;
280 	}
281 	if (*abuf) {
282 		kmem_free(*abuf, (*fctio)->fctio_alen);
283 		*abuf = NULL;
284 	}
285 	if (*ibuf) {
286 		kmem_free(*ibuf, (*fctio)->fctio_ilen);
287 		*ibuf = NULL;
288 	}
289 	kmem_free(*fctio, sizeof (fctio_t));
290 	return (ret);
291 }
292 
293 int
294 fct_copyout_iocdata(intptr_t data, int mode, fctio_t *fctio, void *obuf)
295 {
296 	int ret = 0;
297 
298 	if (fctio->fctio_olen) {
299 		ret = ddi_copyout(obuf,
300 		    (void *)(unsigned long)fctio->fctio_obuf, fctio->fctio_olen,
301 		    mode);
302 		if (ret) {
303 			return (EFAULT);
304 		}
305 	}
306 	ret = ddi_copyout(fctio, (void *)data, sizeof (fctio_t), mode);
307 	if (ret) {
308 		return (EFAULT);
309 	}
310 	return (0);
311 }
312 
313 int
314 fct_get_port_list(char *pathList, int count)
315 {
316 	fct_i_local_port_t *iport;
317 	int	i = 0, maxPorts = 0;
318 
319 	ASSERT(pathList != NULL);
320 
321 	mutex_enter(&fct_global_mutex);
322 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
323 		if (i < count)
324 			bcopy(iport->iport_port->port_pwwn,
325 			    pathList + 8 * i, 8);
326 		maxPorts ++;
327 		i++;
328 	}
329 	mutex_exit(&fct_global_mutex);
330 	return (maxPorts);
331 }
332 
333 /* invoked with fct_global_mutex locked */
334 fct_i_local_port_t *
335 fct_get_iport_per_wwn(uint8_t *pwwn)
336 {
337 	fct_i_local_port_t *iport;
338 
339 	ASSERT(mutex_owned(&fct_global_mutex));
340 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
341 		if (bcmp(iport->iport_port->port_pwwn, pwwn, 8) == 0)
342 			return (iport);
343 	}
344 	return (NULL);
345 }
346 
347 int
348 fct_get_adapter_attr(uint8_t *pwwn, fc_tgt_hba_adapter_attributes_t *hba_attr,
349     uint32_t *err_detail)
350 {
351 	fct_i_local_port_t *iport;
352 	fct_port_attrs_t *attr;
353 
354 	hba_attr->version = FCT_HBA_ADAPTER_ATTRIBUTES_VERSION;
355 	iport = fct_get_iport_per_wwn(pwwn);
356 	if (!iport) {
357 		*err_detail = FCTIO_BADWWN;
358 		return (ENXIO);
359 	}
360 
361 	attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
362 	    KM_SLEEP);
363 	mutex_exit(&fct_global_mutex);
364 	iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
365 	mutex_enter(&fct_global_mutex);
366 
367 	bcopy(attr->manufacturer, hba_attr->Manufacturer,
368 	    sizeof (hba_attr->Manufacturer));
369 	bcopy(attr->serial_number, hba_attr->SerialNumber,
370 	    sizeof (hba_attr->SerialNumber));
371 	bcopy(attr->model, hba_attr->Model, sizeof (hba_attr->Model));
372 	bcopy(attr->model_description, hba_attr->ModelDescription,
373 	    sizeof (hba_attr->ModelDescription));
374 	if (iport->iport_port->port_sym_node_name)
375 		bcopy(iport->iport_port->port_sym_node_name,
376 		    hba_attr->NodeSymbolicName,
377 		    strlen(iport->iport_port->port_sym_node_name));
378 	else
379 		bcopy(utsname.nodename, hba_attr->NodeSymbolicName,
380 		    strlen(utsname.nodename));
381 	bcopy(attr->hardware_version, hba_attr->HardwareVersion,
382 	    sizeof (hba_attr->HardwareVersion));
383 	bcopy(attr->option_rom_version, hba_attr->OptionROMVersion,
384 	    sizeof (hba_attr->OptionROMVersion));
385 	bcopy(attr->firmware_version, hba_attr->FirmwareVersion,
386 	    sizeof (hba_attr->FirmwareVersion));
387 	hba_attr->VendorSpecificID = attr->vendor_specific_id;
388 	bcopy(iport->iport_port->port_nwwn, hba_attr->NodeWWN,
389 	    sizeof (hba_attr->NodeWWN));
390 
391 	bcopy(attr->driver_name, hba_attr->DriverName,
392 	    sizeof (hba_attr->DriverName));
393 	bcopy(attr->driver_version, hba_attr->DriverVersion,
394 	    sizeof (hba_attr->DriverVersion));
395 
396 
397 	/* hba_attr->NumberOfPorts = fct_count_fru_ports(iport); */
398 	hba_attr->NumberOfPorts = 1;
399 
400 	kmem_free(attr, sizeof (fct_port_attrs_t));
401 	return (0);
402 }
403 
404 int
405 fct_get_adapter_port_attr(fct_i_local_port_t *ilport, uint8_t *pwwn,
406     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *err_detail)
407 {
408 	fct_i_local_port_t *iport = ilport;
409 	fct_i_remote_port_t *irp = NULL;
410 	fct_port_attrs_t *attr;
411 	int i = 0;
412 
413 	port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
414 
415 	if (!ilport) {
416 		iport = fct_get_iport_per_wwn(pwwn);
417 		if (!iport) {
418 			*err_detail = FCTIO_BADWWN;
419 			return (ENXIO);
420 		}
421 	}
422 
423 	attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
424 	    KM_SLEEP);
425 	mutex_exit(&fct_global_mutex);
426 	iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
427 	mutex_enter(&fct_global_mutex);
428 
429 	port_attr->lastChange = iport->iport_last_change;
430 	bcopy(iport->iport_port->port_nwwn, port_attr->NodeWWN,
431 	    sizeof (port_attr->NodeWWN));
432 	bcopy(iport->iport_port->port_pwwn, port_attr->PortWWN,
433 	    sizeof (port_attr->PortWWN));
434 	bzero(port_attr->FabricName, sizeof (port_attr->FabricName));
435 	port_attr->PortFcId = iport->iport_link_info.portid;
436 	if ((iport->iport_link_state & S_LINK_ONLINE) ||
437 	    (iport->iport_link_state & S_RCVD_LINK_UP)) {
438 		port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
439 	} else {
440 		port_attr->PortState = FC_HBA_PORTSTATE_OFFLINE;
441 	}
442 	switch (iport->iport_link_info.port_topology) {
443 		case PORT_TOPOLOGY_PT_TO_PT:
444 			port_attr->PortType = FC_HBA_PORTTYPE_PTP;
445 			break;
446 		case PORT_TOPOLOGY_PRIVATE_LOOP:
447 			port_attr->PortType = FC_HBA_PORTTYPE_LPORT;
448 			break;
449 		case PORT_TOPOLOGY_PUBLIC_LOOP:
450 			port_attr->PortType = FC_HBA_PORTTYPE_NLPORT;
451 			break;
452 		case PORT_TOPOLOGY_FABRIC_PT_TO_PT:
453 			port_attr->PortType = FC_HBA_PORTTYPE_FPORT;
454 			break;
455 		default:
456 			port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
457 			break;
458 	}
459 	port_attr->PortSupportedClassofService = attr->supported_cos;
460 	port_attr->PortSupportedFc4Types[0] = 0;
461 	port_attr->PortActiveFc4Types[2] = 1;
462 	if (iport->iport_port->port_sym_port_name)
463 		bcopy(iport->iport_port->port_sym_port_name,
464 		    port_attr->PortSymbolicName,
465 		    strlen(iport->iport_port->port_sym_port_name));
466 	else if (iport->iport_port->port_default_alias)
467 		bcopy(iport->iport_port->port_default_alias,
468 		    port_attr->PortSymbolicName,
469 		    strlen(iport->iport_port->port_default_alias));
470 	else
471 		port_attr->PortSymbolicName[0] = 0;
472 	/* the definition is different so need to translate */
473 	if (attr->supported_speed & PORT_SPEED_1G)
474 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_1GBIT;
475 	if (attr->supported_speed & PORT_SPEED_2G)
476 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_2GBIT;
477 	if (attr->supported_speed & PORT_SPEED_4G)
478 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_4GBIT;
479 	if (attr->supported_speed & PORT_SPEED_8G)
480 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_8GBIT;
481 	if (attr->supported_speed & PORT_SPEED_10G)
482 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_10GBIT;
483 	if (attr->supported_speed & PORT_SPEED_16G)
484 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_16GBIT;
485 	switch (iport->iport_link_info.port_speed) {
486 		case PORT_SPEED_1G:
487 			port_attr->PortSpeed = FC_HBA_PORTSPEED_1GBIT;
488 			break;
489 		case PORT_SPEED_2G:
490 			port_attr->PortSpeed = FC_HBA_PORTSPEED_2GBIT;
491 			break;
492 		case PORT_SPEED_4G:
493 			port_attr->PortSpeed = FC_HBA_PORTSPEED_4GBIT;
494 			break;
495 		case PORT_SPEED_8G:
496 			port_attr->PortSpeed = FC_HBA_PORTSPEED_8GBIT;
497 			break;
498 		case PORT_SPEED_10G:
499 			port_attr->PortSpeed = FC_HBA_PORTSPEED_10GBIT;
500 			break;
501 		case PORT_SPEED_16G:
502 			port_attr->PortSpeed = FC_HBA_PORTSPEED_16GBIT;
503 			break;
504 		default:
505 			port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
506 			break;
507 	}
508 	port_attr->PortMaxFrameSize = attr->max_frame_size;
509 	rw_enter(&iport->iport_lock, RW_READER);
510 	port_attr->NumberofDiscoveredPorts = iport->iport_nrps_login;
511 	for (; i < iport->iport_port->port_max_logins; i++) {
512 		irp = iport->iport_rp_slots[i];
513 		if (irp && irp->irp_flags & IRP_PLOGI_DONE) {
514 			if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
515 				port_attr->NumberofDiscoveredPorts --;
516 		}
517 	}
518 	rw_exit(&iport->iport_lock);
519 
520 	kmem_free(attr, sizeof (fct_port_attrs_t));
521 
522 	return (0);
523 }
524 
525 int
526 fct_get_discovered_port_attr(fct_i_remote_port_t *remote_port,
527     uint8_t *port_wwn, uint32_t index, fc_tgt_hba_port_attributes_t *port_attr,
528     uint32_t *error_detail)
529 {
530 	fct_i_local_port_t *iport;
531 	fct_i_remote_port_t *irp = remote_port;
532 	int	count = 0, i = 0;
533 
534 	port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
535 	if (!remote_port) {
536 		iport = fct_get_iport_per_wwn(port_wwn);
537 		if (!iport) {
538 			*error_detail = FCTIO_BADWWN;
539 			return (ENXIO);
540 		}
541 
542 		rw_enter(&iport->iport_lock, RW_READER);
543 
544 		if (index >= iport->iport_nrps_login) {
545 			rw_exit(&iport->iport_lock);
546 			*error_detail = FCTIO_OUTOFBOUNDS;
547 			return (EINVAL);
548 		}
549 		for (; i < iport->iport_port->port_max_logins; i++) {
550 			irp = iport->iport_rp_slots[i];
551 			if (irp && irp->irp_flags & IRP_PLOGI_DONE &&
552 			    !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
553 				count ++;
554 				if ((index + 1) <= count)
555 					break;
556 			}
557 		}
558 		if (i >= iport->iport_port->port_max_logins) {
559 			rw_exit(&iport->iport_lock);
560 			*error_detail = FCTIO_OUTOFBOUNDS;
561 			return (EINVAL);
562 		}
563 		ASSERT(irp);
564 	} else {
565 		iport = (fct_i_local_port_t *)
566 		    irp->irp_rp->rp_port->port_fct_private;
567 	}
568 	port_attr->lastChange = iport->iport_last_change;
569 	rw_enter(&irp->irp_lock, RW_READER);
570 	bcopy(irp->irp_rp->rp_pwwn, port_attr->PortWWN,
571 	    sizeof (port_attr->PortWWN));
572 	bcopy(irp->irp_rp->rp_nwwn, port_attr->NodeWWN,
573 	    sizeof (port_attr->NodeWWN));
574 	port_attr->PortFcId = irp->irp_portid;
575 	if (irp->irp_spn)
576 		(void) strncpy(port_attr->PortSymbolicName, irp->irp_spn,
577 		    strlen(irp->irp_spn));
578 	else
579 		port_attr->PortSymbolicName[0] = '\0';
580 	port_attr->PortSupportedClassofService = irp->irp_cos;
581 	bcopy((caddr_t)irp->irp_fc4types, port_attr->PortActiveFc4Types,
582 	    sizeof (irp->irp_fc4types));
583 	bcopy((caddr_t)irp->irp_fc4types, port_attr->PortSupportedFc4Types,
584 	    sizeof (irp->irp_fc4types));
585 	if (irp->irp_flags & IRP_PLOGI_DONE)
586 		port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
587 	else
588 		port_attr->PortState = FC_HBA_PORTSTATE_UNKNOWN;
589 
590 	port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
591 	port_attr->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
592 	port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
593 	port_attr->PortMaxFrameSize = 0;
594 	port_attr->NumberofDiscoveredPorts = 0;
595 	rw_exit(&irp->irp_lock);
596 	if (!remote_port) {
597 		rw_exit(&iport->iport_lock);
598 	}
599 	return (0);
600 }
601 
602 int
603 fct_get_port_attr(uint8_t *port_wwn,
604     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *error_detail)
605 {
606 	fct_i_local_port_t *iport;
607 	fct_i_remote_port_t *irp;
608 	int i, ret;
609 
610 	iport = fct_get_iport_per_wwn(port_wwn);
611 	if (iport) {
612 		return (fct_get_adapter_port_attr(iport, port_wwn,
613 		    port_attr, error_detail));
614 	}
615 	/* else */
616 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
617 		rw_enter(&iport->iport_lock, RW_READER);
618 		for (i = 0; i < rportid_table_size; i++) {
619 			irp = iport->iport_rp_tb[i];
620 			while (irp) {
621 				if (bcmp(irp->irp_rp->rp_pwwn,
622 				    port_wwn, 8) == 0 &&
623 				    irp->irp_flags & IRP_PLOGI_DONE) {
624 					ret = fct_get_discovered_port_attr(
625 					    irp, NULL, 0, port_attr,
626 					    error_detail);
627 					rw_exit(&iport->iport_lock);
628 					return (ret);
629 				}
630 				irp = irp->irp_next;
631 			}
632 		}
633 		rw_exit(&iport->iport_lock);
634 	}
635 	*error_detail = FCTIO_BADWWN;
636 	return (ENXIO);
637 }
638 
639 /* ARGSUSED */
640 int
641 fct_get_port_stats(uint8_t *port_wwn,
642     fc_tgt_hba_adapter_port_stats_t *port_stats, uint32_t *error_detail)
643 {
644 	int ret;
645 	fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
646 	fct_port_link_status_t	stat;
647 	uint32_t buf_size = sizeof (fc_tgt_hba_adapter_port_stats_t);
648 
649 	if (!iport)
650 		return (ENXIO);
651 	port_stats->version = FCT_HBA_ADAPTER_PORT_STATS_VERSION;
652 
653 	if (iport->iport_port->port_info == NULL) {
654 		*error_detail = FCTIO_FAILURE;
655 		return (EIO);
656 	}
657 	ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
658 	    iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
659 	if (ret != STMF_SUCCESS) {
660 		*error_detail = FCTIO_FAILURE;
661 		return (EIO);
662 	}
663 
664 	port_stats->SecondsSinceLastReset = 0;
665 	port_stats->TxFrames = 0;
666 	port_stats->TxWords = 0;
667 	port_stats->RxFrames = 0;
668 	port_stats->RxWords = 0;
669 	port_stats->LIPCount = 0;
670 	port_stats->NOSCount = 0;
671 	port_stats->ErrorFrames = 0;
672 	port_stats->DumpedFrames = 0;
673 	port_stats->LinkFailureCount = stat.LinkFailureCount;
674 	port_stats->LossOfSyncCount = stat.LossOfSyncCount;
675 	port_stats->LossOfSignalCount = stat.LossOfSignalsCount;
676 	port_stats->PrimitiveSeqProtocolErrCount =
677 	    stat.PrimitiveSeqProtocolErrorCount;
678 	port_stats->InvalidTxWordCount =
679 	    stat.InvalidTransmissionWordCount;
680 	port_stats->InvalidCRCCount = stat.InvalidCRCCount;
681 
682 	return (ret);
683 }
684 
685 int
686 fct_get_link_status(uint8_t *port_wwn, uint64_t *dest_id,
687     fct_port_link_status_t *link_status, uint32_t *error_detail)
688 {
689 	fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
690 	fct_i_remote_port_t *irp = NULL;
691 	uint32_t buf_size = sizeof (fct_port_link_status_t);
692 	stmf_status_t ret = 0;
693 	int i;
694 	fct_cmd_t *cmd = NULL;
695 
696 	if (!iport) {
697 		*error_detail = FCTIO_BADWWN;
698 		return (ENXIO);
699 	}
700 
701 	/*
702 	 * If what we are requesting is zero or same as local port,
703 	 * then we use port_info()
704 	 */
705 	if (dest_id == NULL || *dest_id == iport->iport_link_info.portid) {
706 		if (iport->iport_port->port_info == NULL) {
707 			*error_detail = FCTIO_FAILURE;
708 			return (EIO);
709 		}
710 		ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
711 		    iport->iport_port, NULL,
712 		    (uint8_t *)link_status, &buf_size);
713 		if (ret == STMF_SUCCESS) {
714 			return (0);
715 		} else {
716 			*error_detail = FCTIO_FAILURE;
717 			return (EIO);
718 		}
719 	}
720 
721 	/*
722 	 * For remote port, we will send RLS
723 	 */
724 	for (i = 0; i < rportid_table_size; i++) {
725 		irp = iport->iport_rp_tb[i];
726 		while (irp) {
727 			if (irp->irp_rp->rp_id == *dest_id &&
728 			    irp->irp_flags & IRP_PLOGI_DONE) {
729 				goto SEND_RLS_ELS;
730 			}
731 			irp = irp->irp_next;
732 		}
733 	}
734 	return (ENXIO);
735 
736 SEND_RLS_ELS:
737 	cmd = fct_create_solels(iport->iport_port,
738 	    irp->irp_rp, 0, ELS_OP_RLS,
739 	    0, fct_rls_cb);
740 	if (!cmd)
741 		return (ENOMEM);
742 	iport->iport_rls_cb_data.fct_link_status = link_status;
743 	CMD_TO_ICMD(cmd)->icmd_cb_private = &iport->iport_rls_cb_data;
744 	fct_post_to_solcmd_queue(iport->iport_port, cmd);
745 	sema_p(&iport->iport_rls_sema);
746 	if (iport->iport_rls_cb_data.fct_els_res != FCT_SUCCESS)
747 		ret = EIO;
748 	return (ret);
749 }
750 
751 static int
752 fct_forcelip(uint8_t *port_wwn, uint32_t *fctio_errno)
753 {
754 	fct_status_t		 rval;
755 	fct_i_local_port_t	*iport;
756 
757 	mutex_enter(&fct_global_mutex);
758 	iport = fct_get_iport_per_wwn(port_wwn);
759 	mutex_exit(&fct_global_mutex);
760 	if (iport == NULL) {
761 		return (-1);
762 	}
763 
764 	iport->iport_port->port_ctl(iport->iport_port,
765 	    FCT_CMD_FORCE_LIP, &rval);
766 	if (rval != FCT_SUCCESS) {
767 		*fctio_errno = FCTIO_FAILURE;
768 	} else {
769 		*fctio_errno = 0;
770 	}
771 
772 	return (0);
773 }
774 
775 static int
776 fct_fctiocmd(intptr_t data, int mode)
777 {
778 	int ret	 = 0;
779 	void		*ibuf = NULL;
780 	void		*obuf = NULL;
781 	void		*abuf = NULL;
782 	fctio_t		*fctio;
783 	uint32_t	attr_length;
784 
785 	ret = fct_copyin_iocdata(data, mode, &fctio, &ibuf, &abuf, &obuf);
786 	if (ret) {
787 		return (ret);
788 	}
789 
790 	switch (fctio->fctio_cmd) {
791 	case FCTIO_ADAPTER_LIST: {
792 		fc_tgt_hba_list_t *list = (fc_tgt_hba_list_t *)obuf;
793 		int		count;
794 
795 		if (fctio->fctio_olen < sizeof (fc_tgt_hba_list_t)) {
796 			ret = EINVAL;
797 			break;
798 		}
799 		list->numPorts = (fctio->fctio_olen -
800 		    sizeof (fc_tgt_hba_list_t))/8 + 1;
801 
802 		list->version = FCT_HBA_LIST_VERSION;
803 		count = fct_get_port_list((char *)list->port_wwn,
804 		    list->numPorts);
805 		if (count < 0) {
806 			ret = ENXIO;
807 			break;
808 		}
809 		if (count > list->numPorts) {
810 			fctio->fctio_errno = FCTIO_MOREDATA;
811 			ret = ENOSPC;
812 		}
813 		list->numPorts = count;
814 		break;
815 		}
816 	case FCTIO_GET_ADAPTER_ATTRIBUTES: {
817 		fc_tgt_hba_adapter_attributes_t *hba_attr;
818 		uint8_t	*port_wwn = (uint8_t *)ibuf;
819 
820 		attr_length = sizeof (fc_tgt_hba_adapter_attributes_t);
821 		if (fctio->fctio_olen < attr_length ||
822 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
823 			ret = EINVAL;
824 			break;
825 		}
826 		hba_attr = (fc_tgt_hba_adapter_attributes_t *)obuf;
827 
828 		mutex_enter(&fct_global_mutex);
829 		ret = fct_get_adapter_attr(port_wwn, hba_attr,
830 		    &fctio->fctio_errno);
831 		mutex_exit(&fct_global_mutex);
832 
833 		break;
834 		}
835 	case FCTIO_GET_ADAPTER_PORT_ATTRIBUTES: {
836 		fc_tgt_hba_port_attributes_t *port_attr;
837 
838 		uint8_t *port_wwn = (uint8_t *)ibuf;
839 
840 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
841 		if (fctio->fctio_olen < attr_length ||
842 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
843 			ret = EINVAL;
844 			break;
845 		}
846 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
847 
848 		mutex_enter(&fct_global_mutex);
849 		ret = fct_get_adapter_port_attr(NULL, port_wwn, port_attr,
850 		    &fctio->fctio_errno);
851 		mutex_exit(&fct_global_mutex);
852 
853 		break;
854 		}
855 	case FCTIO_GET_DISCOVERED_PORT_ATTRIBUTES: {
856 		uint8_t *port_wwn = (uint8_t *)ibuf;
857 		uint32_t *port_index = (uint32_t *)abuf;
858 		fc_tgt_hba_port_attributes_t *port_attr;
859 
860 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
861 		if (fctio->fctio_olen < attr_length ||
862 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
863 			ret = EINVAL;
864 			break;
865 		}
866 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
867 
868 		mutex_enter(&fct_global_mutex);
869 		ret = fct_get_discovered_port_attr(NULL, port_wwn,
870 		    *port_index, port_attr, &fctio->fctio_errno);
871 		mutex_exit(&fct_global_mutex);
872 
873 		break;
874 		}
875 	case FCTIO_GET_PORT_ATTRIBUTES: {
876 		uint8_t *port_wwn = (uint8_t *)ibuf;
877 		fc_tgt_hba_port_attributes_t *port_attr;
878 
879 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
880 		if (fctio->fctio_olen < attr_length ||
881 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
882 			ret = EINVAL;
883 			break;
884 		}
885 
886 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
887 
888 		mutex_enter(&fct_global_mutex);
889 		ret = fct_get_port_attr(port_wwn, port_attr,
890 		    &fctio->fctio_errno);
891 		mutex_exit(&fct_global_mutex);
892 
893 		break;
894 		}
895 	case FCTIO_GET_ADAPTER_PORT_STATS: {
896 		uint8_t *port_wwn = (uint8_t *)ibuf;
897 		fc_tgt_hba_adapter_port_stats_t *port_stats =
898 		    (fc_tgt_hba_adapter_port_stats_t *)obuf;
899 		mutex_enter(&fct_global_mutex);
900 		ret = fct_get_port_stats(port_wwn, port_stats,
901 		    &fctio->fctio_errno);
902 		mutex_exit(&fct_global_mutex);
903 		break;
904 		}
905 	case FCTIO_GET_LINK_STATUS: {
906 		uint8_t *port_wwn = (uint8_t *)ibuf;
907 		fct_port_link_status_t *link_status =
908 		    (fct_port_link_status_t *)obuf;
909 		uint64_t *dest_id = abuf;
910 
911 		mutex_enter(&fct_global_mutex);
912 		ret = fct_get_link_status(port_wwn, dest_id, link_status,
913 		    &fctio->fctio_errno);
914 		mutex_exit(&fct_global_mutex);
915 		break;
916 		}
917 
918 	case FCTIO_FORCE_LIP:
919 		ret = fct_forcelip((uint8_t *)ibuf, &fctio->fctio_errno);
920 		break;
921 
922 	default:
923 		break;
924 	}
925 	if (ret == 0) {
926 		ret = fct_copyout_iocdata(data, mode, fctio, obuf);
927 	} else if (fctio->fctio_errno) {
928 		(void) fct_copyout_iocdata(data, mode, fctio, obuf);
929 	}
930 
931 	if (obuf) {
932 		kmem_free(obuf, fctio->fctio_olen);
933 		obuf = NULL;
934 	}
935 	if (abuf) {
936 		kmem_free(abuf, fctio->fctio_alen);
937 		abuf = NULL;
938 	}
939 
940 	if (ibuf) {
941 		kmem_free(ibuf, fctio->fctio_ilen);
942 		ibuf = NULL;
943 	}
944 	kmem_free(fctio, sizeof (fctio_t));
945 	return (ret);
946 }
947 
948 typedef struct {
949 	void	*bp;	/* back pointer from internal struct to main struct */
950 	int	alloc_size;
951 	fct_struct_id_t struct_id;
952 } __ifct_t;
953 
954 typedef struct {
955 	__ifct_t	*fp;	/* Framework private */
956 	void		*cp;	/* Caller private */
957 	void		*ss;	/* struct specific */
958 } __fct_t;
959 
960 static struct {
961 	int shared;
962 	int fw_private;
963 	int struct_specific;
964 } fct_sizes[] = { { 0, 0, 0 },
965 	{ GET_STRUCT_SIZE(fct_local_port_t),
966 		GET_STRUCT_SIZE(fct_i_local_port_t), 0 },
967 	{ GET_STRUCT_SIZE(fct_remote_port_t),
968 		GET_STRUCT_SIZE(fct_i_remote_port_t), 0 },
969 	{ GET_STRUCT_SIZE(fct_cmd_t),
970 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
971 	{ GET_STRUCT_SIZE(fct_cmd_t),
972 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
973 	{ GET_STRUCT_SIZE(fct_cmd_t),
974 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_sol_ct_t) },
975 	{ GET_STRUCT_SIZE(fct_cmd_t), GET_STRUCT_SIZE(fct_i_cmd_t),
976 		GET_STRUCT_SIZE(fct_rcvd_abts_t) },
977 	{ GET_STRUCT_SIZE(fct_cmd_t),	/* FCT_STRUCT_CMD_FCP_XCHG */
978 		GET_STRUCT_SIZE(fct_i_cmd_t), 0 },
979 	{ GET_STRUCT_SIZE(fct_dbuf_store_t),
980 		GET_STRUCT_SIZE(__ifct_t), 0 }
981 };
982 
983 void *
984 fct_alloc(fct_struct_id_t struct_id, int additional_size, int flags)
985 {
986 	int fct_size;
987 	int kmem_flag;
988 	__fct_t *sh;
989 
990 	if ((struct_id == 0) || (struct_id >= FCT_MAX_STRUCT_IDS))
991 		return (NULL);
992 
993 	if ((curthread->t_flag & T_INTR_THREAD) || (flags & AF_FORCE_NOSLEEP)) {
994 		kmem_flag = KM_NOSLEEP;
995 	} else {
996 		kmem_flag = KM_SLEEP;
997 	}
998 
999 	additional_size = (additional_size + 7) & (~7);
1000 	fct_size = fct_sizes[struct_id].shared +
1001 	    fct_sizes[struct_id].fw_private +
1002 	    fct_sizes[struct_id].struct_specific + additional_size;
1003 
1004 	if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1005 		stmf_local_port_t *lport;
1006 
1007 		lport = (stmf_local_port_t *)stmf_alloc(
1008 		    STMF_STRUCT_STMF_LOCAL_PORT, fct_size, flags);
1009 		if (lport) {
1010 			sh = (__fct_t *)lport->lport_port_private;
1011 			sh->ss = lport;
1012 		} else {
1013 			return (NULL);
1014 		}
1015 	} else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1016 		stmf_dbuf_store_t *ds;
1017 
1018 		ds = (stmf_dbuf_store_t *)stmf_alloc(STMF_STRUCT_DBUF_STORE,
1019 		    fct_size, flags);
1020 		if (ds) {
1021 			sh = (__fct_t *)ds->ds_port_private;
1022 			sh->ss = ds;
1023 		} else {
1024 			return (NULL);
1025 		}
1026 	} else {
1027 		sh = (__fct_t *)kmem_zalloc(fct_size, kmem_flag);
1028 	}
1029 
1030 	if (sh == NULL)
1031 		return (NULL);
1032 
1033 	sh->fp = (__ifct_t *)GET_BYTE_OFFSET(sh, fct_sizes[struct_id].shared);
1034 	sh->cp = GET_BYTE_OFFSET(sh->fp, fct_sizes[struct_id].fw_private);
1035 	if (fct_sizes[struct_id].struct_specific)
1036 		sh->ss = GET_BYTE_OFFSET(sh->cp, additional_size);
1037 
1038 	sh->fp->bp = sh;
1039 	sh->fp->alloc_size = fct_size;
1040 	sh->fp->struct_id = struct_id;
1041 
1042 	if (struct_id == FCT_STRUCT_CMD_FCP_XCHG) {
1043 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_FCP_XCHG;
1044 	} else if (struct_id == FCT_STRUCT_CMD_RCVD_ELS) {
1045 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ELS;
1046 	} else if (struct_id == FCT_STRUCT_CMD_SOL_ELS) {
1047 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_ELS;
1048 	} else if (struct_id == FCT_STRUCT_CMD_RCVD_ABTS) {
1049 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ABTS;
1050 	} else if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1051 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_CT;
1052 	}
1053 
1054 	return (sh);
1055 }
1056 
1057 void
1058 fct_free(void *ptr)
1059 {
1060 	__fct_t *sh = (__fct_t *)ptr;
1061 	fct_struct_id_t struct_id = sh->fp->struct_id;
1062 
1063 	if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
1064 		fct_sol_ct_t *ct = (fct_sol_ct_t *)
1065 		    ((fct_cmd_t *)ptr)->cmd_specific;
1066 
1067 		if (ct->ct_req_alloc_size) {
1068 			kmem_free(ct->ct_req_payload, ct->ct_req_alloc_size);
1069 		}
1070 		if (ct->ct_resp_alloc_size) {
1071 			kmem_free(ct->ct_resp_payload, ct->ct_resp_alloc_size);
1072 		}
1073 	} else if ((struct_id == FCT_STRUCT_CMD_RCVD_ELS) ||
1074 	    (struct_id == FCT_STRUCT_CMD_SOL_ELS)) {
1075 		fct_els_t *els = (fct_els_t *)
1076 			((fct_cmd_t *)ptr)->cmd_specific;
1077 		if (els->els_req_alloc_size)
1078 			kmem_free(els->els_req_payload,
1079 				els->els_req_alloc_size);
1080 		if (els->els_resp_alloc_size)
1081 			kmem_free(els->els_resp_payload,
1082 				els->els_resp_alloc_size);
1083 	}
1084 
1085 	if (struct_id == FCT_STRUCT_LOCAL_PORT) {
1086 		stmf_free(((fct_local_port_t *)ptr)->port_lport);
1087 	} else if (struct_id == FCT_STRUCT_DBUF_STORE) {
1088 		stmf_free(((fct_dbuf_store_t *)ptr)->fds_ds);
1089 	} else {
1090 		kmem_free(ptr, sh->fp->alloc_size);
1091 	}
1092 }
1093 
1094 stmf_data_buf_t *
1095 fct_alloc_dbuf(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
1096     uint32_t flags)
1097 {
1098 	fct_local_port_t *port = (fct_local_port_t *)
1099 	    task->task_lport->lport_port_private;
1100 
1101 	return (port->port_fds->fds_alloc_data_buf(port, size,
1102 	    pminsize, flags));
1103 }
1104 
1105 stmf_status_t
1106 fct_setup_dbuf(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t flags)
1107 {
1108 	fct_local_port_t *port = (fct_local_port_t *)
1109 	    task->task_lport->lport_port_private;
1110 
1111 	ASSERT(port->port_fds->fds_setup_dbuf != NULL);
1112 	if (port->port_fds->fds_setup_dbuf == NULL)
1113 		return (STMF_FAILURE);
1114 
1115 	return (port->port_fds->fds_setup_dbuf(port, dbuf, flags));
1116 }
1117 
1118 void
1119 fct_teardown_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1120 {
1121 	fct_dbuf_store_t *fds = ds->ds_port_private;
1122 
1123 	fds->fds_teardown_dbuf(fds, dbuf);
1124 }
1125 
1126 void
1127 fct_free_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
1128 {
1129 	fct_dbuf_store_t *fds;
1130 
1131 	fds = (fct_dbuf_store_t *)ds->ds_port_private;
1132 
1133 	fds->fds_free_data_buf(fds, dbuf);
1134 }
1135 
1136 static uint32_t taskq_cntr = 0;
1137 
1138 fct_status_t
1139 fct_register_local_port(fct_local_port_t *port)
1140 {
1141 	fct_i_local_port_t	*iport;
1142 	stmf_local_port_t	*lport;
1143 	fct_cmd_slot_t		*slot;
1144 	int			i;
1145 	char			taskq_name[FCT_TASKQ_NAME_LEN];
1146 
1147 	iport = (fct_i_local_port_t *)port->port_fct_private;
1148 	if (port->port_fca_version != FCT_FCA_MODREV_1) {
1149 		cmn_err(CE_WARN,
1150 		    "fct: %s driver version mismatch",
1151 		    port->port_default_alias);
1152 		return (FCT_FAILURE);
1153 	}
1154 	if (port->port_default_alias) {
1155 		int l = strlen(port->port_default_alias);
1156 
1157 		if (l < 16) {
1158 			iport->iport_alias = iport->iport_alias_mem;
1159 		} else {
1160 			iport->iport_alias =
1161 			    (char *)kmem_zalloc(l+1, KM_SLEEP);
1162 		}
1163 		(void) strcpy(iport->iport_alias, port->port_default_alias);
1164 	} else {
1165 		iport->iport_alias = NULL;
1166 	}
1167 	stmf_wwn_to_devid_desc((scsi_devid_desc_t *)iport->iport_id,
1168 	    port->port_pwwn, PROTOCOL_FIBRE_CHANNEL);
1169 	(void) snprintf(taskq_name, sizeof (taskq_name), "stmf_fct_taskq_%d",
1170 	    atomic_inc_32_nv(&taskq_cntr));
1171 	if ((iport->iport_worker_taskq = ddi_taskq_create(NULL,
1172 	    taskq_name, 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
1173 		return (FCT_FAILURE);
1174 	}
1175 	mutex_init(&iport->iport_worker_lock, NULL, MUTEX_DRIVER, NULL);
1176 	cv_init(&iport->iport_worker_cv, NULL, CV_DRIVER, NULL);
1177 	rw_init(&iport->iport_lock, NULL, RW_DRIVER, NULL);
1178 	sema_init(&iport->iport_rls_sema, 0, NULL, SEMA_DRIVER, NULL);
1179 
1180 	/* Remote port mgmt */
1181 	iport->iport_rp_slots = (fct_i_remote_port_t **)kmem_zalloc(
1182 	    port->port_max_logins * sizeof (fct_i_remote_port_t *), KM_SLEEP);
1183 	iport->iport_rp_tb = kmem_zalloc(rportid_table_size *
1184 	    sizeof (fct_i_remote_port_t *), KM_SLEEP);
1185 
1186 	/* fct_cmds for SCSI traffic */
1187 	iport->iport_total_alloced_ncmds = 0;
1188 	iport->iport_cached_ncmds = 0;
1189 	port->port_fca_fcp_cmd_size =
1190 	    (port->port_fca_fcp_cmd_size + 7) & ~7;
1191 	iport->iport_cached_cmdlist = NULL;
1192 	mutex_init(&iport->iport_cached_cmd_lock, NULL, MUTEX_DRIVER, NULL);
1193 
1194 	/* Initialize cmd slots */
1195 	iport->iport_cmd_slots = (fct_cmd_slot_t *)kmem_zalloc(
1196 	    port->port_max_xchges * sizeof (fct_cmd_slot_t), KM_SLEEP);
1197 	iport->iport_next_free_slot = 0;
1198 	for (i = 0; i < port->port_max_xchges; ) {
1199 		slot = &iport->iport_cmd_slots[i];
1200 		slot->slot_no = (uint16_t)i;
1201 		slot->slot_next = (uint16_t)(++i);
1202 	}
1203 	slot->slot_next = FCT_SLOT_EOL;
1204 	iport->iport_nslots_free = port->port_max_xchges;
1205 
1206 	iport->iport_task_green_limit =
1207 	    (port->port_max_xchges * FCT_TASK_GREEN_LIMIT) / 100;
1208 	iport->iport_task_yellow_limit =
1209 	    (port->port_max_xchges * FCT_TASK_YELLOW_LIMIT) / 100;
1210 	iport->iport_task_red_limit =
1211 	    (port->port_max_xchges * FCT_TASK_RED_LIMIT) / 100;
1212 
1213 	/* Start worker thread */
1214 	atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1215 	(void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1216 	    fct_port_worker, port, DDI_SLEEP);
1217 	/* Wait for taskq to start */
1218 	while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1219 		delay(1);
1220 	}
1221 
1222 	lport = port->port_lport;
1223 	lport->lport_id = (scsi_devid_desc_t *)iport->iport_id;
1224 	lport->lport_alias = iport->iport_alias;
1225 	lport->lport_pp = port->port_pp;
1226 	port->port_fds->fds_ds->ds_alloc_data_buf = fct_alloc_dbuf;
1227 	port->port_fds->fds_ds->ds_free_data_buf = fct_free_dbuf;
1228 	port->port_fds->fds_ds->ds_setup_dbuf = fct_setup_dbuf;
1229 	port->port_fds->fds_ds->ds_teardown_dbuf = fct_teardown_dbuf;
1230 	lport->lport_ds = port->port_fds->fds_ds;
1231 	lport->lport_xfer_data = fct_xfer_scsi_data;
1232 	lport->lport_send_status = fct_send_scsi_status;
1233 	lport->lport_task_free = fct_scsi_task_free;
1234 	lport->lport_abort = fct_scsi_abort;
1235 	lport->lport_ctl = fct_ctl;
1236 	lport->lport_info = fct_info;
1237 	lport->lport_event_handler = fct_event_handler;
1238 	/* set up as alua participating port */
1239 	stmf_set_port_alua(lport);
1240 	if (stmf_register_local_port(port->port_lport) != FCT_SUCCESS) {
1241 		goto fct_regport_fail1;
1242 	}
1243 	(void) stmf_lport_add_event(lport, LPORT_EVENT_INITIAL_LUN_MAPPED);
1244 
1245 	mutex_enter(&fct_global_mutex);
1246 	iport->iport_next = fct_iport_list;
1247 	iport->iport_prev = NULL;
1248 	if (iport->iport_next)
1249 		iport->iport_next->iport_prev = iport;
1250 	fct_iport_list = iport;
1251 	mutex_exit(&fct_global_mutex);
1252 
1253 	fct_init_kstats(iport);
1254 
1255 	fct_log_local_port_event(port, ESC_SUNFC_PORT_ATTACH);
1256 
1257 	return (FCT_SUCCESS);
1258 
1259 fct_regport_fail1:;
1260 	/* Stop the taskq 1st */
1261 	if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1262 		atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1263 		cv_broadcast(&iport->iport_worker_cv);
1264 		while (iport->iport_flags & IPORT_WORKER_RUNNING) {
1265 			delay(1);
1266 		}
1267 	}
1268 	ddi_taskq_destroy(iport->iport_worker_taskq);
1269 	if (iport->iport_rp_tb) {
1270 		kmem_free(iport->iport_rp_tb, rportid_table_size *
1271 		    sizeof (fct_i_remote_port_t *));
1272 	}
1273 	return (FCT_FAILURE);
1274 }
1275 
1276 fct_status_t
1277 fct_deregister_local_port(fct_local_port_t *port)
1278 {
1279 	fct_i_local_port_t	*iport;
1280 	fct_i_cmd_t		*icmd, *next_icmd;
1281 	int			ndx;
1282 
1283 	iport = (fct_i_local_port_t *)port->port_fct_private;
1284 
1285 	if ((iport->iport_state != FCT_STATE_OFFLINE) ||
1286 	    iport->iport_state_not_acked) {
1287 		return (FCT_FAILURE);
1288 	}
1289 
1290 	/* Stop the taskq 1st */
1291 	if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1292 		atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1293 		cv_broadcast(&iport->iport_worker_cv);
1294 		for (ndx = 0; ndx < 100; ndx++) {
1295 			if ((iport->iport_flags & IPORT_WORKER_RUNNING)
1296 			    == 0) {
1297 				break;
1298 			}
1299 			delay(drv_usectohz(10000));
1300 		}
1301 		if (ndx == 100) {
1302 			atomic_and_32(&iport->iport_flags,
1303 			    ~IPORT_TERMINATE_WORKER);
1304 			return (FCT_WORKER_STUCK);
1305 		}
1306 	}
1307 
1308 	if (stmf_deregister_local_port(port->port_lport) != FCT_SUCCESS) {
1309 		goto fct_deregport_fail1;
1310 	}
1311 
1312 	mutex_enter(&fct_global_mutex);
1313 	if (iport->iport_next)
1314 		iport->iport_next->iport_prev = iport->iport_prev;
1315 	if (iport->iport_prev)
1316 		iport->iport_prev->iport_next = iport->iport_next;
1317 	else
1318 		fct_iport_list = iport->iport_next;
1319 	mutex_exit(&fct_global_mutex);
1320 	/*
1321 	 * At this time, there should be no outstanding and pending
1322 	 * I/Os, so we can just release resources.
1323 	 */
1324 	ASSERT(iport->iport_total_alloced_ncmds == iport->iport_cached_ncmds);
1325 	for (icmd = iport->iport_cached_cmdlist; icmd; icmd = next_icmd) {
1326 		next_icmd = icmd->icmd_next;
1327 		fct_free(icmd->icmd_cmd);
1328 	}
1329 	mutex_destroy(&iport->iport_cached_cmd_lock);
1330 	kmem_free(iport->iport_cmd_slots, port->port_max_xchges *
1331 	    sizeof (fct_cmd_slot_t));
1332 	kmem_free(iport->iport_rp_slots, port->port_max_logins *
1333 	    sizeof (fct_i_remote_port_t *));
1334 	rw_destroy(&iport->iport_lock);
1335 	cv_destroy(&iport->iport_worker_cv);
1336 	sema_destroy(&iport->iport_rls_sema);
1337 	mutex_destroy(&iport->iport_worker_lock);
1338 	ddi_taskq_destroy(iport->iport_worker_taskq);
1339 	if (iport->iport_rp_tb) {
1340 		kmem_free(iport->iport_rp_tb, rportid_table_size *
1341 		    sizeof (fct_i_remote_port_t *));
1342 	}
1343 
1344 	if (iport->iport_kstat_portstat) {
1345 		kstat_delete(iport->iport_kstat_portstat);
1346 	}
1347 
1348 	fct_log_local_port_event(port, ESC_SUNFC_PORT_DETACH);
1349 	return (FCT_SUCCESS);
1350 
1351 fct_deregport_fail1:;
1352 	/* Restart the worker */
1353 	atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1354 	(void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1355 	    fct_port_worker, port, DDI_SLEEP);
1356 	/* Wait for taskq to start */
1357 	while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1358 		delay(1);
1359 	}
1360 	return (FCT_FAILURE);
1361 }
1362 
1363 /* ARGSUSED */
1364 void
1365 fct_handle_event(fct_local_port_t *port, int event_id, uint32_t event_flags,
1366     caddr_t arg)
1367 {
1368 	char			info[FCT_INFO_LEN];
1369 	fct_i_event_t		*e;
1370 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1371 	    port->port_fct_private;
1372 
1373 	e = kmem_zalloc(sizeof (fct_i_event_t), KM_NOSLEEP);
1374 
1375 	if (e == NULL) {
1376 		/*
1377 		 * XXX Throw HBA fatal error event
1378 		 */
1379 		(void) snprintf(info, sizeof (info),
1380 		    "fct_handle_event: iport-%p, allocation "
1381 		    "of fct_i_event failed", (void *)iport);
1382 		(void) fct_port_shutdown(iport->iport_port,
1383 		    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1384 		return;
1385 	}
1386 	/* Just queue the event */
1387 	e->event_type = event_id;
1388 	mutex_enter(&iport->iport_worker_lock);
1389 	if (iport->iport_event_head == NULL) {
1390 		iport->iport_event_head = iport->iport_event_tail = e;
1391 	} else {
1392 		iport->iport_event_tail->event_next = e;
1393 		iport->iport_event_tail = e;
1394 	}
1395 	if (IS_WORKER_SLEEPING(iport))
1396 		cv_signal(&iport->iport_worker_cv);
1397 	mutex_exit(&iport->iport_worker_lock);
1398 }
1399 
1400 /*
1401  * Called with iport_lock held as reader.
1402  */
1403 fct_i_remote_port_t *
1404 fct_portid_to_portptr(fct_i_local_port_t *iport, uint32_t portid)
1405 {
1406 	fct_i_remote_port_t	*irp;
1407 
1408 	irp = iport->iport_rp_tb[FCT_PORTID_HASH_FUNC(portid)];
1409 	for (; irp != NULL; irp = irp->irp_next) {
1410 		if (irp->irp_portid == portid)
1411 			return (irp);
1412 	}
1413 
1414 	return (NULL);
1415 
1416 }
1417 
1418 /*
1419  * Called with irp_lock held as writer.
1420  */
1421 void
1422 fct_queue_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1423 {
1424 	int hash_key =
1425 	    FCT_PORTID_HASH_FUNC(irp->irp_portid);
1426 
1427 	irp->irp_next = iport->iport_rp_tb[hash_key];
1428 	iport->iport_rp_tb[hash_key] = irp;
1429 	iport->iport_nrps++;
1430 }
1431 
1432 /*
1433  * Called with irp_lock and iport_lock held as writer.
1434  */
1435 void
1436 fct_deque_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1437 {
1438 	fct_i_remote_port_t	*irp_next = NULL;
1439 	fct_i_remote_port_t	*irp_last = NULL;
1440 	int hash_key			  =
1441 	    FCT_PORTID_HASH_FUNC(irp->irp_portid);
1442 
1443 	irp_next = iport->iport_rp_tb[hash_key];
1444 	irp_last = NULL;
1445 	while (irp_next != NULL) {
1446 		if (irp == irp_next) {
1447 			if (irp->irp_flags & IRP_PLOGI_DONE) {
1448 				atomic_dec_32(&iport->iport_nrps_login);
1449 			}
1450 			atomic_and_32(&irp->irp_flags,
1451 			    ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1452 			break;
1453 		}
1454 		irp_last = irp_next;
1455 		irp_next = irp_next->irp_next;
1456 	}
1457 
1458 	if (irp_next) {
1459 		if (irp_last == NULL) {
1460 			iport->iport_rp_tb[hash_key] =
1461 			    irp->irp_next;
1462 		} else {
1463 			irp_last->irp_next = irp->irp_next;
1464 		}
1465 		irp->irp_next = NULL;
1466 		iport->iport_nrps--;
1467 	}
1468 }
1469 
1470 int
1471 fct_is_irp_logging_out(fct_i_remote_port_t *irp, int force_implicit)
1472 {
1473 	int logging_out = 0;
1474 
1475 	rw_enter(&irp->irp_lock, RW_WRITER);
1476 	if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1477 		logging_out = 0;
1478 		goto ilo_done;
1479 	}
1480 	if ((irp->irp_els_list == NULL) && (irp->irp_deregister_timer)) {
1481 		if (force_implicit && irp->irp_nonfcp_xchg_count) {
1482 			logging_out = 0;
1483 		} else {
1484 			logging_out = 1;
1485 		}
1486 		goto ilo_done;
1487 	}
1488 	if (irp->irp_els_list) {
1489 		fct_i_cmd_t *icmd;
1490 		/* Last session affecting ELS should be a LOGO */
1491 		for (icmd = irp->irp_els_list; icmd; icmd = icmd->icmd_next) {
1492 			uint8_t op = (ICMD_TO_ELS(icmd))->els_req_payload[0];
1493 			if (op == ELS_OP_LOGO) {
1494 				if (force_implicit) {
1495 					if (icmd->icmd_flags & ICMD_IMPLICIT)
1496 						logging_out = 1;
1497 					else
1498 						logging_out = 0;
1499 				} else {
1500 					logging_out = 1;
1501 				}
1502 			} else if ((op == ELS_OP_PLOGI) ||
1503 			    (op == ELS_OP_PRLI) ||
1504 			    (op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
1505 				logging_out = 0;
1506 			}
1507 		}
1508 	}
1509 ilo_done:;
1510 	rw_exit(&irp->irp_lock);
1511 
1512 	return (logging_out);
1513 }
1514 
1515 /*
1516  * The force_implicit flag enforces the implicit semantics which may be
1517  * needed if a received logout got stuck e.g. a response to a received
1518  * LOGO never came back from the FCA.
1519  */
1520 int
1521 fct_implicitly_logo_all(fct_i_local_port_t *iport, int force_implicit)
1522 {
1523 	fct_i_remote_port_t	*irp = NULL;
1524 	fct_cmd_t		*cmd = NULL;
1525 	int			 i   = 0;
1526 	int			nports = 0;
1527 
1528 	if (!iport->iport_nrps) {
1529 		return (nports);
1530 	}
1531 
1532 	rw_enter(&iport->iport_lock, RW_WRITER);
1533 	for (i = 0; i < rportid_table_size; i++) {
1534 		irp = iport->iport_rp_tb[i];
1535 		while (irp) {
1536 			if ((!(irp->irp_flags & IRP_PLOGI_DONE)) &&
1537 			    (fct_is_irp_logging_out(irp, force_implicit))) {
1538 				irp = irp->irp_next;
1539 				continue;
1540 			}
1541 
1542 			cmd = fct_create_solels(iport->iport_port, irp->irp_rp,
1543 			    1, ELS_OP_LOGO, 0, fct_logo_cb);
1544 			if (cmd == NULL) {
1545 				stmf_trace(iport->iport_alias,
1546 				    "fct_implictly_logo_all: cmd null");
1547 				rw_exit(&iport->iport_lock);
1548 
1549 				return (nports);
1550 			}
1551 
1552 			fct_post_implicit_logo(cmd);
1553 			nports++;
1554 			irp = irp->irp_next;
1555 		}
1556 	}
1557 	rw_exit(&iport->iport_lock);
1558 
1559 	return (nports);
1560 }
1561 
1562 void
1563 fct_rehash(fct_i_local_port_t *iport)
1564 {
1565 	fct_i_remote_port_t **iport_rp_tb_tmp;
1566 	fct_i_remote_port_t **iport_rp_tb_new;
1567 	fct_i_remote_port_t *irp;
1568 	fct_i_remote_port_t *irp_next;
1569 	int i;
1570 
1571 	iport_rp_tb_new = kmem_zalloc(rportid_table_size *
1572 	    sizeof (fct_i_remote_port_t *), KM_SLEEP);
1573 	rw_enter(&iport->iport_lock, RW_WRITER);
1574 	/* reconstruct the hash table */
1575 	iport_rp_tb_tmp = iport->iport_rp_tb;
1576 	iport->iport_rp_tb = iport_rp_tb_new;
1577 	iport->iport_nrps = 0;
1578 	for (i = 0; i < rportid_table_size; i++) {
1579 		irp = iport_rp_tb_tmp[i];
1580 		while (irp) {
1581 			irp_next = irp->irp_next;
1582 			fct_queue_rp(iport, irp);
1583 			irp = irp_next;
1584 		}
1585 	}
1586 	rw_exit(&iport->iport_lock);
1587 	kmem_free(iport_rp_tb_tmp, rportid_table_size *
1588 	    sizeof (fct_i_remote_port_t *));
1589 
1590 }
1591 
1592 uint8_t
1593 fct_local_port_cleanup_done(fct_i_local_port_t *iport)
1594 {
1595 	fct_i_remote_port_t *irp;
1596 	int i;
1597 
1598 	if (iport->iport_nrps_login)
1599 		return (0);
1600 	/* loop all rps to check if the cmd have already been drained */
1601 	for (i = 0; i < rportid_table_size; i++) {
1602 		irp = iport->iport_rp_tb[i];
1603 		while (irp) {
1604 			if (irp->irp_fcp_xchg_count ||
1605 			    irp->irp_nonfcp_xchg_count)
1606 				return (0);
1607 			irp = irp->irp_next;
1608 		}
1609 	}
1610 	return (1);
1611 }
1612 
1613 fct_cmd_t *
1614 fct_scsi_task_alloc(fct_local_port_t *port, uint16_t rp_handle,
1615     uint32_t rportid, uint8_t *lun, uint16_t cdb_length, uint16_t task_ext)
1616 {
1617 	fct_cmd_t *cmd;
1618 	fct_i_cmd_t *icmd;
1619 	fct_i_local_port_t *iport =
1620 	    (fct_i_local_port_t *)port->port_fct_private;
1621 	fct_i_remote_port_t *irp;
1622 	scsi_task_t *task;
1623 	fct_remote_port_t *rp;
1624 	uint16_t cmd_slot;
1625 
1626 	rw_enter(&iport->iport_lock, RW_READER);
1627 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
1628 		rw_exit(&iport->iport_lock);
1629 		stmf_trace(iport->iport_alias, "cmd alloc called while the port"
1630 		    " was offline");
1631 		return (NULL);
1632 	}
1633 
1634 	if (rp_handle == FCT_HANDLE_NONE) {
1635 		irp = fct_portid_to_portptr(iport, rportid);
1636 		if (irp == NULL) {
1637 			rw_exit(&iport->iport_lock);
1638 			stmf_trace(iport->iport_alias, "cmd received from "
1639 			    "non existent port %x", rportid);
1640 			return (NULL);
1641 		}
1642 	} else {
1643 		if ((rp_handle >= port->port_max_logins) ||
1644 		    ((irp = iport->iport_rp_slots[rp_handle]) == NULL)) {
1645 			rw_exit(&iport->iport_lock);
1646 			stmf_trace(iport->iport_alias, "cmd received from "
1647 			    "invalid port handle %x", rp_handle);
1648 			return (NULL);
1649 		}
1650 	}
1651 	rp = irp->irp_rp;
1652 
1653 	rw_enter(&irp->irp_lock, RW_READER);
1654 	if ((irp->irp_flags & IRP_PRLI_DONE) == 0) {
1655 		rw_exit(&irp->irp_lock);
1656 		rw_exit(&iport->iport_lock);
1657 		stmf_trace(iport->iport_alias, "cmd alloc called while fcp "
1658 		    "login was not done. portid=%x, rp=%p", rp->rp_id, rp);
1659 		return (NULL);
1660 	}
1661 
1662 	mutex_enter(&iport->iport_cached_cmd_lock);
1663 	if ((icmd = iport->iport_cached_cmdlist) != NULL) {
1664 		iport->iport_cached_cmdlist = icmd->icmd_next;
1665 		iport->iport_cached_ncmds--;
1666 		cmd = icmd->icmd_cmd;
1667 	} else {
1668 		icmd = NULL;
1669 	}
1670 	mutex_exit(&iport->iport_cached_cmd_lock);
1671 	if (icmd == NULL) {
1672 		cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_FCP_XCHG,
1673 		    port->port_fca_fcp_cmd_size, 0);
1674 		if (cmd == NULL) {
1675 			rw_exit(&irp->irp_lock);
1676 			rw_exit(&iport->iport_lock);
1677 			stmf_trace(iport->iport_alias, "Ran out of "
1678 			    "memory, port=%p", port);
1679 			return (NULL);
1680 		}
1681 
1682 		icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1683 		icmd->icmd_next = NULL;
1684 		cmd->cmd_port = port;
1685 		atomic_inc_32(&iport->iport_total_alloced_ncmds);
1686 	}
1687 
1688 	/*
1689 	 * The accuracy of iport_max_active_ncmds is not important
1690 	 */
1691 	if ((iport->iport_total_alloced_ncmds - iport->iport_cached_ncmds) >
1692 	    iport->iport_max_active_ncmds) {
1693 		iport->iport_max_active_ncmds =
1694 		    iport->iport_total_alloced_ncmds -
1695 		    iport->iport_cached_ncmds;
1696 	}
1697 
1698 	/* Lets get a slot */
1699 	cmd_slot = fct_alloc_cmd_slot(iport, cmd);
1700 	if (cmd_slot == FCT_SLOT_EOL) {
1701 		rw_exit(&irp->irp_lock);
1702 		rw_exit(&iport->iport_lock);
1703 		stmf_trace(iport->iport_alias, "Ran out of xchg resources");
1704 		cmd->cmd_handle = 0;
1705 		fct_cmd_free(cmd);
1706 		return (NULL);
1707 	}
1708 	atomic_inc_16(&irp->irp_fcp_xchg_count);
1709 	cmd->cmd_rp = rp;
1710 	icmd->icmd_flags |= ICMD_IN_TRANSITION | ICMD_KNOWN_TO_FCA;
1711 	rw_exit(&irp->irp_lock);
1712 	rw_exit(&iport->iport_lock);
1713 
1714 	icmd->icmd_start_time = ddi_get_lbolt();
1715 
1716 	cmd->cmd_specific = stmf_task_alloc(port->port_lport, irp->irp_session,
1717 	    lun, cdb_length, task_ext);
1718 	if ((task = (scsi_task_t *)cmd->cmd_specific) != NULL) {
1719 		task->task_port_private = cmd;
1720 		return (cmd);
1721 	}
1722 
1723 	fct_cmd_free(cmd);
1724 
1725 	return (NULL);
1726 }
1727 
1728 void
1729 fct_scsi_task_free(scsi_task_t *task)
1730 {
1731 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1732 
1733 	cmd->cmd_comp_status = task->task_completion_status;
1734 	fct_cmd_free(cmd);
1735 }
1736 
1737 void
1738 fct_post_rcvd_cmd(fct_cmd_t *cmd, stmf_data_buf_t *dbuf)
1739 {
1740 	fct_dbuf_store_t *fds;
1741 
1742 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1743 		fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1744 		fct_i_local_port_t *iport =
1745 		    (fct_i_local_port_t *)cmd->cmd_port->port_fct_private;
1746 		fct_i_remote_port_t *irp =
1747 		    (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private;
1748 		scsi_task_t *task = (scsi_task_t *)cmd->cmd_specific;
1749 
1750 		uint16_t irp_task = irp->irp_fcp_xchg_count;
1751 		uint32_t load = iport->iport_total_alloced_ncmds -
1752 		    iport->iport_cached_ncmds;
1753 
1754 		DTRACE_FC_4(scsi__command,
1755 		    fct_cmd_t, cmd,
1756 		    fct_i_local_port_t, iport,
1757 		    scsi_task_t, task,
1758 		    fct_i_remote_port_t, irp);
1759 
1760 		if (load >= iport->iport_task_green_limit) {
1761 			if ((load < iport->iport_task_yellow_limit &&
1762 			    irp_task >= 4) ||
1763 			    (load >= iport->iport_task_yellow_limit &&
1764 			    load < iport->iport_task_red_limit &&
1765 			    irp_task >= 1) ||
1766 			    (load >= iport->iport_task_red_limit))
1767 				task->task_additional_flags |=
1768 				    TASK_AF_PORT_LOAD_HIGH;
1769 		}
1770 		/*
1771 		 * If the target driver accepts sglists, fill in task fields.
1772 		 */
1773 		fds = cmd->cmd_port->port_fds;
1774 		if (fds->fds_setup_dbuf != NULL) {
1775 			task->task_additional_flags |= TASK_AF_ACCEPT_LU_DBUF;
1776 			task->task_copy_threshold = fds->fds_copy_threshold;
1777 			task->task_max_xfer_len = fds->fds_max_sgl_xfer_len;
1778 			/*
1779 			 * A single stream load encounters a little extra
1780 			 * latency if large xfers are done in 1 chunk.
1781 			 * Give a hint to the LU that starting the xfer
1782 			 * with a smaller chunk would be better in this case.
1783 			 * For any other load, use maximum chunk size.
1784 			 */
1785 			if (load == 1) {
1786 				/* estimate */
1787 				task->task_1st_xfer_len = 128*1024;
1788 			} else {
1789 				/* zero means no hint */
1790 				task->task_1st_xfer_len = 0;
1791 			}
1792 		}
1793 
1794 		stmf_post_task((scsi_task_t *)cmd->cmd_specific, dbuf);
1795 		atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_TRANSITION);
1796 		return;
1797 	}
1798 	/* We dont need dbuf for other cmds */
1799 	if (dbuf) {
1800 		cmd->cmd_port->port_fds->fds_free_data_buf(
1801 		    cmd->cmd_port->port_fds, dbuf);
1802 		dbuf = NULL;
1803 	}
1804 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1805 		fct_handle_els(cmd);
1806 		return;
1807 	}
1808 	if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) {
1809 		fct_handle_rcvd_abts(cmd);
1810 		return;
1811 	}
1812 
1813 	ASSERT(0);
1814 }
1815 
1816 /*
1817  * This function bypasses fct_handle_els()
1818  */
1819 void
1820 fct_post_implicit_logo(fct_cmd_t *cmd)
1821 {
1822 	fct_local_port_t *port = cmd->cmd_port;
1823 	fct_i_local_port_t *iport =
1824 	    (fct_i_local_port_t *)port->port_fct_private;
1825 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1826 	fct_remote_port_t *rp = cmd->cmd_rp;
1827 	fct_i_remote_port_t *irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1828 
1829 	icmd->icmd_start_time = ddi_get_lbolt();
1830 
1831 	rw_enter(&irp->irp_lock, RW_WRITER);
1832 	atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
1833 	atomic_inc_16(&irp->irp_nonfcp_xchg_count);
1834 	atomic_inc_16(&irp->irp_sa_elses_count);
1835 	/*
1836 	 * An implicit LOGO can also be posted to a irp where a PLOGI might
1837 	 * be in process. That PLOGI will reset this flag and decrement the
1838 	 * iport_nrps_login counter.
1839 	 */
1840 	if (irp->irp_flags & IRP_PLOGI_DONE) {
1841 		atomic_dec_32(&iport->iport_nrps_login);
1842 	}
1843 	atomic_and_32(&irp->irp_flags, ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1844 	atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1845 	fct_post_to_discovery_queue(iport, irp, icmd);
1846 	rw_exit(&irp->irp_lock);
1847 }
1848 
1849 /*
1850  * called with iport_lock held, return the slot number
1851  */
1852 uint16_t
1853 fct_alloc_cmd_slot(fct_i_local_port_t *iport, fct_cmd_t *cmd)
1854 {
1855 	uint16_t cmd_slot;
1856 	uint32_t old, new;
1857 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1858 
1859 	do {
1860 		old = iport->iport_next_free_slot;
1861 		cmd_slot = old & 0xFFFF;
1862 		if (cmd_slot == FCT_SLOT_EOL)
1863 			return (cmd_slot);
1864 		/*
1865 		 * We use high order 16 bits as a counter which keeps on
1866 		 * incrementing to avoid ABA issues with atomic lists.
1867 		 */
1868 		new = ((old + (0x10000)) & 0xFFFF0000);
1869 		new |= iport->iport_cmd_slots[cmd_slot].slot_next;
1870 	} while (atomic_cas_32(&iport->iport_next_free_slot, old, new) != old);
1871 
1872 	atomic_dec_16(&iport->iport_nslots_free);
1873 	iport->iport_cmd_slots[cmd_slot].slot_cmd = icmd;
1874 	cmd->cmd_handle = (uint32_t)cmd_slot | 0x80000000 |
1875 	    (((uint32_t)(iport->iport_cmd_slots[cmd_slot].slot_uniq_cntr))
1876 	    << 24);
1877 	return (cmd_slot);
1878 }
1879 
1880 /*
1881  * If icmd is not NULL, irp_lock must be held
1882  */
1883 void
1884 fct_post_to_discovery_queue(fct_i_local_port_t *iport,
1885     fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
1886 {
1887 	fct_i_cmd_t	**p;
1888 
1889 	ASSERT(!MUTEX_HELD(&iport->iport_worker_lock));
1890 	if (icmd) {
1891 		icmd->icmd_next = NULL;
1892 		for (p = &irp->irp_els_list; *p != NULL;
1893 		    p = &((*p)->icmd_next))
1894 			;
1895 
1896 		*p = icmd;
1897 		atomic_or_32(&icmd->icmd_flags, ICMD_IN_IRP_QUEUE);
1898 	}
1899 
1900 	mutex_enter(&iport->iport_worker_lock);
1901 	if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1902 
1903 		/*
1904 		 * CAUTION: do not grab local_port/remote_port locks after
1905 		 * grabbing the worker lock.
1906 		 */
1907 		irp->irp_discovery_next = NULL;
1908 		if (iport->iport_rpwe_tail) {
1909 			iport->iport_rpwe_tail->irp_discovery_next = irp;
1910 			iport->iport_rpwe_tail = irp;
1911 		} else {
1912 			iport->iport_rpwe_head = iport->iport_rpwe_tail = irp;
1913 		}
1914 
1915 		atomic_or_32(&irp->irp_flags, IRP_IN_DISCOVERY_QUEUE);
1916 	}
1917 
1918 	/*
1919 	 * We need always signal the port worker irrespective of the fact that
1920 	 * irp is already in discovery queue or not.
1921 	 */
1922 	if (IS_WORKER_SLEEPING(iport)) {
1923 		cv_signal(&iport->iport_worker_cv);
1924 	}
1925 	mutex_exit(&iport->iport_worker_lock);
1926 }
1927 
1928 stmf_status_t
1929 fct_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t ioflags)
1930 {
1931 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1932 
1933 	DTRACE_FC_5(xfer__start,
1934 	    fct_cmd_t, cmd,
1935 	    fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1936 	    scsi_task_t, task,
1937 	    fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1938 	    stmf_data_buf_t, dbuf);
1939 
1940 	return (cmd->cmd_port->port_xfer_scsi_data(cmd, dbuf, ioflags));
1941 }
1942 
1943 void
1944 fct_scsi_data_xfer_done(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags)
1945 {
1946 	fct_i_cmd_t	*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1947 	uint32_t	old, new;
1948 	uint32_t	iof = 0;
1949 
1950 	DTRACE_FC_5(xfer__done,
1951 	    fct_cmd_t, cmd,
1952 	    fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1953 	    scsi_task_t, ((scsi_task_t *)cmd->cmd_specific),
1954 	    fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1955 	    stmf_data_buf_t, dbuf);
1956 
1957 	if (ioflags & FCT_IOF_FCA_DONE) {
1958 		do {
1959 			old = new = icmd->icmd_flags;
1960 			if (old & ICMD_BEING_ABORTED) {
1961 				return;
1962 			}
1963 			new &= ~ICMD_KNOWN_TO_FCA;
1964 		} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1965 		iof = STMF_IOF_LPORT_DONE;
1966 		cmd->cmd_comp_status = dbuf->db_xfer_status;
1967 	}
1968 
1969 	if (icmd->icmd_flags & ICMD_BEING_ABORTED)
1970 		return;
1971 	stmf_data_xfer_done((scsi_task_t *)cmd->cmd_specific, dbuf, iof);
1972 }
1973 
1974 stmf_status_t
1975 fct_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
1976 {
1977 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1978 
1979 	DTRACE_FC_4(scsi__response,
1980 	    fct_cmd_t, cmd,
1981 	    fct_i_local_port_t,
1982 	    (fct_i_local_port_t *)cmd->cmd_port->port_fct_private,
1983 	    scsi_task_t, task,
1984 	    fct_i_remote_port_t,
1985 	    (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private);
1986 
1987 	return (cmd->cmd_port->port_send_cmd_response(cmd, ioflags));
1988 }
1989 
1990 void
1991 fct_send_response_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
1992 {
1993 	fct_i_cmd_t	*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1994 	fct_local_port_t *port = cmd->cmd_port;
1995 	fct_i_local_port_t *iport = (fct_i_local_port_t *)
1996 	    port->port_fct_private;
1997 	uint32_t old, new;
1998 
1999 	if ((ioflags & FCT_IOF_FCA_DONE) == 0) {
2000 		/* Until we support confirmed completions, this is an error */
2001 		fct_queue_cmd_for_termination(cmd, s);
2002 		return;
2003 	}
2004 	do {
2005 		old = new = icmd->icmd_flags;
2006 		if (old & ICMD_BEING_ABORTED) {
2007 			return;
2008 		}
2009 		new &= ~ICMD_KNOWN_TO_FCA;
2010 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2011 
2012 	cmd->cmd_comp_status = s;
2013 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2014 		stmf_send_status_done((scsi_task_t *)cmd->cmd_specific, s,
2015 		    STMF_IOF_LPORT_DONE);
2016 		return;
2017 	}
2018 
2019 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
2020 		fct_cmd_free(cmd);
2021 		return;
2022 	} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
2023 		fct_handle_sol_els_completion(iport, icmd);
2024 	} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
2025 		/* Tell the caller that we are done */
2026 		atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
2027 	} else {
2028 		ASSERT(0);
2029 	}
2030 }
2031 
2032 void
2033 fct_cmd_free(fct_cmd_t *cmd)
2034 {
2035 	char			info[FCT_INFO_LEN];
2036 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2037 	fct_local_port_t	*port = cmd->cmd_port;
2038 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
2039 	    port->port_fct_private;
2040 	fct_i_remote_port_t	*irp = NULL;
2041 	int			do_abts_acc = 0;
2042 	uint32_t		old, new;
2043 
2044 	ASSERT(!mutex_owned(&iport->iport_worker_lock));
2045 	/* Give the slot back */
2046 	if (CMD_HANDLE_VALID(cmd->cmd_handle)) {
2047 		uint16_t n = CMD_HANDLE_SLOT_INDEX(cmd->cmd_handle);
2048 		fct_cmd_slot_t *slot;
2049 
2050 		/*
2051 		 * If anything went wrong, grab the lock as writer. This is
2052 		 * probably unnecessary.
2053 		 */
2054 		if ((cmd->cmd_comp_status != FCT_SUCCESS) ||
2055 		    (icmd->icmd_flags & ICMD_ABTS_RECEIVED)) {
2056 			rw_enter(&iport->iport_lock, RW_WRITER);
2057 		} else {
2058 			rw_enter(&iport->iport_lock, RW_READER);
2059 		}
2060 
2061 		if ((icmd->icmd_flags & ICMD_ABTS_RECEIVED) &&
2062 		    (cmd->cmd_link != NULL)) {
2063 			do_abts_acc = 1;
2064 		}
2065 
2066 		/* XXX Validate slot before freeing */
2067 
2068 		slot = &iport->iport_cmd_slots[n];
2069 		slot->slot_uniq_cntr++;
2070 		slot->slot_cmd = NULL;
2071 		do {
2072 			old = iport->iport_next_free_slot;
2073 			slot->slot_next = old & 0xFFFF;
2074 			new = (old + 0x10000) & 0xFFFF0000;
2075 			new |= slot->slot_no;
2076 		} while (atomic_cas_32(&iport->iport_next_free_slot,
2077 		    old, new) != old);
2078 		cmd->cmd_handle = 0;
2079 		atomic_inc_16(&iport->iport_nslots_free);
2080 		if (cmd->cmd_rp) {
2081 			irp = (fct_i_remote_port_t *)
2082 			    cmd->cmd_rp->rp_fct_private;
2083 			if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2084 				atomic_dec_16(&irp->irp_fcp_xchg_count);
2085 			else
2086 				atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2087 		}
2088 		rw_exit(&iport->iport_lock);
2089 	} else if ((icmd->icmd_flags & ICMD_IMPLICIT) &&
2090 	    (icmd->icmd_flags & ICMD_IMPLICIT_CMD_HAS_RESOURCE)) {
2091 		/* for implicit cmd, no cmd slot is used */
2092 		if (cmd->cmd_rp) {
2093 			irp = (fct_i_remote_port_t *)
2094 			    cmd->cmd_rp->rp_fct_private;
2095 			if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
2096 				atomic_dec_16(&irp->irp_fcp_xchg_count);
2097 			else
2098 				atomic_dec_16(&irp->irp_nonfcp_xchg_count);
2099 		}
2100 	}
2101 
2102 	if (do_abts_acc) {
2103 		fct_cmd_t *lcmd = cmd->cmd_link;
2104 		fct_fill_abts_acc(lcmd);
2105 		if (port->port_send_cmd_response(lcmd,
2106 		    FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2107 			/*
2108 			 * XXX Throw HBA fatal error event
2109 			 * Later shutdown svc will terminate the ABTS in the end
2110 			 */
2111 			(void) snprintf(info, sizeof (info),
2112 			    "fct_cmd_free: iport-%p, ABTS_ACC"
2113 			    " port_send_cmd_response failed", (void *)iport);
2114 			(void) fct_port_shutdown(iport->iport_port,
2115 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2116 			return;
2117 		} else {
2118 			fct_cmd_free(lcmd);
2119 			cmd->cmd_link = NULL;
2120 		}
2121 	}
2122 
2123 	/* Free the cmd */
2124 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2125 		if (iport->iport_cached_ncmds < max_cached_ncmds) {
2126 			icmd->icmd_flags = 0;
2127 			mutex_enter(&iport->iport_cached_cmd_lock);
2128 			icmd->icmd_next = iport->iport_cached_cmdlist;
2129 			iport->iport_cached_cmdlist = icmd;
2130 			iport->iport_cached_ncmds++;
2131 			mutex_exit(&iport->iport_cached_cmd_lock);
2132 		} else {
2133 			atomic_dec_32(&iport->iport_total_alloced_ncmds);
2134 			fct_free(cmd);
2135 		}
2136 	} else {
2137 		fct_free(cmd);
2138 	}
2139 }
2140 
2141 /* ARGSUSED */
2142 stmf_status_t
2143 fct_scsi_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
2144     uint32_t flags)
2145 {
2146 	stmf_status_t ret = STMF_SUCCESS;
2147 	scsi_task_t *task;
2148 	fct_cmd_t *cmd;
2149 	fct_i_cmd_t *icmd;
2150 	fct_local_port_t *port;
2151 	uint32_t old, new;
2152 
2153 	ASSERT(abort_cmd == STMF_LPORT_ABORT_TASK);
2154 
2155 	task = (scsi_task_t *)arg;
2156 	cmd = (fct_cmd_t *)task->task_port_private;
2157 	icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2158 	port = (fct_local_port_t *)lport->lport_port_private;
2159 
2160 	do {
2161 		old = new = icmd->icmd_flags;
2162 		if ((old & ICMD_KNOWN_TO_FCA) == 0)
2163 			return (STMF_NOT_FOUND);
2164 		ASSERT((old & ICMD_FCA_ABORT_CALLED) == 0);
2165 		new |= ICMD_BEING_ABORTED | ICMD_FCA_ABORT_CALLED;
2166 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2167 	ret = port->port_abort_cmd(port, cmd, 0);
2168 	if ((ret == FCT_NOT_FOUND) || (ret == FCT_ABORT_SUCCESS)) {
2169 		atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2170 	} else if (ret == FCT_BUSY) {
2171 		atomic_and_32(&icmd->icmd_flags, ~ICMD_FCA_ABORT_CALLED);
2172 	}
2173 
2174 	return (ret);
2175 }
2176 
2177 void
2178 fct_ctl(struct stmf_local_port *lport, int cmd, void *arg)
2179 {
2180 	fct_local_port_t *port;
2181 	fct_i_local_port_t *iport;
2182 	stmf_change_status_t st;
2183 	stmf_change_status_t *pst;
2184 
2185 	ASSERT((cmd == STMF_CMD_LPORT_ONLINE) ||
2186 	    (cmd == STMF_ACK_LPORT_ONLINE_COMPLETE) ||
2187 	    (cmd == STMF_CMD_LPORT_OFFLINE) ||
2188 	    (cmd == STMF_ACK_LPORT_OFFLINE_COMPLETE) ||
2189 	    (cmd == FCT_CMD_PORT_ONLINE_COMPLETE) ||
2190 	    (cmd == FCT_CMD_PORT_OFFLINE_COMPLETE));
2191 
2192 	port = (fct_local_port_t *)lport->lport_port_private;
2193 	pst = (stmf_change_status_t *)arg;
2194 	st.st_completion_status = STMF_SUCCESS;
2195 	st.st_additional_info = NULL;
2196 
2197 	iport = (fct_i_local_port_t *)port->port_fct_private;
2198 	/*
2199 	 * We are mostly a passthrough, except during offline.
2200 	 */
2201 	switch (cmd) {
2202 	case STMF_CMD_LPORT_ONLINE:
2203 		if (iport->iport_state == FCT_STATE_ONLINE)
2204 			st.st_completion_status = STMF_ALREADY;
2205 		else if (iport->iport_state != FCT_STATE_OFFLINE)
2206 			st.st_completion_status = STMF_INVALID_ARG;
2207 		if (st.st_completion_status != STMF_SUCCESS) {
2208 			(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport,
2209 			    &st);
2210 			break;
2211 		}
2212 		iport->iport_state_not_acked = 1;
2213 		iport->iport_state = FCT_STATE_ONLINING;
2214 		port->port_ctl(port, FCT_CMD_PORT_ONLINE, arg);
2215 		break;
2216 	case FCT_CMD_PORT_ONLINE_COMPLETE:
2217 		ASSERT(iport->iport_state == FCT_STATE_ONLINING);
2218 		if (pst->st_completion_status != FCT_SUCCESS) {
2219 			iport->iport_state = FCT_STATE_OFFLINE;
2220 			iport->iport_state_not_acked = 0;
2221 		} else {
2222 			iport->iport_state = FCT_STATE_ONLINE;
2223 		}
2224 		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, arg);
2225 		break;
2226 	case STMF_ACK_LPORT_ONLINE_COMPLETE:
2227 		ASSERT(iport->iport_state == FCT_STATE_ONLINE);
2228 		iport->iport_state_not_acked = 0;
2229 		port->port_ctl(port, FCT_ACK_PORT_ONLINE_COMPLETE, arg);
2230 		break;
2231 
2232 	case STMF_CMD_LPORT_OFFLINE:
2233 		if (iport->iport_state == FCT_STATE_OFFLINE)
2234 			st.st_completion_status = STMF_ALREADY;
2235 		else if (iport->iport_state != FCT_STATE_ONLINE)
2236 			st.st_completion_status = STMF_INVALID_ARG;
2237 		if (st.st_completion_status != STMF_SUCCESS) {
2238 			(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2239 			    &st);
2240 			break;
2241 		}
2242 		iport->iport_state_not_acked = 1;
2243 		iport->iport_state = FCT_STATE_OFFLINING;
2244 		port->port_ctl(port, FCT_CMD_PORT_OFFLINE, arg);
2245 		break;
2246 	case FCT_CMD_PORT_OFFLINE_COMPLETE:
2247 		ASSERT(iport->iport_state == FCT_STATE_OFFLINING);
2248 		if (pst->st_completion_status != FCT_SUCCESS) {
2249 			iport->iport_state = FCT_STATE_ONLINE;
2250 			iport->iport_state_not_acked = 0;
2251 			(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2252 			    pst);
2253 			break;
2254 		}
2255 
2256 		/*
2257 		 * If FCA's offline was successful, we dont tell stmf yet.
2258 		 * Becasue now we have to do the cleanup before we go upto
2259 		 * stmf. That cleanup is done by the worker thread.
2260 		 */
2261 
2262 		/* FCA is offline, post a link down, its harmless anyway */
2263 		fct_handle_event(port, FCT_EVENT_LINK_DOWN, 0, 0);
2264 
2265 		/* Trigger port offline processing by the worker */
2266 		iport->iport_offline_prstate = FCT_OPR_START;
2267 		break;
2268 	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
2269 		ASSERT(iport->iport_state == FCT_STATE_OFFLINE);
2270 		iport->iport_state_not_acked = 0;
2271 		port->port_ctl(port, FCT_ACK_PORT_OFFLINE_COMPLETE, arg);
2272 		break;
2273 	}
2274 }
2275 
2276 /* ARGSUSED */
2277 stmf_status_t
2278 fct_info(uint32_t cmd, stmf_local_port_t *lport, void *arg, uint8_t *buf,
2279     uint32_t *bufsizep)
2280 {
2281 	return (STMF_NOT_SUPPORTED);
2282 }
2283 
2284 /*
2285  * implicit: if it's true, it means it will only be used in fct module, or else
2286  * it will be sent to the link.
2287  */
2288 fct_cmd_t *
2289 fct_create_solels(fct_local_port_t *port, fct_remote_port_t *rp, int implicit,
2290     uchar_t elsop, uint32_t wkdid, fct_icmd_cb_t icmdcb)
2291 {
2292 	fct_cmd_t		*cmd	= NULL;
2293 	fct_i_cmd_t		*icmd	= NULL;
2294 	fct_els_t		*els	= NULL;
2295 	fct_i_remote_port_t	*irp	= NULL;
2296 	uint8_t			*p	= NULL;
2297 	uint32_t		 ptid	= 0;
2298 
2299 	cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_SOL_ELS,
2300 	    port->port_fca_sol_els_private_size, 0);
2301 	if (!cmd) {
2302 		return (NULL);
2303 	}
2304 
2305 	if (rp) {
2306 		irp = RP_TO_IRP(rp);
2307 	} else if (((irp = fct_portid_to_portptr(PORT_TO_IPORT(port),
2308 	    wkdid)) == NULL) && (elsop != ELS_OP_PLOGI)) {
2309 		stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2310 		    "fct_create_solels: Must PLOGI to %x first", wkdid);
2311 		fct_free(cmd);
2312 		return (NULL);
2313 	}
2314 
2315 	cmd->cmd_port	= port;
2316 	cmd->cmd_oxid	= PTR2INT(cmd, uint16_t);
2317 	cmd->cmd_rxid	= 0xFFFF;
2318 	cmd->cmd_handle = 0;
2319 	icmd		= CMD_TO_ICMD(cmd);
2320 	els		= ICMD_TO_ELS(icmd);
2321 	icmd->icmd_cb	= icmdcb;
2322 	if (irp) {
2323 		cmd->cmd_rp	   = irp->irp_rp;
2324 		cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2325 		cmd->cmd_rportid   = irp->irp_rp->rp_id;
2326 	} else {
2327 		cmd->cmd_rp_handle = FCT_HANDLE_NONE;
2328 		cmd->cmd_rportid   = wkdid;
2329 	}
2330 	cmd->cmd_lportid = (PORT_TO_IPORT(port))->iport_link_info.portid;
2331 
2332 	if (implicit) {
2333 		/*
2334 		 * Since we will not send it to FCA, so we only allocate space
2335 		 */
2336 		ASSERT(elsop & (ELS_OP_LOGO | ELS_OP_PLOGI));
2337 		icmd->icmd_flags |= ICMD_IMPLICIT;
2338 		if (elsop == ELS_OP_LOGO) {
2339 			/*
2340 			 * Handling implicit LOGO should dependent on as less
2341 			 * as resources. So a trick here.
2342 			 */
2343 			els->els_req_size = 1;
2344 			els->els_req_payload = cmd->cmd_fca_private;
2345 		} else {
2346 			els->els_req_alloc_size = els->els_req_size = 116;
2347 			els->els_resp_alloc_size = els->els_resp_size = 116;
2348 			els->els_req_payload = (uint8_t *)
2349 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2350 			els->els_resp_payload = (uint8_t *)
2351 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2352 		}
2353 	} else {
2354 		/*
2355 		 * Allocate space for its request and response
2356 		 * Fill the request payload according to spec.
2357 		 */
2358 		switch (elsop) {
2359 		case ELS_OP_LOGO:
2360 			els->els_resp_alloc_size = els->els_resp_size = 4;
2361 			els->els_resp_payload = (uint8_t *)kmem_zalloc(
2362 			    els->els_resp_size, KM_SLEEP);
2363 			els->els_req_alloc_size = els->els_req_size = 16;
2364 			els->els_req_payload = (uint8_t *)kmem_zalloc(
2365 			    els->els_req_size, KM_SLEEP);
2366 			ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2367 			fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2368 			bcopy(port->port_pwwn, els->els_req_payload + 8, 8);
2369 			break;
2370 
2371 		case ELS_OP_RSCN:
2372 			els->els_resp_alloc_size = els->els_resp_size = 4;
2373 			els->els_resp_payload = (uint8_t *)kmem_zalloc(
2374 			    els->els_resp_size, KM_SLEEP);
2375 			els->els_req_size = els->els_req_alloc_size = 8;
2376 			els->els_req_payload = (uint8_t *)kmem_zalloc(
2377 			    els->els_req_size, KM_SLEEP);
2378 			els->els_req_payload[1] = 0x04;
2379 			els->els_req_payload[3] = 0x08;
2380 			els->els_req_payload[4] |= 0x80;
2381 			ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2382 			fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2383 			break;
2384 
2385 		case ELS_OP_PLOGI:
2386 			els->els_resp_alloc_size = els->els_resp_size = 116;
2387 			els->els_resp_payload = (uint8_t *)
2388 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2389 			els->els_req_alloc_size = els->els_req_size = 116;
2390 			p = els->els_req_payload = (uint8_t *)
2391 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2392 			bcopy(port->port_pwwn, p + 20, 8);
2393 			bcopy(port->port_nwwn, p + 28, 8);
2394 
2395 			/*
2396 			 * Common service parameters
2397 			 */
2398 			p[0x04] = 0x09;		/* high version */
2399 			p[0x05] = 0x08;		/* low version */
2400 			p[0x06] = 0x00;		/* BB credit: 0x0065 */
2401 			p[0x07] = 0x65;
2402 
2403 			/* CI0: Continuously Increasing Offset - 1 */
2404 			/* RRO: Randomly Relative Offset - 0 */
2405 			/* VVV: Vendor Version Level - 0 */
2406 			/* N-F: N or F Port Payload Sender - 0 (N) */
2407 			/* BBM: BB Credit Management - 0 (Normal) */
2408 			p[0x08] = 0x80;
2409 			p[0x09] = 0x00;
2410 
2411 			/* Max RX size */
2412 			p[0x0A] = 0x08;
2413 			p[0x0B] = 0x00;
2414 
2415 			/* NPTCS: N Port Total Concurrent Sequences - 0x0000 */
2416 			p[0x0C] = 0x00;
2417 			p[0x0D] = 0x00;
2418 
2419 			/* ROIC: Relative Offset By Info - 0xFFFF */
2420 			p[0x0E] = 0xFF;
2421 			p[0x0F] = 0xFF;
2422 
2423 			/* EDTOV: Error Detect Timeout - 0x000007D0 */
2424 			p[0x10] = 0x00;
2425 			p[0x11] = 0x00;
2426 			p[0x12] = 0x07;
2427 			p[0x13] = 0xD0;
2428 
2429 			/*
2430 			 * Class-3 Parameters
2431 			 */
2432 			/* C3-VAL: Class 3 Value - 1 */
2433 			/* C3-XID: X_ID Reassignment - 0 */
2434 			/* C3-IPA: Initial Process Assignment */
2435 			/* C3-AI-DCC: Data compression capable */
2436 			/* C3-AI-DC-HB: Data compression history buffer size */
2437 			/* C3-AI-DCE: Data encrytion capable */
2438 			/* C3-AI-CSC: Clock synchronization capable */
2439 			/* C3-ErrPol: Error pliciy */
2440 			/* C3-CatSeq: Information Cat. Per Sequence */
2441 			/* C3-AR-DCC: */
2442 			/* C3-AR-DC-HB: */
2443 			/* C3-AR-DCE: */
2444 			/* C3-AR-CSC */
2445 			p[0x44] = 0x80;
2446 			p[0x45] = 0x00;
2447 			p[0x46] = 0x00;
2448 			p[0x47] = 0x00;
2449 			p[0x48] = 0x00;
2450 			p[0x49] = 0x00;
2451 
2452 			/* C3-RxSize: Class 3 receive data size */
2453 			p[0x4A] = 0x08;
2454 			p[0x4B] = 0x00;
2455 
2456 			/* C3-ConSeq: Class 3 Concourrent sequences */
2457 			p[0x4C] = 0x00;
2458 			p[0x4D] = 0xFF;
2459 
2460 			/* C3-OSPE: Class 3 open sequence per exchange */
2461 			p[0x50] = 0x00;
2462 			p[0x51] = 0x01;
2463 
2464 			break;
2465 
2466 		case ELS_OP_SCR:
2467 			els->els_resp_alloc_size = els->els_resp_size = 4;
2468 			els->els_resp_payload = (uint8_t *)
2469 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2470 			els->els_req_alloc_size = els->els_req_size = 8;
2471 			p = els->els_req_payload = (uint8_t *)
2472 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2473 			p[7] = FC_SCR_FULL_REGISTRATION;
2474 			break;
2475 		case ELS_OP_RLS:
2476 			els->els_resp_alloc_size = els->els_resp_size = 28;
2477 			els->els_resp_payload = (uint8_t *)
2478 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2479 			els->els_req_alloc_size = els->els_req_size = 8;
2480 			p = els->els_req_payload = (uint8_t *)
2481 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2482 			ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2483 			fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2484 			break;
2485 
2486 		default:
2487 			ASSERT(0);
2488 		}
2489 	}
2490 
2491 	els->els_req_payload[0] = elsop;
2492 	return (cmd);
2493 }
2494 
2495 fct_cmd_t *
2496 fct_create_solct(fct_local_port_t *port, fct_remote_port_t *query_rp,
2497     uint16_t ctop, fct_icmd_cb_t icmdcb)
2498 {
2499 	fct_cmd_t		*cmd	 = NULL;
2500 	fct_i_cmd_t		*icmd	 = NULL;
2501 	fct_sol_ct_t		*ct	 = NULL;
2502 	uint8_t			*p	 = NULL;
2503 	fct_i_remote_port_t	*irp	 = NULL;
2504 	fct_i_local_port_t	*iport	 = NULL;
2505 	char			*nname	 = NULL;
2506 	int			 namelen = 0;
2507 
2508 	/*
2509 	 * Allocate space
2510 	 */
2511 	cmd = fct_alloc(FCT_STRUCT_CMD_SOL_CT,
2512 	    port->port_fca_sol_ct_private_size, 0);
2513 	if (!cmd) {
2514 		return (NULL);
2515 	}
2516 
2517 	/*
2518 	 * We should have PLOGIed to the name server (0xFFFFFC)
2519 	 * Caution: this irp is not query_rp->rp_fct_private.
2520 	 */
2521 	irp = fct_portid_to_portptr((fct_i_local_port_t *)
2522 	    port->port_fct_private, FS_NAME_SERVER);
2523 	if (irp == NULL) {
2524 		stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2525 		    "fct_create_solct: Must PLOGI name server first");
2526 		fct_free(cmd);
2527 		return (NULL);
2528 	}
2529 
2530 	cmd->cmd_port	   = port;
2531 	cmd->cmd_rp	   = irp->irp_rp;
2532 	cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2533 	cmd->cmd_rportid   = irp->irp_rp->rp_id;
2534 	cmd->cmd_lportid   = (PORT_TO_IPORT(port))->iport_link_info.portid;
2535 	cmd->cmd_oxid	   = PTR2INT(cmd, uint16_t);
2536 	cmd->cmd_rxid	   = 0xFFFF;
2537 	cmd->cmd_handle	   = 0;
2538 	icmd		   = CMD_TO_ICMD(cmd);
2539 	ct		   = ICMD_TO_CT(icmd);
2540 	icmd->icmd_cb	   = icmdcb;
2541 	iport		   = ICMD_TO_IPORT(icmd);
2542 
2543 	switch (ctop) {
2544 	case NS_GSNN_NN:
2545 		/*
2546 		 * Allocate max space for its sybolic name
2547 		 */
2548 		ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2549 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2550 		    KM_SLEEP);
2551 
2552 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2553 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2554 		    KM_SLEEP);
2555 
2556 		bcopy(query_rp->rp_nwwn, p + 16, 8);
2557 		break;
2558 
2559 	case NS_RNN_ID:
2560 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2561 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2562 		    KM_SLEEP);
2563 		ct->ct_req_size = ct->ct_req_alloc_size = 28;
2564 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2565 		    KM_SLEEP);
2566 
2567 		/*
2568 		 * Port Identifier
2569 		 */
2570 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2571 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2572 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2573 
2574 		/*
2575 		 * Node Name
2576 		 */
2577 		bcopy(port->port_nwwn, p + 20, 8);
2578 		break;
2579 
2580 	case NS_RCS_ID:
2581 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2582 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2583 		    KM_SLEEP);
2584 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2585 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2586 		    KM_SLEEP);
2587 
2588 		/*
2589 		 * Port Identifier
2590 		 */
2591 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2592 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2593 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2594 
2595 		/*
2596 		 * Class of Service
2597 		 */
2598 		*(p + 23) = FC_NS_CLASS3;
2599 		break;
2600 
2601 	case NS_RFT_ID:
2602 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2603 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2604 		    KM_SLEEP);
2605 		ct->ct_req_size = ct->ct_req_alloc_size = 52;
2606 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2607 		    KM_SLEEP);
2608 
2609 		/*
2610 		 * Port Identifier
2611 		 */
2612 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2613 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2614 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2615 
2616 		/*
2617 		 * FC-4 Protocol Types
2618 		 */
2619 		*(p + 22) = 0x1;	/* 0x100 */
2620 		break;
2621 
2622 	case NS_RSPN_ID:
2623 		/*
2624 		 * If we get here, port->port_sym_port_name is always not NULL.
2625 		 */
2626 		ASSERT(port->port_sym_port_name);
2627 		namelen = strlen(port->port_sym_port_name);
2628 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2629 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2630 		    KM_SLEEP);
2631 		ct->ct_req_size = ct->ct_req_alloc_size =
2632 		    (21 + namelen + 3) & ~3;
2633 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2634 		    KM_SLEEP);
2635 
2636 		/*
2637 		 * Port Identifier
2638 		 */
2639 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2640 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2641 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2642 
2643 		/*
2644 		 * String length
2645 		 */
2646 		p[20] = namelen;
2647 
2648 		/*
2649 		 * Symbolic port name
2650 		 */
2651 		bcopy(port->port_sym_port_name, p + 21, ct->ct_req_size - 21);
2652 		break;
2653 
2654 	case NS_RSNN_NN:
2655 		namelen = port->port_sym_node_name == NULL ?
2656 		    strlen(utsname.nodename) :
2657 		    strlen(port->port_sym_node_name);
2658 		nname = port->port_sym_node_name == NULL ?
2659 		    utsname.nodename : port->port_sym_node_name;
2660 
2661 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2662 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2663 		    KM_SLEEP);
2664 		ct->ct_req_size = ct->ct_req_alloc_size =
2665 		    (25 + namelen + 3) & ~3;
2666 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2667 		    KM_SLEEP);
2668 
2669 		/*
2670 		 * Node name
2671 		 */
2672 		bcopy(port->port_nwwn, p + 16, 8);
2673 
2674 		/*
2675 		 * String length
2676 		 */
2677 		p[24] = namelen;
2678 
2679 		/*
2680 		 * Symbolic node name
2681 		 */
2682 		bcopy(nname, p + 25, ct->ct_req_size - 25);
2683 		break;
2684 
2685 	case NS_GSPN_ID:
2686 		ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2687 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2688 		    KM_SLEEP);
2689 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2690 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2691 		    KM_SLEEP);
2692 		/*
2693 		 * Port Identifier
2694 		 */
2695 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2696 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2697 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2698 		break;
2699 
2700 	case NS_GCS_ID:
2701 		ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2702 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2703 		    KM_SLEEP);
2704 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2705 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2706 		    KM_SLEEP);
2707 		/*
2708 		 * Port Identifier
2709 		 */
2710 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2711 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2712 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2713 		break;
2714 
2715 	case NS_GFT_ID:
2716 		ct->ct_resp_alloc_size = ct->ct_resp_size = 48;
2717 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2718 		    KM_SLEEP);
2719 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2720 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2721 		    KM_SLEEP);
2722 		/*
2723 		 * Port Identifier
2724 		 */
2725 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2726 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2727 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2728 		break;
2729 
2730 	case NS_GID_PN:
2731 		ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2732 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2733 		    KM_SLEEP);
2734 
2735 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2736 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2737 		    KM_SLEEP);
2738 
2739 		bcopy(query_rp->rp_pwwn, p + 16, 8);
2740 		break;
2741 
2742 	default:
2743 		/* CONSTCOND */
2744 		ASSERT(0);
2745 	}
2746 
2747 	FCT_FILL_CTIU_PREAMBLE(p, ctop);
2748 	return (cmd);
2749 }
2750 
2751 /*
2752  * Cmd can only be solicited CT/ELS. They will be dispatched to the discovery
2753  * queue eventually too.
2754  * We queue solicited cmds here to track solicited cmds and to take full use
2755  * of single thread mechanism.
2756  * But in current implmentation, we don't use  this mechanism on SOL_CT, PLOGI.
2757  * To avoid to interrupt current flow, ICMD_IN_SOLCMD_QUEUE is used here.
2758  */
2759 void
2760 fct_post_to_solcmd_queue(fct_local_port_t *port, fct_cmd_t *cmd)
2761 {
2762 	fct_i_local_port_t	*iport	= (fct_i_local_port_t *)
2763 	    port->port_fct_private;
2764 	fct_i_cmd_t *icmd		= (fct_i_cmd_t *)cmd->cmd_fct_private;
2765 
2766 	mutex_enter(&iport->iport_worker_lock);
2767 	icmd->icmd_solcmd_next = iport->iport_solcmd_queue;
2768 	iport->iport_solcmd_queue = icmd;
2769 	atomic_or_32(&icmd->icmd_flags, ICMD_IN_SOLCMD_QUEUE | ICMD_SOLCMD_NEW);
2770 	if (IS_WORKER_SLEEPING(iport)) {
2771 		cv_signal(&iport->iport_worker_cv);
2772 	}
2773 	mutex_exit(&iport->iport_worker_lock);
2774 }
2775 
2776 /* ARGSUSED */
2777 void
2778 fct_event_handler(stmf_local_port_t *lport, int eventid, void *arg,
2779     uint32_t flags)
2780 {
2781 	fct_local_port_t	*port  = (fct_local_port_t *)
2782 	    lport->lport_port_private;
2783 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
2784 	    port->port_fct_private;
2785 	stmf_scsi_session_t	*ss;
2786 	fct_i_remote_port_t	*irp;
2787 
2788 	switch (eventid) {
2789 	case LPORT_EVENT_INITIAL_LUN_MAPPED:
2790 		ss = (stmf_scsi_session_t *)arg;
2791 		irp = (fct_i_remote_port_t *)ss->ss_port_private;
2792 		stmf_trace(iport->iport_alias,
2793 		    "Initial LUN mapped to session ss-%p, irp-%p", ss, irp);
2794 		break;
2795 
2796 	default:
2797 		stmf_trace(iport->iport_alias,
2798 		    "Unknown event received, %d", eventid);
2799 	}
2800 }
2801 
2802 void
2803 fct_send_cmd_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2804 {
2805 	/* XXX For now just call send_resp_done() */
2806 	fct_send_response_done(cmd, s, ioflags);
2807 }
2808 
2809 void
2810 fct_cmd_fca_aborted(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2811 {
2812 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2813 	char			info[FCT_INFO_LEN];
2814 	unsigned long long	st;
2815 
2816 	st = s;	/* To make gcc happy */
2817 	ASSERT(icmd->icmd_flags & ICMD_BEING_ABORTED);
2818 	if ((((s != FCT_ABORT_SUCCESS) && (s != FCT_NOT_FOUND))) ||
2819 	    ((ioflags & FCT_IOF_FCA_DONE) == 0)) {
2820 		(void) snprintf(info, sizeof (info),
2821 		    "fct_cmd_fca_aborted: cmd-%p, "
2822 		    "s-%llx, iofalgs-%x", (void *)cmd, st, ioflags);
2823 		(void) fct_port_shutdown(cmd->cmd_port,
2824 		    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2825 		return;
2826 	}
2827 
2828 	atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2829 	/* For non FCP Rest of the work is done by the terminator */
2830 	/* For FCP stuff just call stmf */
2831 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2832 		stmf_task_lport_aborted((scsi_task_t *)cmd->cmd_specific,
2833 		    s, STMF_IOF_LPORT_DONE);
2834 	}
2835 }
2836 
2837 /*
2838  * FCA drivers will use it, when they want to abort some FC transactions
2839  * due to lack of resource.
2840  */
2841 uint16_t
2842 fct_get_rp_handle(fct_local_port_t *port, uint32_t rportid)
2843 {
2844 	fct_i_remote_port_t	*irp;
2845 
2846 	irp = fct_portid_to_portptr(
2847 	    (fct_i_local_port_t *)(port->port_fct_private), rportid);
2848 	if (irp == NULL) {
2849 		return (0xFFFF);
2850 	} else {
2851 		return (irp->irp_rp->rp_handle);
2852 	}
2853 }
2854 
2855 fct_cmd_t *
2856 fct_handle_to_cmd(fct_local_port_t *port, uint32_t fct_handle)
2857 {
2858 	fct_cmd_slot_t *slot;
2859 	uint16_t ndx;
2860 
2861 	if (!CMD_HANDLE_VALID(fct_handle))
2862 		return (NULL);
2863 	if ((ndx = CMD_HANDLE_SLOT_INDEX(fct_handle)) >= port->port_max_xchges)
2864 		return (NULL);
2865 
2866 	slot = &((fct_i_local_port_t *)port->port_fct_private)->iport_cmd_slots[
2867 	    ndx];
2868 
2869 	if ((slot->slot_uniq_cntr | 0x80) != (fct_handle >> 24))
2870 		return (NULL);
2871 	return (slot->slot_cmd->icmd_cmd);
2872 }
2873 
2874 void
2875 fct_queue_scsi_task_for_termination(fct_cmd_t *cmd, fct_status_t s)
2876 {
2877 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2878 
2879 	uint32_t old, new;
2880 
2881 	do {
2882 		old = icmd->icmd_flags;
2883 		if ((old & (ICMD_BEING_ABORTED | ICMD_KNOWN_TO_FCA)) !=
2884 		    ICMD_KNOWN_TO_FCA)
2885 			return;
2886 		new = old | ICMD_BEING_ABORTED;
2887 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2888 	stmf_abort(STMF_QUEUE_TASK_ABORT, (scsi_task_t *)cmd->cmd_specific,
2889 	    s, NULL);
2890 }
2891 
2892 void
2893 fct_fill_abts_acc(fct_cmd_t *cmd)
2894 {
2895 	fct_rcvd_abts_t *abts = (fct_rcvd_abts_t *)cmd->cmd_specific;
2896 	uint8_t *p;
2897 
2898 	abts->abts_resp_rctl = BLS_OP_BA_ACC;
2899 	p = abts->abts_resp_payload;
2900 	bzero(p, 12);
2901 	*((uint16_t *)(p+4)) = BE_16(cmd->cmd_oxid);
2902 	*((uint16_t *)(p+6)) = BE_16(cmd->cmd_rxid);
2903 	p[10] = p[11] = 0xff;
2904 }
2905 
2906 void
2907 fct_handle_rcvd_abts(fct_cmd_t *cmd)
2908 {
2909 	char			info[FCT_INFO_LEN];
2910 	fct_local_port_t	*port = cmd->cmd_port;
2911 	fct_i_local_port_t	*iport =
2912 	    (fct_i_local_port_t *)port->port_fct_private;
2913 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2914 	fct_i_remote_port_t	*irp;
2915 	fct_cmd_t		*c = NULL;
2916 	fct_i_cmd_t		*ic = NULL;
2917 	int			found = 0;
2918 	int			i;
2919 
2920 	icmd->icmd_start_time = ddi_get_lbolt();
2921 	icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
2922 
2923 	rw_enter(&iport->iport_lock, RW_WRITER);
2924 	/* Make sure local port is sane */
2925 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2926 		rw_exit(&iport->iport_lock);
2927 		stmf_trace(iport->iport_alias, "ABTS not posted becasue"
2928 		    "port state was %x", iport->iport_link_state);
2929 		fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2930 		return;
2931 	}
2932 
2933 	if (cmd->cmd_rp_handle == FCT_HANDLE_NONE)
2934 		irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
2935 	else if (cmd->cmd_rp_handle < port->port_max_logins)
2936 		irp = iport->iport_rp_slots[cmd->cmd_rp_handle];
2937 	else
2938 		irp = NULL;
2939 	if (irp == NULL) {
2940 		/* XXX Throw a logout to the initiator */
2941 		rw_exit(&iport->iport_lock);
2942 		stmf_trace(iport->iport_alias, "ABTS received from"
2943 		    " %x without a session", cmd->cmd_rportid);
2944 		fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
2945 		return;
2946 	}
2947 
2948 	DTRACE_FC_3(abts__receive,
2949 	    fct_cmd_t, cmd,
2950 	    fct_local_port_t, port,
2951 	    fct_i_remote_port_t, irp);
2952 
2953 	cmd->cmd_rp = irp->irp_rp;
2954 
2955 	/*
2956 	 * No need to allocate an xchg resource. ABTSes use the same
2957 	 * xchg resource as the cmd they are aborting.
2958 	 */
2959 	rw_enter(&irp->irp_lock, RW_WRITER);
2960 	mutex_enter(&iport->iport_worker_lock);
2961 	/* Lets find the command first */
2962 	for (i = 0; i < port->port_max_xchges; i++) {
2963 		if ((ic = iport->iport_cmd_slots[i].slot_cmd) == NULL)
2964 			continue;
2965 		if ((ic->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
2966 			continue;
2967 		c = ic->icmd_cmd;
2968 		if (!CMD_HANDLE_VALID(c->cmd_handle))
2969 			continue;
2970 		if ((c->cmd_rportid != cmd->cmd_rportid) ||
2971 		    (c->cmd_oxid != cmd->cmd_oxid))
2972 			continue;
2973 		/* Found the command */
2974 		found = 1;
2975 		break;
2976 	}
2977 	if (!found) {
2978 		mutex_exit(&iport->iport_worker_lock);
2979 		rw_exit(&irp->irp_lock);
2980 		rw_exit(&iport->iport_lock);
2981 		/* Dont even bother queueing it. Just respond */
2982 		fct_fill_abts_acc(cmd);
2983 		if (port->port_send_cmd_response(cmd,
2984 		    FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2985 			/*
2986 			 * XXX Throw HBA fatal error event
2987 			 * Later shutdown svc will terminate the ABTS in the end
2988 			 */
2989 			(void) snprintf(info, sizeof (info),
2990 			    "fct_handle_rcvd_abts: iport-%p, "
2991 			    "ABTS_ACC port_send_cmd_response failed",
2992 			    (void *)iport);
2993 			(void) fct_port_shutdown(iport->iport_port,
2994 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2995 		} else {
2996 			fct_cmd_free(cmd);
2997 		}
2998 		return;
2999 	}
3000 
3001 	/* Check if this an abts retry */
3002 	if (c->cmd_link && (ic->icmd_flags & ICMD_ABTS_RECEIVED)) {
3003 		/* Kill this abts. */
3004 		fct_q_for_termination_lock_held(iport, icmd, FCT_ABORTED);
3005 		if (IS_WORKER_SLEEPING(iport))
3006 			cv_signal(&iport->iport_worker_cv);
3007 		mutex_exit(&iport->iport_worker_lock);
3008 		rw_exit(&irp->irp_lock);
3009 		rw_exit(&iport->iport_lock);
3010 		return;
3011 	}
3012 	c->cmd_link = cmd;
3013 	atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
3014 	cmd->cmd_link = c;
3015 	mutex_exit(&iport->iport_worker_lock);
3016 	rw_exit(&irp->irp_lock);
3017 	fct_queue_cmd_for_termination(c, FCT_ABTS_RECEIVED);
3018 	rw_exit(&iport->iport_lock);
3019 }
3020 
3021 void
3022 fct_queue_cmd_for_termination(fct_cmd_t *cmd, fct_status_t s)
3023 {
3024 	fct_local_port_t *port = cmd->cmd_port;
3025 	fct_i_local_port_t *iport = (fct_i_local_port_t *)
3026 	    port->port_fct_private;
3027 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
3028 
3029 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
3030 		fct_queue_scsi_task_for_termination(cmd, s);
3031 		return;
3032 	}
3033 	mutex_enter(&iport->iport_worker_lock);
3034 	fct_q_for_termination_lock_held(iport, icmd, s);
3035 	if (IS_WORKER_SLEEPING(iport))
3036 		cv_signal(&iport->iport_worker_cv);
3037 	mutex_exit(&iport->iport_worker_lock);
3038 }
3039 
3040 /*
3041  * This function will not be called for SCSI CMDS
3042  */
3043 void
3044 fct_q_for_termination_lock_held(fct_i_local_port_t *iport, fct_i_cmd_t *icmd,
3045     fct_status_t s)
3046 {
3047 	uint32_t old, new;
3048 	fct_i_cmd_t **ppicmd;
3049 
3050 	do {
3051 		old = icmd->icmd_flags;
3052 		if (old & ICMD_BEING_ABORTED)
3053 			return;
3054 		new = old | ICMD_BEING_ABORTED;
3055 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3056 
3057 	icmd->icmd_start_time = ddi_get_lbolt();
3058 	icmd->icmd_cmd->cmd_comp_status = s;
3059 
3060 	icmd->icmd_next = NULL;
3061 	for (ppicmd = &(iport->iport_abort_queue); *ppicmd != NULL;
3062 	    ppicmd = &((*ppicmd)->icmd_next))
3063 		;
3064 
3065 	*ppicmd = icmd;
3066 }
3067 
3068 /*
3069  * For those cmds, for which we called fca_abort but it has not yet completed,
3070  * reset the FCA_ABORT_CALLED flag, so that abort can be called again.
3071  * This is done after a FCA offline. The reason is that after offline, the
3072  * firmware is not running so abort will never complete. But if we call it
3073  * again, the FCA will detect that it is not offline and it will
3074  * not call the firmware at all. Most likely it will abort in a synchronous
3075  * manner i.e. return FCT_ABORT_SUCCESS or FCT_NOT_FOUND.
3076  */
3077 void
3078 fct_reset_flag_abort_called(fct_i_local_port_t *iport)
3079 {
3080 	fct_i_cmd_t *icmd;
3081 	uint32_t old, new;
3082 	int i, do_clear;
3083 
3084 	ASSERT(mutex_owned(&iport->iport_worker_lock));
3085 	mutex_exit(&iport->iport_worker_lock);
3086 	rw_enter(&iport->iport_lock, RW_WRITER);
3087 	mutex_enter(&iport->iport_worker_lock);
3088 
3089 	for (i = 0; i < iport->iport_port->port_max_xchges; i++) {
3090 		if (iport->iport_cmd_slots[i].slot_cmd == NULL)
3091 			continue;
3092 
3093 		icmd = iport->iport_cmd_slots[i].slot_cmd;
3094 
3095 		do {
3096 			old = new = icmd->icmd_flags;
3097 			if ((old & (ICMD_KNOWN_TO_FCA |
3098 			    ICMD_FCA_ABORT_CALLED)) == (ICMD_KNOWN_TO_FCA |
3099 			    ICMD_FCA_ABORT_CALLED)) {
3100 				new &= ~ICMD_FCA_ABORT_CALLED;
3101 				do_clear = 1;
3102 			} else {
3103 				do_clear = 0;
3104 				break;
3105 			}
3106 		} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
3107 		if (do_clear &&
3108 		    (icmd->icmd_cmd->cmd_type == FCT_CMD_FCP_XCHG)) {
3109 			stmf_abort(STMF_REQUEUE_TASK_ABORT_LPORT,
3110 			    icmd->icmd_cmd->cmd_specific, 0, NULL);
3111 		}
3112 	}
3113 
3114 	rw_exit(&iport->iport_lock);
3115 }
3116 
3117 /*
3118  * Modify the irp_deregister_timer such that the ports start deregistering
3119  * quickly.
3120  */
3121 void
3122 fct_irp_deregister_speedup(fct_i_local_port_t *iport)
3123 {
3124 	fct_i_remote_port_t *irp;
3125 	int i;
3126 
3127 	if (!iport->iport_nrps)
3128 		return;
3129 
3130 	for (i = 0; i < rportid_table_size; i++) {
3131 		irp = iport->iport_rp_tb[i];
3132 		while (irp) {
3133 			irp->irp_deregister_timer = ddi_get_lbolt() - 1;
3134 			irp = irp->irp_next;
3135 		}
3136 	}
3137 }
3138 
3139 disc_action_t
3140 fct_handle_port_offline(fct_i_local_port_t *iport)
3141 {
3142 	if (iport->iport_offline_prstate == FCT_OPR_START) {
3143 		fct_reset_flag_abort_called(iport);
3144 		iport->iport_offline_prstate = FCT_OPR_CMD_CLEANUP_WAIT;
3145 		/* fct_ctl has already submitted a link offline event */
3146 		return (DISC_ACTION_DELAY_RESCAN);
3147 	}
3148 	if (iport->iport_offline_prstate == FCT_OPR_CMD_CLEANUP_WAIT) {
3149 		if (iport->iport_link_state != PORT_STATE_LINK_DOWN)
3150 			return (DISC_ACTION_DELAY_RESCAN);
3151 		/*
3152 		 * All I/Os have been killed at this time. Lets speedup
3153 		 * the port deregister process.
3154 		 */
3155 		mutex_exit(&iport->iport_worker_lock);
3156 		rw_enter(&iport->iport_lock, RW_WRITER);
3157 		fct_irp_deregister_speedup(iport);
3158 		rw_exit(&iport->iport_lock);
3159 		mutex_enter(&iport->iport_worker_lock);
3160 		iport->iport_offline_prstate = FCT_OPR_INT_CLEANUP_WAIT;
3161 		return (DISC_ACTION_RESCAN);
3162 	}
3163 	if (iport->iport_offline_prstate == FCT_OPR_INT_CLEANUP_WAIT) {
3164 		stmf_change_status_t st;
3165 
3166 		if (iport->iport_solcmd_queue) {
3167 			return (DISC_ACTION_DELAY_RESCAN);
3168 		}
3169 
3170 		if (iport->iport_nrps) {
3171 			/*
3172 			 * A port logout may have gone when implicit logo all
3173 			 * was retried. So do the port speedup again here.
3174 			 */
3175 			mutex_exit(&iport->iport_worker_lock);
3176 			rw_enter(&iport->iport_lock, RW_WRITER);
3177 			fct_irp_deregister_speedup(iport);
3178 			rw_exit(&iport->iport_lock);
3179 			mutex_enter(&iport->iport_worker_lock);
3180 			return (DISC_ACTION_DELAY_RESCAN);
3181 		}
3182 
3183 		if (iport->iport_event_head != NULL) {
3184 			return (DISC_ACTION_DELAY_RESCAN);
3185 		}
3186 
3187 		st.st_completion_status = STMF_SUCCESS;
3188 		st.st_additional_info = NULL;
3189 		iport->iport_offline_prstate = FCT_OPR_DONE;
3190 		iport->iport_state = FCT_STATE_OFFLINE;
3191 		mutex_exit(&iport->iport_worker_lock);
3192 		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
3193 		    iport->iport_port->port_lport, &st);
3194 		mutex_enter(&iport->iport_worker_lock);
3195 		return (DISC_ACTION_DELAY_RESCAN);
3196 	}
3197 
3198 	/* NOTREACHED */
3199 	return (0);
3200 }
3201 
3202 /*
3203  * See stmf.h for information on rflags. Additional info is just a text
3204  * description of the reason for this call. Additional_info can be NULL.
3205  * Also the caller can declare additional info on the stack. stmf_ctl
3206  * makes a copy of it before returning.
3207  */
3208 fct_status_t
3209 fct_port_initialize(fct_local_port_t *port, uint32_t rflags,
3210     char *additional_info)
3211 {
3212 	stmf_state_change_info_t st;
3213 
3214 	st.st_rflags = rflags;
3215 	st.st_additional_info = additional_info;
3216 	stmf_trace(NULL, "fct_port_initialize: port-%p, %s", port,
3217 	    additional_info? additional_info : "no more information");
3218 	return (stmf_ctl(STMF_CMD_LPORT_ONLINE, port->port_lport, &st));
3219 }
3220 
3221 fct_status_t
3222 fct_port_shutdown(fct_local_port_t *port, uint32_t rflags,
3223     char *additional_info)
3224 {
3225 	stmf_state_change_info_t st;
3226 
3227 	st.st_rflags = rflags;
3228 	st.st_additional_info = additional_info;
3229 	stmf_trace(NULL, "fct_port_shutdown: port-%p, %s", port,
3230 	    additional_info? additional_info : "no more information");
3231 	return (stmf_ctl(STMF_CMD_LPORT_OFFLINE, port->port_lport, &st));
3232 }
3233 
3234 /*
3235  * Called by worker thread. The aim is to terminate the command
3236  * using whatever means it takes.
3237  * Called with worker lock held.
3238  */
3239 disc_action_t
3240 fct_cmd_terminator(fct_i_local_port_t *iport)
3241 {
3242 	char			info[FCT_INFO_LEN];
3243 	clock_t			endtime;
3244 	fct_i_cmd_t		**ppicmd;
3245 	fct_i_cmd_t		*icmd;
3246 	fct_cmd_t		*cmd;
3247 	fct_local_port_t	*port = iport->iport_port;
3248 	disc_action_t		ret = DISC_ACTION_NO_WORK;
3249 	fct_status_t		abort_ret;
3250 	int			fca_done, fct_done, cmd_implicit = 0;
3251 	int			flags;
3252 	unsigned long long	st;
3253 
3254 	/* Lets Limit each run to 20ms max. */
3255 	endtime = ddi_get_lbolt() + drv_usectohz(20000);
3256 
3257 	/* Start from where we left off last time */
3258 	if (iport->iport_ppicmd_term) {
3259 		ppicmd = iport->iport_ppicmd_term;
3260 		iport->iport_ppicmd_term = NULL;
3261 	} else {
3262 		ppicmd = &iport->iport_abort_queue;
3263 	}
3264 
3265 	/*
3266 	 * Once a command gets on discovery queue, this is the only thread
3267 	 * which can access it. So no need for the lock here.
3268 	 */
3269 	mutex_exit(&iport->iport_worker_lock);
3270 
3271 	while ((icmd = *ppicmd) != NULL) {
3272 		cmd = icmd->icmd_cmd;
3273 
3274 		/* Always remember that cmd->cmd_rp can be NULL */
3275 		if ((icmd->icmd_flags & (ICMD_KNOWN_TO_FCA |
3276 		    ICMD_FCA_ABORT_CALLED)) == ICMD_KNOWN_TO_FCA) {
3277 			atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3278 			if (CMD_HANDLE_VALID(cmd->cmd_handle))
3279 				flags = 0;
3280 			else
3281 				flags = FCT_IOF_FORCE_FCA_DONE;
3282 			abort_ret = port->port_abort_cmd(port, cmd, flags);
3283 			if ((abort_ret != FCT_SUCCESS) &&
3284 			    (abort_ret != FCT_ABORT_SUCCESS) &&
3285 			    (abort_ret != FCT_NOT_FOUND)) {
3286 				if (flags & FCT_IOF_FORCE_FCA_DONE) {
3287 					/*
3288 					 * XXX trigger port fatal,
3289 					 * Abort the termination, and shutdown
3290 					 * svc will trigger fct_cmd_termination
3291 					 * again.
3292 					 */
3293 					(void) snprintf(info, sizeof (info),
3294 					    "fct_cmd_terminator:"
3295 					    " iport-%p, port_abort_cmd with "
3296 					    "FORCE_FCA_DONE failed",
3297 					    (void *)iport);
3298 					(void) fct_port_shutdown(
3299 					    iport->iport_port,
3300 					    STMF_RFLAG_FATAL_ERROR |
3301 					    STMF_RFLAG_RESET, info);
3302 
3303 					mutex_enter(&iport->iport_worker_lock);
3304 					iport->iport_ppicmd_term = ppicmd;
3305 					return (DISC_ACTION_DELAY_RESCAN);
3306 				}
3307 				atomic_and_32(&icmd->icmd_flags,
3308 				    ~ICMD_FCA_ABORT_CALLED);
3309 			} else if ((flags & FCT_IOF_FORCE_FCA_DONE) ||
3310 			    (abort_ret == FCT_ABORT_SUCCESS) ||
3311 			    (abort_ret == FCT_NOT_FOUND)) {
3312 				atomic_and_32(&icmd->icmd_flags,
3313 				    ~ICMD_KNOWN_TO_FCA);
3314 			}
3315 			ret |= DISC_ACTION_DELAY_RESCAN;
3316 		} else if (icmd->icmd_flags & ICMD_IMPLICIT) {
3317 			if (cmd->cmd_type == FCT_CMD_SOL_ELS)
3318 				cmd->cmd_comp_status = FCT_ABORTED;
3319 			atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3320 			cmd_implicit = 1;
3321 		}
3322 		if ((icmd->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
3323 			fca_done = 1;
3324 		else
3325 			fca_done = 0;
3326 		if ((icmd->icmd_flags & ICMD_IN_IRP_QUEUE) == 0)
3327 			fct_done = 1;
3328 		else
3329 			fct_done = 0;
3330 		if ((fca_done || cmd_implicit) && fct_done) {
3331 			mutex_enter(&iport->iport_worker_lock);
3332 			ASSERT(*ppicmd == icmd);
3333 			*ppicmd = (*ppicmd)->icmd_next;
3334 			mutex_exit(&iport->iport_worker_lock);
3335 			if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) ||
3336 			    (cmd->cmd_type == FCT_CMD_RCVD_ABTS)) {
3337 				/* Free the cmd */
3338 				fct_cmd_free(cmd);
3339 			} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
3340 				fct_handle_sol_els_completion(iport, icmd);
3341 				if (icmd->icmd_flags & ICMD_IMPLICIT) {
3342 					if (IS_LOGO_ELS(icmd)) {
3343 						/* IMPLICIT LOGO is special */
3344 						fct_cmd_free(cmd);
3345 					}
3346 				}
3347 			} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3348 				fct_sol_ct_t *ct = ICMD_TO_CT(icmd);
3349 
3350 				/* Tell the caller that we are done */
3351 				atomic_or_32(&icmd->icmd_flags,
3352 				    ICMD_CMD_COMPLETE);
3353 				if (fct_netbuf_to_value(
3354 				    ct->ct_req_payload + 8, 2) == NS_GID_PN) {
3355 					fct_i_remote_port_t *irp;
3356 
3357 					rw_enter(&iport->iport_lock, RW_READER);
3358 					irp = fct_lookup_irp_by_portwwn(iport,
3359 					    ct->ct_req_payload + 16);
3360 
3361 					if (irp) {
3362 						atomic_and_32(&irp->irp_flags,
3363 						    ~IRP_RSCN_QUEUED);
3364 					}
3365 					rw_exit(&iport->iport_lock);
3366 				}
3367 			} else {
3368 				ASSERT(0);
3369 			}
3370 		} else {
3371 			clock_t	timeout_ticks;
3372 			if (port->port_fca_abort_timeout)
3373 				timeout_ticks = drv_usectohz(
3374 				    port->port_fca_abort_timeout*1000);
3375 			else
3376 				/* 10 seconds by default */
3377 				timeout_ticks = drv_usectohz(10 * 1000000);
3378 			if ((ddi_get_lbolt() >
3379 			    (icmd->icmd_start_time+timeout_ticks)) &&
3380 			    iport->iport_state == FCT_STATE_ONLINE) {
3381 				/* timeout, reset the port */
3382 				char cmd_type[10];
3383 				if (cmd->cmd_type == FCT_CMD_RCVD_ELS ||
3384 				    cmd->cmd_type == FCT_CMD_SOL_ELS) {
3385 					fct_els_t *els = cmd->cmd_specific;
3386 					(void) snprintf(cmd_type,
3387 					    sizeof (cmd_type), "%x.%x",
3388 					    cmd->cmd_type,
3389 					    els->els_req_payload[0]);
3390 				} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3391 					fct_sol_ct_t *ct = cmd->cmd_specific;
3392 					(void) snprintf(cmd_type,
3393 					    sizeof (cmd_type), "%x.%02x%02x",
3394 					    cmd->cmd_type,
3395 					    ct->ct_req_payload[8],
3396 					    ct->ct_req_payload[9]);
3397 				} else {
3398 					cmd_type[0] = 0;
3399 				}
3400 				st = cmd->cmd_comp_status;	/* gcc fix */
3401 				(void) snprintf(info, sizeof (info),
3402 				    "fct_cmd_terminator:"
3403 				    " iport-%p, cmd_type(0x%s),"
3404 				    " reason(%llx)", (void *)iport, cmd_type,
3405 				    st);
3406 				(void) fct_port_shutdown(port,
3407 				    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET,
3408 				    info);
3409 			}
3410 			ppicmd = &((*ppicmd)->icmd_next);
3411 		}
3412 
3413 		if (ddi_get_lbolt() > endtime) {
3414 			mutex_enter(&iport->iport_worker_lock);
3415 			iport->iport_ppicmd_term = ppicmd;
3416 			return (DISC_ACTION_DELAY_RESCAN);
3417 		}
3418 	}
3419 	mutex_enter(&iport->iport_worker_lock);
3420 	if (iport->iport_abort_queue)
3421 		return (DISC_ACTION_DELAY_RESCAN);
3422 	if (ret == DISC_ACTION_NO_WORK)
3423 		return (DISC_ACTION_RESCAN);
3424 	return (ret);
3425 }
3426 
3427 /*
3428  * Send a syslog event for adapter port level events.
3429  */
3430 void
3431 fct_log_local_port_event(fct_local_port_t *port, char *subclass)
3432 {
3433 	nvlist_t *attr_list;
3434 	int port_instance;
3435 
3436 	if (!fct_dip)
3437 		return;
3438 	port_instance = ddi_get_instance(fct_dip);
3439 
3440 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3441 	    KM_SLEEP) != DDI_SUCCESS) {
3442 		goto alloc_failed;
3443 	}
3444 
3445 	if (nvlist_add_uint32(attr_list, "instance", port_instance)
3446 	    != DDI_SUCCESS) {
3447 		goto error;
3448 	}
3449 
3450 	if (nvlist_add_byte_array(attr_list, "port-wwn",
3451 	    port->port_pwwn, 8) != DDI_SUCCESS) {
3452 		goto error;
3453 	}
3454 
3455 	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3456 	    subclass, attr_list, NULL, DDI_SLEEP);
3457 
3458 	nvlist_free(attr_list);
3459 	return;
3460 
3461 error:
3462 	nvlist_free(attr_list);
3463 alloc_failed:
3464 	stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3465 	    "Unable to send %s event", subclass);
3466 }
3467 
3468 void
3469 fct_log_remote_port_event(fct_local_port_t *port, char *subclass,
3470     uint8_t *rp_pwwn, uint32_t rp_id)
3471 {
3472 	nvlist_t *attr_list;
3473 	int port_instance;
3474 
3475 	if (!fct_dip)
3476 		return;
3477 	port_instance = ddi_get_instance(fct_dip);
3478 
3479 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3480 	    KM_SLEEP) != DDI_SUCCESS) {
3481 		goto alloc_failed;
3482 	}
3483 
3484 	if (nvlist_add_uint32(attr_list, "instance", port_instance)
3485 	    != DDI_SUCCESS) {
3486 		goto error;
3487 	}
3488 
3489 	if (nvlist_add_byte_array(attr_list, "port-wwn",
3490 	    port->port_pwwn, 8) != DDI_SUCCESS) {
3491 		goto error;
3492 	}
3493 
3494 	if (nvlist_add_byte_array(attr_list, "target-port-wwn",
3495 	    rp_pwwn, 8) != DDI_SUCCESS) {
3496 		goto error;
3497 	}
3498 
3499 	if (nvlist_add_uint32(attr_list, "target-port-id",
3500 	    rp_id) != DDI_SUCCESS) {
3501 		goto error;
3502 	}
3503 
3504 	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3505 	    subclass, attr_list, NULL, DDI_SLEEP);
3506 
3507 	nvlist_free(attr_list);
3508 	return;
3509 
3510 error:
3511 	nvlist_free(attr_list);
3512 alloc_failed:
3513 	stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3514 	    "Unable to send %s event", subclass);
3515 }
3516 
3517 uint64_t
3518 fct_netbuf_to_value(uint8_t *buf, uint8_t nbytes)
3519 {
3520 	uint64_t	ret = 0;
3521 	uint8_t		idx = 0;
3522 
3523 	do {
3524 		ret |= (buf[idx] << (8 * (nbytes -idx - 1)));
3525 	} while (++idx < nbytes);
3526 
3527 	return (ret);
3528 }
3529 
3530 void
3531 fct_value_to_netbuf(uint64_t value, uint8_t *buf, uint8_t nbytes)
3532 {
3533 	uint8_t		idx = 0;
3534 
3535 	for (idx = 0; idx < nbytes; idx++) {
3536 		buf[idx] = 0xFF & (value >> (8 * (nbytes - idx - 1)));
3537 	}
3538 }
3539 
3540 /*
3541  * from_ptr: ptr to uchar_t array of size WWN_SIZE
3542  * to_ptr: char ptr to string of size WWN_SIZE*2+1
3543  */
3544 void
3545 fct_wwn_to_str(char *to_ptr, const uint8_t *from_ptr)
3546 {
3547 	ASSERT(to_ptr != NULL && from_ptr != NULL);
3548 
3549 	(void) sprintf(to_ptr, "%02x%02x%02x%02x%02x%02x%02x%02x",
3550 	    from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
3551 	    from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
3552 }
3553 
3554 static int
3555 fct_update_stats(kstat_t *ks, int rw)
3556 {
3557 	fct_i_local_port_t *iport;
3558 	fct_port_stat_t *port_kstat;
3559 	fct_port_link_status_t stat;
3560 	uint32_t	buf_size = sizeof (stat);
3561 	int		ret;
3562 
3563 	if (rw == KSTAT_WRITE)
3564 		return (EACCES);
3565 
3566 	iport = (fct_i_local_port_t *)ks->ks_private;
3567 	port_kstat = (fct_port_stat_t *)ks->ks_data;
3568 
3569 	if (iport->iport_port->port_info == NULL) {
3570 		return (EIO);
3571 	}
3572 	ret = iport->iport_port->port_info(FC_TGT_PORT_RLS,
3573 	    iport->iport_port, NULL, (uint8_t *)&stat, &buf_size);
3574 	if (ret != STMF_SUCCESS) {
3575 		return (EIO);
3576 	}
3577 
3578 	port_kstat->link_failure_cnt.value.ui32 =
3579 	    stat.LinkFailureCount;
3580 	port_kstat->loss_of_sync_cnt.value.ui32 =
3581 	    stat.LossOfSyncCount;
3582 	port_kstat->loss_of_signals_cnt.value.ui32 =
3583 	    stat.LossOfSignalsCount;
3584 	port_kstat->prim_seq_protocol_err_cnt.value.ui32 =
3585 	    stat.PrimitiveSeqProtocolErrorCount;
3586 	port_kstat->invalid_tx_word_cnt.value.ui32 =
3587 	    stat.InvalidTransmissionWordCount;
3588 	port_kstat->invalid_crc_cnt.value.ui32 =
3589 	    stat.InvalidCRCCount;
3590 
3591 	return (0);
3592 }
3593 
3594 void
3595 fct_init_kstats(fct_i_local_port_t *iport)
3596 {
3597 	kstat_t *ks;
3598 	fct_port_stat_t *port_kstat;
3599 	char	name[256];
3600 
3601 	if (iport->iport_alias)
3602 		(void) sprintf(name, "iport_%s", iport->iport_alias);
3603 	else
3604 		(void) sprintf(name, "iport_%"PRIxPTR"", (uintptr_t)iport);
3605 	ks = kstat_create(FCT_MODULE_NAME, 0, name, "rawdata",
3606 	    KSTAT_TYPE_NAMED, sizeof (fct_port_stat_t) / sizeof (kstat_named_t),
3607 	    0);
3608 
3609 	if (ks == NULL) {
3610 		return;
3611 	}
3612 	port_kstat = (fct_port_stat_t *)ks->ks_data;
3613 
3614 	iport->iport_kstat_portstat = ks;
3615 	kstat_named_init(&port_kstat->link_failure_cnt,
3616 	    "Link_failure_cnt", KSTAT_DATA_UINT32);
3617 	kstat_named_init(&port_kstat->loss_of_sync_cnt,
3618 	    "Loss_of_sync_cnt", KSTAT_DATA_UINT32);
3619 	kstat_named_init(&port_kstat->loss_of_signals_cnt,
3620 	    "Loss_of_signals_cnt", KSTAT_DATA_UINT32);
3621 	kstat_named_init(&port_kstat->prim_seq_protocol_err_cnt,
3622 	    "Prim_seq_protocol_err_cnt", KSTAT_DATA_UINT32);
3623 	kstat_named_init(&port_kstat->invalid_tx_word_cnt,
3624 	    "Invalid_tx_word_cnt", KSTAT_DATA_UINT32);
3625 	kstat_named_init(&port_kstat->invalid_crc_cnt,
3626 	    "Invalid_crc_cnt", KSTAT_DATA_UINT32);
3627 	ks->ks_update = fct_update_stats;
3628 	ks->ks_private = (void *)iport;
3629 	kstat_install(ks);
3630 
3631 }
3632