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