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