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