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 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25/*
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
27 * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
28 */
29/*
30 * Fibre channel Transport Library (fctl)
31 *
32 * Function naming conventions:
33 *		Functions called from ULPs begin with fc_ulp_
34 *		Functions called from FCAs begin with fc_fca_
35 *		Internal functions begin with fctl_
36 *
37 * Fibre channel packet layout:
38 *	  +---------------------+<--------+
39 *	  |			|	  |
40 *	  | ULP Packet private	|	  |
41 *	  |			|	  |
42 *	  +---------------------+	  |
43 *	  |			|---------+
44 *	  |  struct  fc_packet	|---------+
45 *	  |			|	  |
46 *	  +---------------------+<--------+
47 *	  |			|
48 *	  | FCA Packet private	|
49 *	  |			|
50 *	  +---------------------+
51 *
52 * So you  loved  the  ascii  art ?  It's  strongly  desirable	to  cache
53 * allocate the entire packet in one common  place.  So we define a set a
54 * of rules.  In a  contiguous	block of memory,  the top  portion of the
55 * block points to ulp packet  private	area, next follows the	fc_packet
56 * structure used  extensively by all the consumers and what follows this
57 * is the FCA packet private.  Note that given a packet	 structure, it is
58 * possible  to get to the  ULP	 and  FCA  Packet  private  fields  using
59 * ulp_private and fca_private fields (which hold pointers) respectively.
60 *
61 * It should be noted with a grain of salt that ULP Packet  private  size
62 * varies  between two different  ULP types, So this poses a challenge to
63 * compute the correct	size of the whole block on a per port basis.  The
64 * transport  layer  doesn't have a problem in dealing with  FCA   packet
65 * private  sizes as it is the sole  manager of ports  underneath.  Since
66 * it's not a good idea to cache allocate  different  sizes of memory for
67 * different ULPs and have the ability to choose from one of these caches
68 * based on ULP type during every packet  allocation,  the transport some
69 * what	 wisely (?)  hands off this job of cache  allocation  to the ULPs
70 * themselves.
71 *
72 * That means FCAs need to make their  packet  private size  known to the
73 * transport   to  pass	 it  up	 to  the   ULPs.  This	is  done   during
74 * fc_fca_attach().  And the transport passes this size up to ULPs during
75 * fc_ulp_port_attach() of each ULP.
76 *
77 * This	 leaves	 us with  another  possible  question;	How  are  packets
78 * allocated for ELS's started by the transport	 itself ?  Well, the port
79 * driver  during  attach  time, cache	allocates  on a per port basis to
80 * handle ELSs too.
81 */
82
83#include <sys/note.h>
84#include <sys/types.h>
85#include <sys/varargs.h>
86#include <sys/param.h>
87#include <sys/errno.h>
88#include <sys/uio.h>
89#include <sys/buf.h>
90#include <sys/modctl.h>
91#include <sys/open.h>
92#include <sys/kmem.h>
93#include <sys/poll.h>
94#include <sys/conf.h>
95#include <sys/cmn_err.h>
96#include <sys/stat.h>
97#include <sys/ddi.h>
98#include <sys/sunddi.h>
99#include <sys/promif.h>
100#include <sys/byteorder.h>
101#include <sys/fibre-channel/fc.h>
102#include <sys/fibre-channel/impl/fc_ulpif.h>
103#include <sys/fibre-channel/impl/fc_fcaif.h>
104#include <sys/fibre-channel/impl/fctl_private.h>
105#include <sys/fibre-channel/impl/fc_portif.h>
106
107/* These are referenced by fp.c!  */
108int did_table_size = D_ID_HASH_TABLE_SIZE;
109int pwwn_table_size = PWWN_HASH_TABLE_SIZE;
110
111static fc_ulp_module_t	*fctl_ulp_modules;
112static fc_fca_port_t	*fctl_fca_portlist;
113static fc_ulp_list_t	*fctl_ulp_list;
114
115static char fctl_greeting[] =
116	"fctl: %s ULP same type (0x%x) as existing module.\n";
117
118static char *fctl_undefined = "Undefined";
119
120/*
121 * This lock protects the fc_ulp_module_t linked list (i.e. mod_next field)
122 */
123
124static krwlock_t fctl_ulp_lock;
125
126/*
127 * The fctl_mod_ports_lock protects the mod_ports element in the
128 * fc_ulp_ports_t structure
129 */
130
131static krwlock_t fctl_mod_ports_lock;
132
133/*
134 * fctl_port_lock protects the linked list of local port structures
135 * (fctl_fca_portlist).	 When walking the list, this lock must be obtained
136 * prior to any local port locks.
137 */
138
139static kmutex_t fctl_port_lock;
140static kmutex_t	fctl_ulp_list_mutex;
141
142static fctl_nwwn_list_t		*fctl_nwwn_hash_table;
143static kmutex_t			fctl_nwwn_hash_mutex;
144int fctl_nwwn_table_size = NWWN_HASH_TABLE_SIZE;
145
146#if	!defined(lint)
147_NOTE(MUTEX_PROTECTS_DATA(fctl_nwwn_hash_mutex, fctl_nwwn_hash_table))
148_NOTE(MUTEX_PROTECTS_DATA(fctl_ulp_list_mutex, fctl_ulp_list))
149_NOTE(RWLOCK_PROTECTS_DATA(fctl_ulp_lock, ulp_module::mod_next))
150_NOTE(RWLOCK_PROTECTS_DATA(fctl_mod_ports_lock, ulp_module::mod_ports
151    ulp_ports::port_handle))
152_NOTE(DATA_READABLE_WITHOUT_LOCK(ulp_module::mod_info))
153_NOTE(MUTEX_PROTECTS_DATA(ulp_ports::port_mutex, ulp_ports::port_statec
154    ulp_ports::port_dstate))
155#endif /* lint */
156
157#define	FCTL_VERSION		"20090729-1.70"
158#define	FCTL_NAME_VERSION	"SunFC Transport v" FCTL_VERSION
159
160char *fctl_version = FCTL_NAME_VERSION;
161
162extern struct mod_ops mod_miscops;
163
164static struct modlmisc modlmisc = {
165	&mod_miscops,			/* type of module */
166	FCTL_NAME_VERSION		/* Module name */
167};
168
169static struct modlinkage modlinkage = {
170	MODREV_1, (void *)&modlmisc, NULL
171};
172
173static struct bus_ops fctl_fca_busops = {
174	BUSO_REV,
175	nullbusmap,			/* bus_map */
176	NULL,				/* bus_get_intrspec */
177	NULL,				/* bus_add_intrspec */
178	NULL,				/* bus_remove_intrspec */
179	i_ddi_map_fault,		/* bus_map_fault */
180	NULL,				/* bus_dma_map */
181	ddi_dma_allochdl,		/* bus_dma_allochdl */
182	ddi_dma_freehdl,		/* bus_dma_freehdl */
183	ddi_dma_bindhdl,		/* bus_dma_bindhdl */
184	ddi_dma_unbindhdl,		/* bus_unbindhdl */
185	ddi_dma_flush,			/* bus_dma_flush */
186	ddi_dma_win,			/* bus_dma_win */
187	ddi_dma_mctl,			/* bus_dma_ctl */
188	fctl_fca_bus_ctl,		/* bus_ctl */
189	ddi_bus_prop_op,		/* bus_prop_op */
190	NULL,				/* bus_get_eventcookie */
191	NULL,				/* bus_add_eventcall */
192	NULL,				/* bus_remove_event */
193	NULL,				/* bus_post_event */
194	NULL,				/* bus_intr_ctl */
195	NULL,				/* bus_config */
196	NULL,				/* bus_unconfig */
197	NULL,				/* bus_fm_init */
198	NULL,				/* bus_fm_fini */
199	NULL,				/* bus_fm_access_enter */
200	NULL,				/* bus_fm_access_exit */
201	NULL,				/* bus_power */
202	NULL
203};
204
205struct kmem_cache *fctl_job_cache;
206
207static fc_errmap_t fc_errlist [] = {
208	{ FC_FAILURE,		"Operation failed"			},
209	{ FC_SUCCESS,		"Operation success"			},
210	{ FC_CAP_ERROR,		"Capability error"			},
211	{ FC_CAP_FOUND,		"Capability found"			},
212	{ FC_CAP_SETTABLE,	"Capability settable"			},
213	{ FC_UNBOUND,		"Port not bound"			},
214	{ FC_NOMEM,		"No memory"				},
215	{ FC_BADPACKET,		"Bad packet"				},
216	{ FC_OFFLINE,		"Port offline"				},
217	{ FC_OLDPORT,		"Old Port"				},
218	{ FC_NO_MAP,		"No map available"			},
219	{ FC_TRANSPORT_ERROR,	"Transport error"			},
220	{ FC_ELS_FREJECT,	"ELS Frejected"				},
221	{ FC_ELS_PREJECT,	"ELS PRejected"				},
222	{ FC_ELS_BAD,		"Bad ELS request"			},
223	{ FC_ELS_MALFORMED,	"Malformed ELS request"			},
224	{ FC_TOOMANY,		"Too many commands"			},
225	{ FC_UB_BADTOKEN,	"Bad Unsolicited buffer token"		},
226	{ FC_UB_ERROR,		"Unsolicited buffer error"		},
227	{ FC_UB_BUSY,		"Unsolicited buffer busy"		},
228	{ FC_BADULP,		"Bad ULP"				},
229	{ FC_BADTYPE,		"Bad Type"				},
230	{ FC_UNCLAIMED,		"Not Claimed"				},
231	{ FC_ULP_SAMEMODULE,	"Same ULP Module"			},
232	{ FC_ULP_SAMETYPE,	"Same ULP Type"				},
233	{ FC_ABORTED,		"Command Aborted"			},
234	{ FC_ABORT_FAILED,	"Abort Failed"				},
235	{ FC_BADEXCHANGE,	"Bad Exchange"				},
236	{ FC_BADWWN,		"Bad World Wide Name"			},
237	{ FC_BADDEV,		"Bad Device"				},
238	{ FC_BADCMD,		"Bad Command"				},
239	{ FC_BADOBJECT,		"Bad Object"				},
240	{ FC_BADPORT,		"Bad Port"				},
241	{ FC_NOTTHISPORT,	"Not on this Port"			},
242	{ FC_PREJECT,		"Operation Prejected"			},
243	{ FC_FREJECT,		"Operation Frejected"			},
244	{ FC_PBUSY,		"Operation Pbusyed"			},
245	{ FC_FBUSY,		"Operation Fbusyed"			},
246	{ FC_ALREADY,		"Already done"				},
247	{ FC_LOGINREQ,		"PLOGI Required"			},
248	{ FC_RESETFAIL,		"Reset operation failed"		},
249	{ FC_INVALID_REQUEST,	"Invalid Request"			},
250	{ FC_OUTOFBOUNDS,	"Out of Bounds"				},
251	{ FC_TRAN_BUSY,		"Command transport Busy"		},
252	{ FC_STATEC_BUSY,	"State change Busy"			},
253	{ FC_DEVICE_BUSY,	"Port driver is working on this device"	}
254};
255
256fc_pkt_reason_t remote_stop_reasons [] = {
257	{ FC_REASON_ABTS,	"Abort Sequence"	},
258	{ FC_REASON_ABTX,	"Abort Exchange"	},
259	{ FC_REASON_INVALID,	NULL			}
260};
261
262fc_pkt_reason_t general_reasons [] = {
263	{ FC_REASON_HW_ERROR,		"Hardware Error"		},
264	{ FC_REASON_SEQ_TIMEOUT,	"Sequence Timeout"		},
265	{ FC_REASON_ABORTED,		"Aborted"			},
266	{ FC_REASON_ABORT_FAILED,	"Abort Failed"			},
267	{ FC_REASON_NO_CONNECTION,	"No Connection"			},
268	{ FC_REASON_XCHG_DROPPED,	"Exchange Dropped"		},
269	{ FC_REASON_ILLEGAL_FRAME,	"Illegal Frame"			},
270	{ FC_REASON_ILLEGAL_LENGTH,	"Illegal Length"		},
271	{ FC_REASON_UNSUPPORTED,	"Unsuported"			},
272	{ FC_REASON_RX_BUF_TIMEOUT,	"Receive Buffer Timeout"	},
273	{ FC_REASON_FCAL_OPN_FAIL,	"FC AL Open Failed"		},
274	{ FC_REASON_OVERRUN,		"Over run"			},
275	{ FC_REASON_QFULL,		"Queue Full"			},
276	{ FC_REASON_ILLEGAL_REQ,	"Illegal Request",		},
277	{ FC_REASON_PKT_BUSY,		"Busy"				},
278	{ FC_REASON_OFFLINE,		"Offline"			},
279	{ FC_REASON_BAD_XID,		"Bad Exchange Id"		},
280	{ FC_REASON_XCHG_BSY,		"Exchange Busy"			},
281	{ FC_REASON_NOMEM,		"No Memory"			},
282	{ FC_REASON_BAD_SID,		"Bad S_ID"			},
283	{ FC_REASON_NO_SEQ_INIT,	"No Sequence Initiative"	},
284	{ FC_REASON_DIAG_BUSY,		"Diagnostic Busy"		},
285	{ FC_REASON_DMA_ERROR,		"DMA Error"			},
286	{ FC_REASON_CRC_ERROR,		"CRC Error"			},
287	{ FC_REASON_ABORT_TIMEOUT,	"Abort Timeout"			},
288	{ FC_REASON_FCA_UNIQUE,		"FCA Unique"			},
289	{ FC_REASON_INVALID,		NULL				}
290};
291
292fc_pkt_reason_t rjt_reasons [] = {
293	{ FC_REASON_INVALID_D_ID,	"Invalid D_ID"			},
294	{ FC_REASON_INVALID_S_ID,	"Invalid S_ID"			},
295	{ FC_REASON_TEMP_UNAVAILABLE,	"Temporarily Unavailable"	},
296	{ FC_REASON_PERM_UNAVAILABLE,	"Permamnently Unavailable"	},
297	{ FC_REASON_CLASS_NOT_SUPP,	"Class Not Supported",		},
298	{ FC_REASON_DELIMTER_USAGE_ERROR,
299	    "Delimeter Usage Error"		},
300	{ FC_REASON_TYPE_NOT_SUPP,	"Type Not Supported"		},
301	{ FC_REASON_INVALID_LINK_CTRL,	"Invalid Link Control"		},
302	{ FC_REASON_INVALID_R_CTL,	"Invalid R_CTL"			},
303	{ FC_REASON_INVALID_F_CTL,	"Invalid F_CTL"			},
304	{ FC_REASON_INVALID_OX_ID,	"Invalid OX_ID"			},
305	{ FC_REASON_INVALID_RX_ID,	"Invalid RX_ID"			},
306	{ FC_REASON_INVALID_SEQ_ID,	"Invalid Sequence ID"		},
307	{ FC_REASON_INVALID_DF_CTL,	"Invalid DF_CTL"		},
308	{ FC_REASON_INVALID_SEQ_CNT,	"Invalid Sequence count"	},
309	{ FC_REASON_INVALID_PARAM,	"Invalid Parameter"		},
310	{ FC_REASON_EXCH_ERROR,		"Exchange Error"		},
311	{ FC_REASON_PROTOCOL_ERROR,	"Protocol Error"		},
312	{ FC_REASON_INCORRECT_LENGTH,	"Incorrect Length"		},
313	{ FC_REASON_UNEXPECTED_ACK,	"Unexpected Ack"		},
314	{ FC_REASON_UNEXPECTED_LR,	"Unexpected Link reset"		},
315	{ FC_REASON_LOGIN_REQUIRED,	"Login Required"		},
316	{ FC_REASON_EXCESSIVE_SEQS,	"Excessive Sequences"
317	    " Attempted"			},
318	{ FC_REASON_EXCH_UNABLE,	"Exchange incapable"		},
319	{ FC_REASON_ESH_NOT_SUPP,	"Expiration Security Header "
320	    "Not Supported"			},
321	{ FC_REASON_NO_FABRIC_PATH,	"No Fabric Path"		},
322	{ FC_REASON_VENDOR_UNIQUE,	"Vendor Unique"			},
323	{ FC_REASON_INVALID,		NULL				}
324};
325
326fc_pkt_reason_t n_port_busy_reasons [] = {
327	{ FC_REASON_PHYSICAL_BUSY,		"Physical Busy"		},
328	{ FC_REASON_N_PORT_RESOURCE_BSY,	"Resource Busy"		},
329	{ FC_REASON_N_PORT_VENDOR_UNIQUE,	"Vendor Unique"		},
330	{ FC_REASON_INVALID,			NULL			}
331};
332
333fc_pkt_reason_t f_busy_reasons [] = {
334	{ FC_REASON_FABRIC_BSY,		"Fabric Busy"			},
335	{ FC_REASON_N_PORT_BSY,		"N_Port Busy"			},
336	{ FC_REASON_INVALID,		NULL				}
337};
338
339fc_pkt_reason_t ls_ba_rjt_reasons [] = {
340	{ FC_REASON_INVALID_LA_CODE,	"Invalid Link Application Code"	},
341	{ FC_REASON_LOGICAL_ERROR,	"Logical Error"			},
342	{ FC_REASON_LOGICAL_BSY,	"Logical Busy"			},
343	{ FC_REASON_PROTOCOL_ERROR_RJT,	"Protocol Error Reject"		},
344	{ FC_REASON_CMD_UNABLE,		"Unable to Perform Command"	},
345	{ FC_REASON_CMD_UNSUPPORTED,	"Unsupported Command"		},
346	{ FC_REASON_VU_RJT,		"Vendor Unique"			},
347	{ FC_REASON_INVALID,		NULL				}
348};
349
350fc_pkt_reason_t fs_rjt_reasons [] = {
351	{ FC_REASON_FS_INVALID_CMD,	"Invalid Command"		},
352	{ FC_REASON_FS_INVALID_VER,	"Invalid Version"		},
353	{ FC_REASON_FS_LOGICAL_ERR,	"Logical Error"			},
354	{ FC_REASON_FS_INVALID_IUSIZE,	"Invalid IU Size"		},
355	{ FC_REASON_FS_LOGICAL_BUSY,	"Logical Busy"			},
356	{ FC_REASON_FS_PROTOCOL_ERR,	"Protocol Error"		},
357	{ FC_REASON_FS_CMD_UNABLE,	"Unable to Perform Command"	},
358	{ FC_REASON_FS_CMD_UNSUPPORTED,	"Unsupported Command"		},
359	{ FC_REASON_FS_VENDOR_UNIQUE,	"Vendor Unique"			},
360	{ FC_REASON_INVALID,		NULL				}
361};
362
363fc_pkt_action_t	n_port_busy_actions [] = {
364	{ FC_ACTION_SEQ_TERM_RETRY,	"Retry terminated Sequence"	},
365	{ FC_ACTION_SEQ_ACTIVE_RETRY,	"Retry Active Sequence"		},
366	{ FC_REASON_INVALID,		NULL				}
367};
368
369fc_pkt_action_t rjt_timeout_actions [] = {
370	{ FC_ACTION_RETRYABLE,		"Retryable"			},
371	{ FC_ACTION_NON_RETRYABLE,	"Non Retryable"			},
372	{ FC_REASON_INVALID,		NULL				}
373};
374
375fc_pkt_expln_t ba_rjt_explns [] = {
376	{ FC_EXPLN_NONE,		"No Explanation"		},
377	{ FC_EXPLN_INVALID_OX_RX_ID,	"Invalid X_ID"			},
378	{ FC_EXPLN_SEQ_ABORTED,		"Sequence Aborted"		},
379	{ FC_EXPLN_INVALID,		NULL				}
380};
381
382fc_pkt_error_t fc_pkt_errlist[] = {
383	{
384		FC_PKT_SUCCESS,
385		"Operation Success",
386		NULL,
387		NULL,
388		NULL
389	},
390	{	FC_PKT_REMOTE_STOP,
391	    "Remote Stop",
392	    remote_stop_reasons,
393	    NULL,
394	    NULL
395	},
396	{
397		FC_PKT_LOCAL_RJT,
398		"Local Reject",
399		general_reasons,
400		rjt_timeout_actions,
401		NULL
402	},
403	{
404		FC_PKT_NPORT_RJT,
405		"N_Port Reject",
406		rjt_reasons,
407		rjt_timeout_actions,
408		NULL
409	},
410	{
411		FC_PKT_FABRIC_RJT,
412		"Fabric Reject",
413		rjt_reasons,
414		rjt_timeout_actions,
415		NULL
416	},
417	{
418		FC_PKT_LOCAL_BSY,
419		"Local Busy",
420		general_reasons,
421		NULL,
422		NULL,
423	},
424	{
425		FC_PKT_TRAN_BSY,
426		"Transport Busy",
427		general_reasons,
428		NULL,
429		NULL,
430	},
431	{
432		FC_PKT_NPORT_BSY,
433		"N_Port Busy",
434		n_port_busy_reasons,
435		n_port_busy_actions,
436		NULL
437	},
438	{
439		FC_PKT_FABRIC_BSY,
440		"Fabric Busy",
441		f_busy_reasons,
442		NULL,
443		NULL,
444	},
445	{
446		FC_PKT_LS_RJT,
447		"Link Service Reject",
448		ls_ba_rjt_reasons,
449		NULL,
450		NULL,
451	},
452	{
453		FC_PKT_BA_RJT,
454		"Basic Reject",
455		ls_ba_rjt_reasons,
456		NULL,
457		ba_rjt_explns,
458	},
459	{
460		FC_PKT_TIMEOUT,
461		"Timeout",
462		general_reasons,
463		rjt_timeout_actions,
464		NULL
465	},
466	{
467		FC_PKT_FS_RJT,
468		"Fabric Switch Reject",
469		fs_rjt_reasons,
470		NULL,
471		NULL
472	},
473	{
474		FC_PKT_TRAN_ERROR,
475		"Packet Transport error",
476		general_reasons,
477		NULL,
478		NULL
479	},
480	{
481		FC_PKT_FAILURE,
482		"Packet Failure",
483		general_reasons,
484		NULL,
485		NULL
486	},
487	{
488		FC_PKT_PORT_OFFLINE,
489		"Port Offline",
490		NULL,
491		NULL,
492		NULL
493	},
494	{
495		FC_PKT_ELS_IN_PROGRESS,
496		"ELS is in Progress",
497		NULL,
498		NULL,
499		NULL
500	}
501};
502
503int
504_init()
505{
506	int rval;
507
508	rw_init(&fctl_ulp_lock, NULL, RW_DRIVER, NULL);
509	rw_init(&fctl_mod_ports_lock, NULL, RW_DRIVER, NULL);
510	mutex_init(&fctl_port_lock, NULL, MUTEX_DRIVER, NULL);
511	mutex_init(&fctl_nwwn_hash_mutex, NULL, MUTEX_DRIVER, NULL);
512
513	fctl_nwwn_hash_table = kmem_zalloc(sizeof (*fctl_nwwn_hash_table) *
514	    fctl_nwwn_table_size, KM_SLEEP);
515
516	fctl_ulp_modules = NULL;
517	fctl_fca_portlist = NULL;
518
519	fctl_job_cache = kmem_cache_create("fctl_cache",
520	    sizeof (job_request_t), 8, fctl_cache_constructor,
521	    fctl_cache_destructor, NULL, NULL, NULL, 0);
522
523	if (fctl_job_cache == NULL) {
524		kmem_free(fctl_nwwn_hash_table,
525		    sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
526		mutex_destroy(&fctl_nwwn_hash_mutex);
527		mutex_destroy(&fctl_port_lock);
528		rw_destroy(&fctl_ulp_lock);
529		rw_destroy(&fctl_mod_ports_lock);
530		return (ENOMEM);
531	}
532
533	if ((rval = mod_install(&modlinkage)) != 0) {
534		kmem_cache_destroy(fctl_job_cache);
535		kmem_free(fctl_nwwn_hash_table,
536		    sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
537		mutex_destroy(&fctl_nwwn_hash_mutex);
538		mutex_destroy(&fctl_port_lock);
539		rw_destroy(&fctl_ulp_lock);
540		rw_destroy(&fctl_mod_ports_lock);
541	}
542
543	return (rval);
544}
545
546
547/*
548 * The mod_uninstall code doesn't call _fini when
549 * there is living dependent module on fctl. So
550 * there is no need to be extra careful here ?
551 */
552int
553_fini()
554{
555	int rval;
556
557	if ((rval = mod_remove(&modlinkage)) != 0) {
558		return (rval);
559	}
560
561	kmem_cache_destroy(fctl_job_cache);
562	kmem_free(fctl_nwwn_hash_table,
563	    sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
564	mutex_destroy(&fctl_nwwn_hash_mutex);
565	mutex_destroy(&fctl_port_lock);
566	rw_destroy(&fctl_ulp_lock);
567	rw_destroy(&fctl_mod_ports_lock);
568
569	return (rval);
570}
571
572
573int
574_info(struct modinfo *modinfo_p)
575{
576	return (mod_info(&modlinkage, modinfo_p));
577}
578
579
580/* ARGSUSED */
581static int
582fctl_cache_constructor(void *buf, void *cdarg, int kmflag)
583{
584	job_request_t *job = (job_request_t *)buf;
585
586	mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
587	sema_init(&job->job_fctl_sema, 0, NULL, SEMA_DEFAULT, NULL);
588	sema_init(&job->job_port_sema, 0, NULL, SEMA_DEFAULT, NULL);
589
590	return (0);
591}
592
593
594/* ARGSUSED */
595static void
596fctl_cache_destructor(void *buf, void *cdarg)
597{
598	job_request_t *job = (job_request_t *)buf;
599
600	sema_destroy(&job->job_fctl_sema);
601	sema_destroy(&job->job_port_sema);
602	mutex_destroy(&job->job_mutex);
603}
604
605
606/*
607 * fc_ulp_add:
608 *		Add a ULP module
609 *
610 * Return Codes:
611 *		FC_ULP_SAMEMODULE
612 *		FC_SUCCESS
613 *		FC_FAILURE
614 *
615 *   fc_ulp_add	 prints	 a warning message if there is	already a
616 *   similar ULP type  attached and this is unlikely to change as
617 *   we trudge along.  Further, this  function	returns a failure
618 *   code if the same  module  attempts to add more than once for
619 *   the same FC-4 type.
620 */
621int
622fc_ulp_add(fc_ulp_modinfo_t *ulp_info)
623{
624	fc_ulp_module_t *mod;
625	fc_ulp_module_t *prev;
626	job_request_t	*job;
627	fc_ulp_list_t	*new;
628	fc_fca_port_t	*fca_port;
629	int		ntry = 0;
630
631	ASSERT(ulp_info != NULL);
632
633	/*
634	 * Make sure ulp_rev matches fctl version.
635	 * Whenever non-private data structure or non-static interface changes,
636	 * we should use an increased FCTL_ULP_MODREV_# number here and in all
637	 * ulps to prevent version mismatch.
638	 */
639	if (ulp_info->ulp_rev != FCTL_ULP_MODREV_4) {
640		cmn_err(CE_WARN, "fctl: ULP %s version mismatch;"
641		    " ULP %s would not be loaded", ulp_info->ulp_name,
642		    ulp_info->ulp_name);
643		return (FC_BADULP);
644	}
645
646	new = kmem_zalloc(sizeof (*new), KM_SLEEP);
647	ASSERT(new != NULL);
648
649	mutex_enter(&fctl_ulp_list_mutex);
650	new->ulp_info = ulp_info;
651	if (fctl_ulp_list != NULL) {
652		new->ulp_next = fctl_ulp_list;
653	}
654	fctl_ulp_list = new;
655	mutex_exit(&fctl_ulp_list_mutex);
656
657	while (rw_tryenter(&fctl_ulp_lock, RW_WRITER) == 0) {
658		delay(drv_usectohz(1000000));
659		if (ntry++ > FC_ULP_ADD_RETRY_COUNT) {
660			fc_ulp_list_t	*list;
661			fc_ulp_list_t	*last;
662			mutex_enter(&fctl_ulp_list_mutex);
663			for (last = NULL, list = fctl_ulp_list; list != NULL;
664			    list = list->ulp_next) {
665				if (list->ulp_info == ulp_info) {
666					break;
667				}
668				last = list;
669			}
670
671			if (list) {
672				if (last) {
673					last->ulp_next = list->ulp_next;
674				} else {
675					fctl_ulp_list = list->ulp_next;
676				}
677				kmem_free(list, sizeof (*list));
678			}
679			mutex_exit(&fctl_ulp_list_mutex);
680			cmn_err(CE_WARN, "fctl: ULP %s unable to load",
681			    ulp_info->ulp_name);
682			return (FC_FAILURE);
683		}
684	}
685
686	for (mod = fctl_ulp_modules, prev = NULL; mod; mod = mod->mod_next) {
687		ASSERT(mod->mod_info != NULL);
688
689		if (ulp_info == mod->mod_info &&
690		    ulp_info->ulp_type == mod->mod_info->ulp_type) {
691			rw_exit(&fctl_ulp_lock);
692			return (FC_ULP_SAMEMODULE);
693		}
694
695		if (ulp_info->ulp_type == mod->mod_info->ulp_type) {
696			cmn_err(CE_NOTE, fctl_greeting, ulp_info->ulp_name,
697			    ulp_info->ulp_type);
698		}
699		prev = mod;
700	}
701
702	mod = kmem_zalloc(sizeof (*mod), KM_SLEEP);
703	mod->mod_info = ulp_info;
704	mod->mod_next = NULL;
705
706	if (prev) {
707		prev->mod_next = mod;
708	} else {
709		fctl_ulp_modules = mod;
710	}
711
712	/*
713	 * Schedule a job to each port's job_handler
714	 * thread to attach their ports with this ULP.
715	 */
716	mutex_enter(&fctl_port_lock);
717	for (fca_port = fctl_fca_portlist; fca_port != NULL;
718	    fca_port = fca_port->port_next) {
719		job = fctl_alloc_job(JOB_ATTACH_ULP, JOB_TYPE_FCTL_ASYNC,
720		    NULL, NULL, KM_SLEEP);
721
722		fctl_enque_job(fca_port->port_handle, job);
723	}
724	mutex_exit(&fctl_port_lock);
725
726	rw_exit(&fctl_ulp_lock);
727
728	return (FC_SUCCESS);
729}
730
731
732/*
733 * fc_ulp_remove
734 *	Remove a ULP module
735 *
736 * A misbehaving ULP may call this routine while I/Os are in progress.
737 * Currently there is no mechanism to detect it to fail such a request.
738 *
739 * Return Codes:
740 *		FC_SUCCESS
741 *		FC_FAILURE
742 */
743int
744fc_ulp_remove(fc_ulp_modinfo_t *ulp_info)
745{
746	fc_ulp_module_t *mod;
747	fc_ulp_list_t	*list;
748	fc_ulp_list_t	*last;
749	fc_ulp_module_t *prev;
750
751	mutex_enter(&fctl_ulp_list_mutex);
752
753	for (last = NULL, list = fctl_ulp_list; list != NULL;
754	    list = list->ulp_next) {
755		if (list->ulp_info == ulp_info) {
756			break;
757		}
758		last = list;
759	}
760
761	if (list) {
762		if (last) {
763			last->ulp_next = list->ulp_next;
764		} else {
765			fctl_ulp_list = list->ulp_next;
766		}
767		kmem_free(list, sizeof (*list));
768	}
769
770	mutex_exit(&fctl_ulp_list_mutex);
771
772	rw_enter(&fctl_ulp_lock, RW_WRITER);
773
774	for (mod = fctl_ulp_modules, prev = NULL; mod != NULL;
775	    mod = mod->mod_next) {
776		if (mod->mod_info == ulp_info) {
777			break;
778		}
779		prev = mod;
780	}
781
782	if (mod) {
783		fc_ulp_ports_t *next;
784
785		if (prev) {
786			prev->mod_next = mod->mod_next;
787		} else {
788			fctl_ulp_modules = mod->mod_next;
789		}
790
791		rw_enter(&fctl_mod_ports_lock, RW_WRITER);
792
793		while ((next = mod->mod_ports) != NULL) {
794			mod->mod_ports = next->port_next;
795			fctl_dealloc_ulp_port(next);
796		}
797
798		rw_exit(&fctl_mod_ports_lock);
799		rw_exit(&fctl_ulp_lock);
800
801		kmem_free(mod, sizeof (*mod));
802
803		return (FC_SUCCESS);
804	}
805	rw_exit(&fctl_ulp_lock);
806
807	return (FC_FAILURE);
808}
809
810
811/*
812 * The callers typically cache allocate the packet, complete the
813 * DMA setup for pkt_cmd and pkt_resp fields of the packet and
814 * call this function to see if the FCA is interested in doing
815 * its own intialization. For example, socal may like to initialize
816 * the soc_hdr which is pointed to by the pkt_fca_private field
817 * and sitting right below fc_packet_t in memory.
818 *
819 * The caller is required to ensure that pkt_pd is populated with the
820 * handle that it was given when the transport notified it about the
821 * device this packet is associated with.  If there is no associated
822 * device, pkt_pd must be set to NULL.	A non-NULL pkt_pd will cause an
823 * increment of the reference count for said pd.  When the packet is freed,
824 * the reference count will be decremented.  This reference count, in
825 * combination with the PD_GIVEN_TO_ULPS flag guarantees that the pd
826 * will not wink out of existence while there is a packet outstanding.
827 *
828 * This function and fca_init_pkt must not perform any operations that
829 * would result in a call back to the ULP, as the ULP may be required
830 * to hold a mutex across this call to ensure that the pd in question
831 * won't go away prior the call to fc_ulp_transport.
832 *
833 * ULPs are responsible for using the handles they are given during state
834 * change callback processing in a manner that ensures consistency.  That
835 * is, they must be aware that they could be processing a state change
836 * notification that tells them the device associated with a particular
837 * handle has gone away at the same time they are being asked to
838 * initialize a packet using that handle. ULPs must therefore ensure
839 * that their state change processing and packet initialization code
840 * paths are sufficiently synchronized to avoid the use of an
841 * invalidated handle in any fc_packet_t struct that is passed to the
842 * fc_ulp_init_packet() function.
843 */
844int
845fc_ulp_init_packet(opaque_t port_handle, fc_packet_t *pkt, int sleep)
846{
847	int rval;
848	fc_local_port_t *port = port_handle;
849	fc_remote_port_t *pd;
850
851	ASSERT(pkt != NULL);
852
853	pd = pkt->pkt_pd;
854
855	/* Call the FCA driver's fca_init_pkt entry point function. */
856	rval = port->fp_fca_tran->fca_init_pkt(port->fp_fca_handle, pkt, sleep);
857
858	if ((rval == FC_SUCCESS) && (pd != NULL)) {
859		/*
860		 * A !NULL pd here must still be a valid
861		 * reference to the fc_remote_port_t.
862		 */
863		mutex_enter(&pd->pd_mutex);
864		ASSERT(pd->pd_ref_count >= 0);
865		pd->pd_ref_count++;
866		mutex_exit(&pd->pd_mutex);
867	}
868
869	return (rval);
870}
871
872
873/*
874 * This function is called before destroying the cache allocated
875 * fc_packet to free up (and uninitialize) any resource specially
876 * allocated by the FCA driver during tran_init_pkt().
877 *
878 * If the pkt_pd field in the given fc_packet_t struct is not NULL, then
879 * the pd_ref_count reference count is decremented for the indicated
880 * fc_remote_port_t struct.
881 */
882int
883fc_ulp_uninit_packet(opaque_t port_handle, fc_packet_t *pkt)
884{
885	int rval;
886	fc_local_port_t *port = port_handle;
887	fc_remote_port_t *pd;
888
889	ASSERT(pkt != NULL);
890
891	pd = pkt->pkt_pd;
892
893	/* Call the FCA driver's fca_un_init_pkt entry point function */
894	rval = port->fp_fca_tran->fca_un_init_pkt(port->fp_fca_handle, pkt);
895
896	if ((rval == FC_SUCCESS) && (pd != NULL)) {
897		mutex_enter(&pd->pd_mutex);
898
899		ASSERT(pd->pd_ref_count > 0);
900		pd->pd_ref_count--;
901
902		/*
903		 * If at this point the state of this fc_remote_port_t
904		 * struct is PORT_DEVICE_INVALID, it probably means somebody
905		 * is cleaning up old (e.g. retried) packets. If the
906		 * pd_ref_count has also dropped to zero, it's time to
907		 * deallocate this fc_remote_port_t struct.
908		 */
909		if (pd->pd_state == PORT_DEVICE_INVALID &&
910		    pd->pd_ref_count == 0) {
911			fc_remote_node_t *node = pd->pd_remote_nodep;
912
913			mutex_exit(&pd->pd_mutex);
914
915			/*
916			 * Also deallocate the associated fc_remote_node_t
917			 * struct if it has no other associated
918			 * fc_remote_port_t structs.
919			 */
920			if ((fctl_destroy_remote_port(port, pd) == 0) &&
921			    (node != NULL)) {
922				fctl_destroy_remote_node(node);
923			}
924			return (rval);
925		}
926
927		mutex_exit(&pd->pd_mutex);
928	}
929
930	return (rval);
931}
932
933
934int
935fc_ulp_getportmap(opaque_t port_handle, fc_portmap_t **map, uint32_t *len,
936    int flag)
937{
938	int		job_code;
939	fc_local_port_t *port;
940	job_request_t	*job;
941	fc_portmap_t	*tmp_map;
942	uint32_t	tmp_len;
943	fc_portmap_t	*change_list = NULL;
944	uint32_t	listlen = 0;
945
946	port = port_handle;
947
948	mutex_enter(&port->fp_mutex);
949	if (port->fp_statec_busy) {
950		mutex_exit(&port->fp_mutex);
951		return (FC_STATEC_BUSY);
952	}
953
954	if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
955		mutex_exit(&port->fp_mutex);
956		return (FC_OFFLINE);
957	}
958
959	if (port->fp_dev_count && (port->fp_dev_count ==
960	    port->fp_total_devices)) {
961		mutex_exit(&port->fp_mutex);
962		fctl_fillout_map(port, &change_list, &listlen, 1, 1, 0);
963		if (listlen > *len) {
964			tmp_map = (fc_portmap_t *)kmem_zalloc(
965			    listlen * sizeof (fc_portmap_t), KM_NOSLEEP);
966			if (tmp_map == NULL) {
967				return (FC_NOMEM);
968			}
969			if (*map) {
970				kmem_free(*map, (*len) * sizeof (fc_portmap_t));
971			}
972			*map = tmp_map;
973		}
974		if (change_list) {
975			bcopy(change_list, *map,
976			    listlen * sizeof (fc_portmap_t));
977			kmem_free(change_list, listlen * sizeof (fc_portmap_t));
978		}
979		*len = listlen;
980	} else {
981		mutex_exit(&port->fp_mutex);
982
983		switch (flag) {
984		case FC_ULP_PLOGI_DONTCARE:
985			job_code = JOB_PORT_GETMAP;
986			break;
987
988		case FC_ULP_PLOGI_PRESERVE:
989			job_code = JOB_PORT_GETMAP_PLOGI_ALL;
990			break;
991
992		default:
993			return (FC_INVALID_REQUEST);
994		}
995		/*
996		 * Submit a job request to the job handler
997		 * thread to get the map and wait
998		 */
999		job = fctl_alloc_job(job_code, 0, NULL, NULL, KM_SLEEP);
1000		job->job_private = (opaque_t)map;
1001		job->job_arg = (opaque_t)len;
1002		fctl_enque_job(port, job);
1003
1004		fctl_jobwait(job);
1005		/*
1006		 * The result of the last I/O operation is
1007		 * in job_code. We don't care to look at it
1008		 * Rather we look at the number of devices
1009		 * that are found to fill out the map for
1010		 * ULPs.
1011		 */
1012		fctl_dealloc_job(job);
1013	}
1014
1015	/*
1016	 * If we're here, we're returning a map to the caller, which means
1017	 * we'd better make sure every pd in that map has the
1018	 * PD_GIVEN_TO_ULPS flag set.
1019	 */
1020
1021	tmp_len = *len;
1022	tmp_map = *map;
1023
1024	while (tmp_len-- != 0) {
1025		if (tmp_map->map_state != PORT_DEVICE_INVALID) {
1026			fc_remote_port_t *pd =
1027			    (fc_remote_port_t *)tmp_map->map_pd;
1028			mutex_enter(&pd->pd_mutex);
1029			pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1030			mutex_exit(&pd->pd_mutex);
1031		}
1032		tmp_map++;
1033	}
1034
1035	return (FC_SUCCESS);
1036}
1037
1038
1039int
1040fc_ulp_login(opaque_t port_handle, fc_packet_t **ulp_pkt, uint32_t listlen)
1041{
1042	int			rval = FC_SUCCESS;
1043	int			job_flags;
1044	uint32_t		count;
1045	fc_packet_t		**tmp_array;
1046	job_request_t		*job;
1047	fc_local_port_t		*port = port_handle;
1048	fc_ulp_rscn_info_t	*rscnp =
1049	    (fc_ulp_rscn_info_t *)(ulp_pkt[0])->pkt_ulp_rscn_infop;
1050
1051	/*
1052	 * If the port is OFFLINE, or if the port driver is
1053	 * being SUSPENDED/PM_SUSPENDED/DETACHED, block all
1054	 * PLOGI operations
1055	 */
1056	mutex_enter(&port->fp_mutex);
1057	if (port->fp_statec_busy) {
1058		mutex_exit(&port->fp_mutex);
1059		return (FC_STATEC_BUSY);
1060	}
1061
1062	if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
1063	    (port->fp_soft_state &
1064	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1065		mutex_exit(&port->fp_mutex);
1066		return (FC_OFFLINE);
1067	}
1068
1069	/*
1070	 * If the rscn count in the packet is not the same as the rscn count
1071	 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1072	 */
1073	if ((rscnp != NULL) &&
1074	    (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1075	    (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1076		mutex_exit(&port->fp_mutex);
1077		return (FC_DEVICE_BUSY_NEW_RSCN);
1078	}
1079
1080	mutex_exit(&port->fp_mutex);
1081
1082	tmp_array = kmem_zalloc(sizeof (*tmp_array) * listlen, KM_SLEEP);
1083	for (count = 0; count < listlen; count++) {
1084		tmp_array[count] = ulp_pkt[count];
1085	}
1086
1087	job_flags = ((ulp_pkt[0]->pkt_tran_flags) & FC_TRAN_NO_INTR)
1088	    ? 0 : JOB_TYPE_FCTL_ASYNC;
1089
1090#ifdef	DEBUG
1091	{
1092		int next;
1093		int count;
1094		int polled;
1095
1096		polled = ((ulp_pkt[0]->pkt_tran_flags) &
1097		    FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
1098
1099		for (count = 0; count < listlen; count++) {
1100			next = ((ulp_pkt[count]->pkt_tran_flags)
1101			    & FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
1102			ASSERT(next == polled);
1103		}
1104	}
1105#endif
1106
1107	job = fctl_alloc_job(JOB_PLOGI_GROUP, job_flags, NULL, NULL, KM_SLEEP);
1108	job->job_ulp_pkts = tmp_array;
1109	job->job_ulp_listlen = listlen;
1110
1111	while (listlen--) {
1112		fc_packet_t *pkt;
1113
1114		pkt = tmp_array[listlen];
1115		if (pkt->pkt_pd == NULL) {
1116			pkt->pkt_state = FC_PKT_SUCCESS;
1117			continue;
1118		}
1119
1120		mutex_enter(&pkt->pkt_pd->pd_mutex);
1121		if (pkt->pkt_pd->pd_flags == PD_ELS_IN_PROGRESS ||
1122		    pkt->pkt_pd->pd_flags == PD_ELS_MARK) {
1123			/*
1124			 * Set the packet state and let the port
1125			 * driver call the completion routine
1126			 * from its thread
1127			 */
1128			mutex_exit(&pkt->pkt_pd->pd_mutex);
1129			pkt->pkt_state = FC_PKT_ELS_IN_PROGRESS;
1130			continue;
1131		}
1132
1133		if (pkt->pkt_pd->pd_state == PORT_DEVICE_INVALID ||
1134		    pkt->pkt_pd->pd_type == PORT_DEVICE_OLD) {
1135			mutex_exit(&pkt->pkt_pd->pd_mutex);
1136			pkt->pkt_state = FC_PKT_LOCAL_RJT;
1137			continue;
1138		}
1139		mutex_exit(&pkt->pkt_pd->pd_mutex);
1140		pkt->pkt_state = FC_PKT_SUCCESS;
1141	}
1142
1143	fctl_enque_job(port, job);
1144
1145	if (!(job_flags & JOB_TYPE_FCTL_ASYNC)) {
1146		fctl_jobwait(job);
1147		rval = job->job_result;
1148		fctl_dealloc_job(job);
1149	}
1150
1151	return (rval);
1152}
1153
1154
1155opaque_t
1156fc_ulp_get_remote_port(opaque_t port_handle, la_wwn_t *pwwn, int *error,
1157    int create)
1158{
1159	fc_local_port_t		*port;
1160	job_request_t		*job;
1161	fc_remote_port_t	*pd;
1162
1163	port = port_handle;
1164	pd = fctl_get_remote_port_by_pwwn(port, pwwn);
1165
1166	if (pd != NULL) {
1167		*error = FC_SUCCESS;
1168		/*
1169		 * A ULP now knows about this pd, so mark it
1170		 */
1171		mutex_enter(&pd->pd_mutex);
1172		pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1173		mutex_exit(&pd->pd_mutex);
1174		return (pd);
1175	}
1176
1177	mutex_enter(&port->fp_mutex);
1178	if (FC_IS_TOP_SWITCH(port->fp_topology) && create) {
1179		uint32_t	d_id;
1180		fctl_ns_req_t	*ns_cmd;
1181
1182		mutex_exit(&port->fp_mutex);
1183
1184		job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
1185
1186		if (job == NULL) {
1187			*error = FC_NOMEM;
1188			return (pd);
1189		}
1190
1191		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
1192		    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
1193		    0, KM_SLEEP);
1194
1195		if (ns_cmd == NULL) {
1196			fctl_dealloc_job(job);
1197			*error = FC_NOMEM;
1198			return (pd);
1199		}
1200		ns_cmd->ns_cmd_code = NS_GID_PN;
1201		((ns_req_gid_pn_t *)(ns_cmd->ns_cmd_buf))->pwwn = *pwwn;
1202
1203		job->job_result = FC_SUCCESS;
1204		job->job_private = (void *)ns_cmd;
1205		job->job_counter = 1;
1206		fctl_enque_job(port, job);
1207		fctl_jobwait(job);
1208
1209		if (job->job_result != FC_SUCCESS) {
1210			*error = job->job_result;
1211			fctl_free_ns_cmd(ns_cmd);
1212			fctl_dealloc_job(job);
1213			return (pd);
1214		}
1215		d_id = ((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.port_id;
1216		fctl_free_ns_cmd(ns_cmd);
1217
1218		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
1219		    sizeof (ns_resp_gan_t), 0, FCTL_NS_CREATE_DEVICE,
1220		    KM_SLEEP);
1221		ASSERT(ns_cmd != NULL);
1222
1223		ns_cmd->ns_gan_max = 1;
1224		ns_cmd->ns_cmd_code = NS_GA_NXT;
1225		ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
1226		((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = d_id - 1;
1227		((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
1228
1229		job->job_result = FC_SUCCESS;
1230		job->job_private = (void *)ns_cmd;
1231		job->job_counter = 1;
1232		fctl_enque_job(port, job);
1233		fctl_jobwait(job);
1234
1235		fctl_free_ns_cmd(ns_cmd);
1236		if (job->job_result != FC_SUCCESS) {
1237			*error = job->job_result;
1238			fctl_dealloc_job(job);
1239			return (pd);
1240		}
1241		fctl_dealloc_job(job);
1242
1243		/*
1244		 * Check if the port device is created now.
1245		 */
1246		pd = fctl_get_remote_port_by_pwwn(port, pwwn);
1247
1248		if (pd == NULL) {
1249			*error = FC_FAILURE;
1250		} else {
1251			*error = FC_SUCCESS;
1252
1253			/*
1254			 * A ULP now knows about this pd, so mark it
1255			 */
1256			mutex_enter(&pd->pd_mutex);
1257			pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
1258			mutex_exit(&pd->pd_mutex);
1259		}
1260	} else {
1261		mutex_exit(&port->fp_mutex);
1262		*error = FC_FAILURE;
1263	}
1264
1265	return (pd);
1266}
1267
1268
1269/*
1270 * If a NS object exists in the host and query is performed
1271 * on that object, we should retrieve it from our basket
1272 * and return it right here, there by saving a request going
1273 * all the up to the Name Server.
1274 */
1275int
1276fc_ulp_port_ns(opaque_t port_handle, opaque_t pd, fc_ns_cmd_t *ns_req)
1277{
1278	int		rval;
1279	int		fabric;
1280	job_request_t	*job;
1281	fctl_ns_req_t	*ns_cmd;
1282	fc_local_port_t	*port = port_handle;
1283
1284	mutex_enter(&port->fp_mutex);
1285	fabric = FC_IS_TOP_SWITCH(port->fp_topology) ? 1 : 0;
1286	mutex_exit(&port->fp_mutex);
1287
1288	/*
1289	 * Name server query can't be performed for devices not in Fabric
1290	 */
1291	if (!fabric && pd) {
1292		return (FC_BADOBJECT);
1293	}
1294
1295	if (FC_IS_CMD_A_REG(ns_req->ns_cmd)) {
1296		if (pd == NULL) {
1297			rval = fctl_update_host_ns_values(port, ns_req);
1298			if (rval != FC_SUCCESS) {
1299				return (rval);
1300			}
1301		} else {
1302			/*
1303			 * Guess what, FC-GS-2 currently prohibits (not
1304			 * in the strongest language though) setting of
1305			 * NS object values by other ports. But we might
1306			 * get that changed to at least accommodate setting
1307			 * symbolic node/port names - But if disks/tapes
1308			 * were going to provide a method to set these
1309			 * values directly (which in turn might register
1310			 * with the NS when they come up; yep, for that
1311			 * to happen the disks will have to be very well
1312			 * behaved Fabric citizen) we won't need to
1313			 * register the symbolic port/node names for
1314			 * other ports too (rather send down SCSI commands
1315			 * to the devices to set the names)
1316			 *
1317			 * Be that as it may, let's continue to fail
1318			 * registration requests for other ports. period.
1319			 */
1320			return (FC_BADOBJECT);
1321		}
1322
1323		if (!fabric) {
1324			return (FC_SUCCESS);
1325		}
1326	} else if (!fabric) {
1327		return (fctl_retrieve_host_ns_values(port, ns_req));
1328	}
1329
1330	job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
1331	ASSERT(job != NULL);
1332
1333	ns_cmd = fctl_alloc_ns_cmd(ns_req->ns_req_len,
1334	    ns_req->ns_resp_len, ns_req->ns_resp_len, 0, KM_SLEEP);
1335	ASSERT(ns_cmd != NULL);
1336	ns_cmd->ns_cmd_code = ns_req->ns_cmd;
1337	bcopy(ns_req->ns_req_payload, ns_cmd->ns_cmd_buf,
1338	    ns_req->ns_req_len);
1339
1340	job->job_private = (void *)ns_cmd;
1341	fctl_enque_job(port, job);
1342	fctl_jobwait(job);
1343	rval = job->job_result;
1344
1345	if (ns_req->ns_resp_len >= ns_cmd->ns_data_len) {
1346		bcopy(ns_cmd->ns_data_buf, ns_req->ns_resp_payload,
1347		    ns_cmd->ns_data_len);
1348	}
1349	bcopy(&ns_cmd->ns_resp_hdr, &ns_req->ns_resp_hdr,
1350	    sizeof (fc_ct_header_t));
1351
1352	fctl_free_ns_cmd(ns_cmd);
1353	fctl_dealloc_job(job);
1354
1355	return (rval);
1356}
1357
1358
1359int
1360fc_ulp_transport(opaque_t port_handle, fc_packet_t *pkt)
1361{
1362	int			rval;
1363	fc_local_port_t		*port;
1364	fc_remote_port_t	*pd, *newpd;
1365	fc_ulp_rscn_info_t	*rscnp =
1366	    (fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
1367
1368	port = port_handle;
1369
1370	if (pkt->pkt_tran_flags & FC_TRAN_DUMPING) {
1371		return (port->fp_fca_tran->fca_transport(
1372		    port->fp_fca_handle, pkt));
1373	}
1374
1375	mutex_enter(&port->fp_mutex);
1376	if (port->fp_statec_busy) {
1377		mutex_exit(&port->fp_mutex);
1378		return (FC_STATEC_BUSY);
1379	}
1380
1381	/* A locus of race conditions */
1382	if (((FC_PORT_STATE_MASK(port->fp_state)) == FC_STATE_OFFLINE) ||
1383	    (port->fp_soft_state &
1384	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1385		mutex_exit(&port->fp_mutex);
1386		return (FC_OFFLINE);
1387	}
1388
1389	/*
1390	 * If the rscn count in the packet is not the same as the rscn count
1391	 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1392	 */
1393	if ((rscnp != NULL) &&
1394	    (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1395	    (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1396		mutex_exit(&port->fp_mutex);
1397		return (FC_DEVICE_BUSY_NEW_RSCN);
1398	}
1399
1400	pd = pkt->pkt_pd;
1401	if (pd) {
1402		if (pd->pd_type == PORT_DEVICE_OLD ||
1403		    pd->pd_state == PORT_DEVICE_INVALID) {
1404
1405			newpd = fctl_get_remote_port_by_pwwn_mutex_held(port,
1406			    &pd->pd_port_name);
1407
1408			/*
1409			 * The remote port (pd) in the packet is no longer
1410			 * usable, as the old pd still exists we can use the
1411			 * WWN to check if we have a current pd for the device
1412			 * we want. Either way we continue with the old logic
1413			 * whether we have a new pd or not, as the new pd
1414			 * could be bad, or have become unusable.
1415			 */
1416			if ((newpd) && (newpd != pd)) {
1417
1418				/*
1419				 * There is a better remote port (pd) to try,
1420				 * so we need to fix the reference counts, etc.
1421				 */
1422				mutex_enter(&newpd->pd_mutex);
1423				newpd->pd_ref_count++;
1424				pkt->pkt_pd = newpd;
1425				mutex_exit(&newpd->pd_mutex);
1426
1427				mutex_enter(&pd->pd_mutex);
1428				pd->pd_ref_count--;
1429				if ((pd->pd_state == PORT_DEVICE_INVALID) &&
1430				    (pd->pd_ref_count == 0)) {
1431					fc_remote_node_t *node =
1432					    pd->pd_remote_nodep;
1433
1434					mutex_exit(&pd->pd_mutex);
1435					mutex_exit(&port->fp_mutex);
1436
1437					/*
1438					 * This will create another PD hole
1439					 * where we have a reference to a pd,
1440					 * but someone else could remove it.
1441					 */
1442					if ((fctl_destroy_remote_port(port, pd)
1443					    == 0) && (node != NULL)) {
1444						fctl_destroy_remote_node(node);
1445					}
1446					mutex_enter(&port->fp_mutex);
1447				} else {
1448					mutex_exit(&pd->pd_mutex);
1449				}
1450				pd = newpd;
1451			}
1452		}
1453
1454		if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
1455			rval = (pd->pd_state == PORT_DEVICE_VALID) ?
1456			    FC_LOGINREQ : FC_BADDEV;
1457			mutex_exit(&port->fp_mutex);
1458			return (rval);
1459		}
1460
1461		if (pd->pd_flags != PD_IDLE) {
1462			mutex_exit(&port->fp_mutex);
1463			return (FC_DEVICE_BUSY);
1464		}
1465
1466		if (pd->pd_type == PORT_DEVICE_OLD ||
1467		    pd->pd_state == PORT_DEVICE_INVALID) {
1468			mutex_exit(&port->fp_mutex);
1469			return (FC_BADDEV);
1470		}
1471
1472	} else if (FC_IS_REAL_DEVICE(pkt->pkt_cmd_fhdr.d_id)) {
1473		mutex_exit(&port->fp_mutex);
1474		return (FC_BADPACKET);
1475	}
1476	mutex_exit(&port->fp_mutex);
1477
1478	return (port->fp_fca_tran->fca_transport(port->fp_fca_handle, pkt));
1479}
1480
1481
1482int
1483fc_ulp_issue_els(opaque_t port_handle, fc_packet_t *pkt)
1484{
1485	int			rval;
1486	fc_local_port_t		*port = port_handle;
1487	fc_remote_port_t	*pd;
1488	fc_ulp_rscn_info_t	*rscnp =
1489	    (fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
1490
1491	/*
1492	 * If the port is OFFLINE, or if the port driver is
1493	 * being SUSPENDED/PM_SUSPENDED/DETACHED, block all
1494	 * ELS operations
1495	 */
1496	mutex_enter(&port->fp_mutex);
1497	if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
1498	    (port->fp_soft_state &
1499	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
1500		mutex_exit(&port->fp_mutex);
1501		return (FC_OFFLINE);
1502	}
1503
1504	if (port->fp_statec_busy) {
1505		mutex_exit(&port->fp_mutex);
1506		return (FC_STATEC_BUSY);
1507	}
1508
1509	/*
1510	 * If the rscn count in the packet is not the same as the rscn count
1511	 * in the fc_local_port_t, then one or more new RSCNs has occurred.
1512	 */
1513	if ((rscnp != NULL) &&
1514	    (rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
1515	    (rscnp->ulp_rscn_count != port->fp_rscn_count)) {
1516		mutex_exit(&port->fp_mutex);
1517		return (FC_DEVICE_BUSY_NEW_RSCN);
1518	}
1519
1520	mutex_exit(&port->fp_mutex);
1521
1522	if ((pd = pkt->pkt_pd) != NULL) {
1523		mutex_enter(&pd->pd_mutex);
1524		if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
1525			rval = (pd->pd_state == PORT_DEVICE_VALID) ?
1526			    FC_LOGINREQ : FC_BADDEV;
1527			mutex_exit(&pd->pd_mutex);
1528			return (rval);
1529		}
1530
1531		if (pd->pd_flags != PD_IDLE) {
1532			mutex_exit(&pd->pd_mutex);
1533			return (FC_DEVICE_BUSY);
1534		}
1535		if (pd->pd_type == PORT_DEVICE_OLD ||
1536		    pd->pd_state == PORT_DEVICE_INVALID) {
1537			mutex_exit(&pd->pd_mutex);
1538			return (FC_BADDEV);
1539		}
1540		mutex_exit(&pd->pd_mutex);
1541	}
1542
1543	return (port->fp_fca_tran->fca_els_send(port->fp_fca_handle, pkt));
1544}
1545
1546
1547int
1548fc_ulp_uballoc(opaque_t port_handle, uint32_t *count, uint32_t size,
1549    uint32_t type, uint64_t *tokens)
1550{
1551	fc_local_port_t *port = port_handle;
1552
1553	return (port->fp_fca_tran->fca_ub_alloc(port->fp_fca_handle,
1554	    tokens, size, count, type));
1555}
1556
1557
1558int
1559fc_ulp_ubfree(opaque_t port_handle, uint32_t count, uint64_t *tokens)
1560{
1561	fc_local_port_t *port = port_handle;
1562
1563	return (port->fp_fca_tran->fca_ub_free(port->fp_fca_handle,
1564	    count, tokens));
1565}
1566
1567
1568int
1569fc_ulp_ubrelease(opaque_t port_handle, uint32_t count, uint64_t *tokens)
1570{
1571	fc_local_port_t *port = port_handle;
1572
1573	return (port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
1574	    count, tokens));
1575}
1576
1577
1578int
1579fc_ulp_abort(opaque_t port_handle, fc_packet_t *pkt, int flags)
1580{
1581	fc_local_port_t *port = port_handle;
1582
1583	return (port->fp_fca_tran->fca_abort(port->fp_fca_handle, pkt, flags));
1584}
1585
1586
1587/*
1588 * Submit an asynchronous request to the job handler if the sleep
1589 * flag is set to KM_NOSLEEP, as such calls could have been made
1590 * in interrupt contexts, and the goal is to avoid busy waiting,
1591 * blocking on a conditional variable, a semaphore or any of the
1592 * synchronization primitives. A noticeable draw back with this
1593 * asynchronous request is that an FC_SUCCESS is returned long
1594 * before the reset is complete (successful or not).
1595 */
1596int
1597fc_ulp_linkreset(opaque_t port_handle, la_wwn_t *pwwn, int sleep)
1598{
1599	int		rval;
1600	fc_local_port_t *port;
1601	job_request_t	*job;
1602
1603	port = port_handle;
1604	/*
1605	 * Many a times, this function is called from interrupt
1606	 * contexts and there have been several dead locks and
1607	 * hangs - One of the simplest work arounds is to fib
1608	 * if a RESET is in progress.
1609	 */
1610	mutex_enter(&port->fp_mutex);
1611	if (port->fp_soft_state & FP_SOFT_IN_LINK_RESET) {
1612		mutex_exit(&port->fp_mutex);
1613		return (FC_SUCCESS);
1614	}
1615
1616	/*
1617	 * Ward off this reset if a state change is in progress.
1618	 */
1619	if (port->fp_statec_busy) {
1620		mutex_exit(&port->fp_mutex);
1621		return (FC_STATEC_BUSY);
1622	}
1623	port->fp_soft_state |= FP_SOFT_IN_LINK_RESET;
1624	mutex_exit(&port->fp_mutex);
1625
1626	if (fctl_busy_port(port) != 0) {
1627		mutex_enter(&port->fp_mutex);
1628		port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1629		mutex_exit(&port->fp_mutex);
1630		return (FC_FAILURE);
1631	}
1632
1633	if (sleep == KM_SLEEP) {
1634		job = fctl_alloc_job(JOB_LINK_RESET, 0, NULL, NULL, sleep);
1635		ASSERT(job != NULL);
1636
1637		job->job_private = (void *)pwwn;
1638		job->job_counter = 1;
1639		fctl_enque_job(port, job);
1640		fctl_jobwait(job);
1641
1642		mutex_enter(&port->fp_mutex);
1643		port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1644		mutex_exit(&port->fp_mutex);
1645
1646		fctl_idle_port(port);
1647
1648		rval = job->job_result;
1649		fctl_dealloc_job(job);
1650	} else {
1651		job = fctl_alloc_job(JOB_LINK_RESET, JOB_TYPE_FCTL_ASYNC,
1652		    fctl_link_reset_done, port, sleep);
1653		if (job == NULL) {
1654			mutex_enter(&port->fp_mutex);
1655			port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
1656			mutex_exit(&port->fp_mutex);
1657			fctl_idle_port(port);
1658			return (FC_NOMEM);
1659		}
1660		job->job_private = (void *)pwwn;
1661		job->job_counter = 1;
1662		fctl_priority_enque_job(port, job);
1663		rval = FC_SUCCESS;
1664	}
1665
1666	return (rval);
1667}
1668
1669
1670int
1671fc_ulp_port_reset(opaque_t port_handle, uint32_t cmd)
1672{
1673	int		rval = FC_SUCCESS;
1674	fc_local_port_t *port = port_handle;
1675
1676	switch (cmd) {
1677	case FC_RESET_PORT:
1678		rval = port->fp_fca_tran->fca_reset(
1679		    port->fp_fca_handle, FC_FCA_LINK_RESET);
1680		break;
1681
1682	case FC_RESET_ADAPTER:
1683		rval = port->fp_fca_tran->fca_reset(
1684		    port->fp_fca_handle, FC_FCA_RESET);
1685		break;
1686
1687	case FC_RESET_DUMP:
1688		rval = port->fp_fca_tran->fca_reset(
1689		    port->fp_fca_handle, FC_FCA_CORE);
1690		break;
1691
1692	case FC_RESET_CRASH:
1693		rval = port->fp_fca_tran->fca_reset(
1694		    port->fp_fca_handle, FC_FCA_RESET_CORE);
1695		break;
1696
1697	default:
1698		rval = FC_FAILURE;
1699	}
1700
1701	return (rval);
1702}
1703
1704
1705int
1706fc_ulp_get_port_login_params(opaque_t port_handle, la_els_logi_t *login_params)
1707{
1708	fc_local_port_t *port = port_handle;
1709
1710	/* Copy the login parameters */
1711	*login_params = port->fp_service_params;
1712	return (FC_SUCCESS);
1713}
1714
1715
1716int
1717fc_ulp_get_port_instance(opaque_t port_handle)
1718{
1719	fc_local_port_t *port = port_handle;
1720
1721	return (port->fp_instance);
1722}
1723
1724
1725opaque_t
1726fc_ulp_get_port_handle(int port_instance)
1727{
1728	opaque_t	port_handle = NULL;
1729	fc_fca_port_t	*cur;
1730
1731	mutex_enter(&fctl_port_lock);
1732	for (cur = fctl_fca_portlist; cur; cur = cur->port_next) {
1733		if (cur->port_handle->fp_instance == port_instance) {
1734			port_handle = (opaque_t)cur->port_handle;
1735			break;
1736		}
1737	}
1738	mutex_exit(&fctl_port_lock);
1739
1740	return (port_handle);
1741}
1742
1743
1744int
1745fc_ulp_error(int fc_errno, char **errmsg)
1746{
1747	return (fctl_error(fc_errno, errmsg));
1748}
1749
1750
1751int
1752fc_ulp_pkt_error(fc_packet_t *pkt, char **state, char **reason,
1753    char **action, char **expln)
1754{
1755	return (fctl_pkt_error(pkt, state, reason, action, expln));
1756}
1757
1758
1759/*
1760 * If an ULP by the specified name exists, return FC_SUCCESS, else FC_FAILURE
1761 */
1762int
1763fc_ulp_is_name_present(caddr_t ulp_name)
1764{
1765	int		rval = FC_FAILURE;
1766	fc_ulp_list_t	*list;
1767
1768	mutex_enter(&fctl_ulp_list_mutex);
1769	for (list = fctl_ulp_list; list != NULL; list = list->ulp_next) {
1770		if (strcmp(list->ulp_info->ulp_name, ulp_name) == 0) {
1771			rval = FC_SUCCESS;
1772			break;
1773		}
1774	}
1775	mutex_exit(&fctl_ulp_list_mutex);
1776
1777	return (rval);
1778}
1779
1780
1781/*
1782 * Return port WWN for a port Identifier
1783 */
1784int
1785fc_ulp_get_pwwn_by_did(opaque_t port_handle, fc_portid_t d_id, la_wwn_t *pwwn)
1786{
1787	int			rval = FC_FAILURE;
1788	fc_remote_port_t	*pd;
1789	fc_local_port_t		*port = port_handle;
1790
1791	pd = fctl_get_remote_port_by_did(port, d_id.port_id);
1792	if (pd != NULL) {
1793		mutex_enter(&pd->pd_mutex);
1794		*pwwn = pd->pd_port_name;
1795		mutex_exit(&pd->pd_mutex);
1796		rval = FC_SUCCESS;
1797	}
1798
1799	return (rval);
1800}
1801
1802
1803/*
1804 * Return a port map for a port WWN
1805 */
1806int
1807fc_ulp_pwwn_to_portmap(opaque_t port_handle, la_wwn_t *bytes, fc_portmap_t *map)
1808{
1809	fc_local_port_t		*port = port_handle;
1810	fc_remote_node_t	*node;
1811	fc_remote_port_t	*pd;
1812
1813	pd = fctl_get_remote_port_by_pwwn(port, bytes);
1814	if (pd == NULL) {
1815		return (FC_FAILURE);
1816	}
1817
1818	mutex_enter(&pd->pd_mutex);
1819	map->map_pwwn = pd->pd_port_name;
1820	map->map_did = pd->pd_port_id;
1821	map->map_hard_addr = pd->pd_hard_addr;
1822	map->map_state = pd->pd_state;
1823	map->map_type = pd->pd_type;
1824	map->map_flags = 0;
1825
1826	ASSERT(map->map_type <= PORT_DEVICE_DELETE);
1827
1828	bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
1829
1830	node = pd->pd_remote_nodep;
1831	mutex_exit(&pd->pd_mutex);
1832
1833	if (node) {
1834		mutex_enter(&node->fd_mutex);
1835		map->map_nwwn = node->fd_node_name;
1836		mutex_exit(&node->fd_mutex);
1837	}
1838	map->map_pd = pd;
1839
1840	return (FC_SUCCESS);
1841}
1842
1843
1844opaque_t
1845fc_ulp_get_fca_device(opaque_t port_handle, fc_portid_t d_id)
1846{
1847	fc_local_port_t	*port = port_handle;
1848
1849	if (port->fp_fca_tran->fca_get_device == NULL) {
1850		return (NULL);
1851	}
1852
1853	return (port->fp_fca_tran->fca_get_device(port->fp_fca_handle, d_id));
1854}
1855
1856
1857int
1858fc_ulp_port_notify(opaque_t port_handle, uint32_t cmd)
1859{
1860	int		rval = FC_SUCCESS;
1861	fc_local_port_t	*port = port_handle;
1862
1863	if (port->fp_fca_tran->fca_notify) {
1864		mutex_enter(&port->fp_mutex);
1865		switch (cmd) {
1866		case FC_NOTIFY_TARGET_MODE:
1867			port->fp_options |= FP_TARGET_MODE;
1868			break;
1869		case FC_NOTIFY_NO_TARGET_MODE:
1870			port->fp_options &= ~FP_TARGET_MODE;
1871			break;
1872		}
1873		mutex_exit(&port->fp_mutex);
1874		rval = port->fp_fca_tran->fca_notify(port->fp_fca_handle, cmd);
1875	}
1876
1877	return (rval);
1878}
1879
1880
1881void
1882fc_ulp_disable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
1883{
1884	fc_remote_port_t *pd =
1885	    fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
1886
1887	if (pd) {
1888		mutex_enter(&pd->pd_mutex);
1889		pd->pd_aux_flags |= PD_DISABLE_RELOGIN;
1890		mutex_exit(&pd->pd_mutex);
1891	}
1892}
1893
1894
1895void
1896fc_ulp_enable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
1897{
1898	fc_remote_port_t *pd =
1899	    fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
1900
1901	if (pd) {
1902		mutex_enter(&pd->pd_mutex);
1903		pd->pd_aux_flags &= ~PD_DISABLE_RELOGIN;
1904		mutex_exit(&pd->pd_mutex);
1905	}
1906}
1907
1908
1909/*
1910 * fc_fca_init
1911 *		Overload the FCA bus_ops vector in its dev_ops with
1912 *		fctl_fca_busops to handle all the INITchilds for "sf"
1913 *		in one common place.
1914 *
1915 *		Should be called from FCA _init routine.
1916 */
1917void
1918fc_fca_init(struct dev_ops *fca_devops_p)
1919{
1920#ifndef	__lock_lint
1921	fca_devops_p->devo_bus_ops = &fctl_fca_busops;
1922#endif	/* __lock_lint */
1923}
1924
1925
1926/*
1927 * fc_fca_attach
1928 */
1929int
1930fc_fca_attach(dev_info_t *fca_dip, fc_fca_tran_t *tran)
1931{
1932	/*
1933	 * When we are in a position to offer downward compatibility
1934	 * we should change the following check to allow lower revision
1935	 * of FCAs; But we aren't there right now.
1936	 */
1937	if (tran->fca_version != FCTL_FCA_MODREV_5) {
1938		const char *name = ddi_driver_name(fca_dip);
1939
1940		ASSERT(name != NULL);
1941
1942		cmn_err(CE_WARN, "fctl: FCA %s version mismatch"
1943		    " please upgrade %s", name, name);
1944		return (DDI_FAILURE);
1945	}
1946
1947	ddi_set_driver_private(fca_dip, (caddr_t)tran);
1948	return (DDI_SUCCESS);
1949}
1950
1951
1952/*
1953 * fc_fca_detach
1954 */
1955int
1956fc_fca_detach(dev_info_t *fca_dip)
1957{
1958	ddi_set_driver_private(fca_dip, NULL);
1959	return (DDI_SUCCESS);
1960}
1961
1962
1963/*
1964 * Check if the frame is a Link response Frame; Handle all cases (P_RJT,
1965 * F_RJT, P_BSY, F_BSY fall into this category). Check also for some Basic
1966 * Link Service responses such as BA_RJT and Extended Link Service response
1967 * such as LS_RJT. If the response is a Link_Data Frame or something that
1968 * this function doesn't understand return FC_FAILURE; Otherwise, fill out
1969 * various fields (state, action, reason, expln) from the response gotten
1970 * in the packet and return FC_SUCCESS.
1971 */
1972int
1973fc_fca_update_errors(fc_packet_t *pkt)
1974{
1975	int ret = FC_SUCCESS;
1976
1977	switch (pkt->pkt_resp_fhdr.r_ctl) {
1978	case R_CTL_P_RJT: {
1979		uint32_t prjt;
1980
1981		prjt = pkt->pkt_resp_fhdr.ro;
1982		pkt->pkt_state = FC_PKT_NPORT_RJT;
1983		pkt->pkt_action = (prjt & 0xFF000000) >> 24;
1984		pkt->pkt_reason = (prjt & 0xFF0000) >> 16;
1985		break;
1986	}
1987
1988	case R_CTL_F_RJT: {
1989		uint32_t frjt;
1990
1991		frjt = pkt->pkt_resp_fhdr.ro;
1992		pkt->pkt_state = FC_PKT_FABRIC_RJT;
1993		pkt->pkt_action = (frjt & 0xFF000000) >> 24;
1994		pkt->pkt_reason = (frjt & 0xFF0000) >> 16;
1995		break;
1996	}
1997
1998	case R_CTL_P_BSY: {
1999		uint32_t pbsy;
2000
2001		pbsy = pkt->pkt_resp_fhdr.ro;
2002		pkt->pkt_state = FC_PKT_NPORT_BSY;
2003		pkt->pkt_action = (pbsy & 0xFF000000) >> 24;
2004		pkt->pkt_reason = (pbsy & 0xFF0000) >> 16;
2005		break;
2006	}
2007
2008	case R_CTL_F_BSY_LC:
2009	case R_CTL_F_BSY_DF: {
2010		uchar_t fbsy;
2011
2012		fbsy = pkt->pkt_resp_fhdr.type;
2013		pkt->pkt_state = FC_PKT_FABRIC_BSY;
2014		pkt->pkt_reason = (fbsy & 0xF0) >> 4;
2015		break;
2016	}
2017
2018	case R_CTL_LS_BA_RJT: {
2019		uint32_t brjt;
2020
2021		brjt = *(uint32_t *)pkt->pkt_resp;
2022		pkt->pkt_state = FC_PKT_BA_RJT;
2023		pkt->pkt_reason = (brjt & 0xFF0000) >> 16;
2024		pkt->pkt_expln = (brjt & 0xFF00) >> 8;
2025		break;
2026	}
2027
2028	case R_CTL_ELS_RSP: {
2029		la_els_rjt_t *lsrjt;
2030
2031		lsrjt = (la_els_rjt_t *)pkt->pkt_resp;
2032		if (lsrjt->ls_code.ls_code == LA_ELS_RJT) {
2033			pkt->pkt_state = FC_PKT_LS_RJT;
2034			pkt->pkt_reason = lsrjt->reason;
2035			pkt->pkt_action = lsrjt->action;
2036			break;
2037		}
2038	}
2039	/* FALLTHROUGH */
2040
2041	default:
2042		ret = FC_FAILURE;
2043		break;
2044	}
2045
2046	return (ret);
2047}
2048
2049
2050int
2051fc_fca_error(int fc_errno, char **errmsg)
2052{
2053	return (fctl_error(fc_errno, errmsg));
2054}
2055
2056
2057int
2058fc_fca_pkt_error(fc_packet_t *pkt, char **state, char **reason,
2059    char **action, char **expln)
2060{
2061	return (fctl_pkt_error(pkt, state, reason, action, expln));
2062}
2063
2064
2065/*
2066 * WWN to string goodie. Unpredictable results will happen
2067 * if enough memory isn't supplied in str argument. If you
2068 * are wondering how much does this routine need, it is just
2069 * (2 * WWN size + 1). So for a WWN size of 8 bytes the str
2070 * argument should have atleast 17 bytes allocated.
2071 */
2072void
2073fc_wwn_to_str(la_wwn_t *wwn, caddr_t str)
2074{
2075	int count;
2076
2077	for (count = 0; count < FCTL_WWN_SIZE(wwn); count++, str += 2) {
2078		(void) sprintf(str, "%02x", wwn->raw_wwn[count]);
2079	}
2080	*str = '\0';
2081}
2082
2083#define	FC_ATOB(x)	(((x) >= '0' && (x) <= '9') ? ((x) - '0') :	\
2084			((x) >= 'a' && (x) <= 'f') ?			\
2085			((x) - 'a' + 10) : ((x) - 'A' + 10))
2086
2087void
2088fc_str_to_wwn(caddr_t str, la_wwn_t *wwn)
2089{
2090	int count = 0;
2091	uchar_t byte;
2092
2093	while (*str) {
2094		byte = FC_ATOB(*str);
2095		str++;
2096		byte = byte << 4 | FC_ATOB(*str);
2097		str++;
2098		wwn->raw_wwn[count++] = byte;
2099	}
2100}
2101
2102/*
2103 * FCA driver's intercepted bus control operations.
2104 */
2105static int
2106fctl_fca_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
2107    ddi_ctl_enum_t op, void *arg, void *result)
2108{
2109	switch (op) {
2110	case DDI_CTLOPS_REPORTDEV:
2111		break;
2112
2113	case DDI_CTLOPS_IOMIN:
2114		break;
2115
2116	case DDI_CTLOPS_INITCHILD:
2117		return (fctl_initchild(fca_dip, (dev_info_t *)arg));
2118
2119	case DDI_CTLOPS_UNINITCHILD:
2120		return (fctl_uninitchild(fca_dip, (dev_info_t *)arg));
2121
2122	default:
2123		return (ddi_ctlops(fca_dip, rip, op, arg, result));
2124	}
2125
2126	return (DDI_SUCCESS);
2127}
2128
2129
2130/*
2131 * FCAs indicate the maximum number of ports supported in their
2132 * tran structure. Fail the INITCHILD if the child port number
2133 * is any greater than the maximum number of ports supported
2134 * by the FCA.
2135 */
2136static int
2137fctl_initchild(dev_info_t *fca_dip, dev_info_t *port_dip)
2138{
2139	int		rval;
2140	int		port_no;
2141	int		port_len;
2142	char		name[20];
2143	fc_fca_tran_t	*tran;
2144	dev_info_t	*dip;
2145	int		portprop;
2146
2147	port_len = sizeof (port_no);
2148
2149	/* physical port do not has this property */
2150	portprop = ddi_prop_get_int(DDI_DEV_T_ANY, port_dip,
2151	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
2152	    "phyport-instance", -1);
2153
2154	if ((portprop == -1) && ndi_dev_is_persistent_node(port_dip)) {
2155		/*
2156		 * Clear any addr bindings created by fcode interpreter
2157		 * in devi_last_addr so that a ndi_devi_find should never
2158		 * return this fcode node.
2159		 */
2160		ddi_set_name_addr(port_dip, NULL);
2161		return (DDI_FAILURE);
2162	}
2163
2164	rval = ddi_prop_op(DDI_DEV_T_ANY, port_dip, PROP_LEN_AND_VAL_BUF,
2165	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "port",
2166	    (caddr_t)&port_no, &port_len);
2167
2168	if (rval != DDI_SUCCESS) {
2169		return (DDI_FAILURE);
2170	}
2171
2172	tran = (fc_fca_tran_t *)ddi_get_driver_private(fca_dip);
2173	ASSERT(tran != NULL);
2174
2175	(void) sprintf((char *)name, "%x,0", port_no);
2176	ddi_set_name_addr(port_dip, name);
2177
2178	dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
2179
2180	/*
2181	 * Even though we never initialize FCode nodes of fp, such a node
2182	 * could still be there after a DR operation. There will only be
2183	 * one FCode node, so if this is the one, clear it and issue a
2184	 * ndi_devi_find again.
2185	 */
2186	if ((portprop == -1) && dip && ndi_dev_is_persistent_node(dip)) {
2187		ddi_set_name_addr(dip, NULL);
2188		dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
2189	}
2190
2191	if ((portprop == -1) && dip && (dip != port_dip)) {
2192		/*
2193		 * Here we have a duplicate .conf entry. Clear the addr
2194		 * set previously and return failure.
2195		 */
2196		ddi_set_name_addr(port_dip, NULL);
2197		return (DDI_FAILURE);
2198	}
2199
2200	return (DDI_SUCCESS);
2201}
2202
2203
2204/* ARGSUSED */
2205static int
2206fctl_uninitchild(dev_info_t *fca_dip, dev_info_t *port_dip)
2207{
2208	ddi_set_name_addr(port_dip, NULL);
2209	return (DDI_SUCCESS);
2210}
2211
2212
2213static dev_info_t *
2214fctl_findchild(dev_info_t *pdip, char *cname, char *caddr)
2215{
2216	dev_info_t *dip;
2217	char *addr;
2218
2219	ASSERT(cname != NULL && caddr != NULL);
2220	/* ASSERT(DEVI_BUSY_OWNED(pdip)); */
2221
2222	for (dip = ddi_get_child(pdip); dip != NULL;
2223	    dip = ddi_get_next_sibling(dip)) {
2224		if (strcmp(cname, ddi_node_name(dip)) != 0) {
2225			continue;
2226		}
2227
2228		if ((addr = ddi_get_name_addr(dip)) == NULL) {
2229			if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
2230			    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
2231			    "bus-addr", &addr) == DDI_PROP_SUCCESS) {
2232				if (strcmp(caddr, addr) == 0) {
2233					ddi_prop_free(addr);
2234					return (dip);
2235				}
2236				ddi_prop_free(addr);
2237			}
2238		} else {
2239			if (strcmp(caddr, addr) == 0) {
2240				return (dip);
2241			}
2242		}
2243	}
2244
2245	return (NULL);
2246}
2247
2248int
2249fctl_check_npiv_portindex(dev_info_t *dip, int vindex)
2250{
2251	int i, instance;
2252	fc_local_port_t *port;
2253
2254	instance = ddi_get_instance(dip);
2255	port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2256	if ((!port) || (vindex <= 0) || (vindex >= FC_NPIV_MAX_PORT)) {
2257		return (0);
2258	}
2259
2260	i = vindex-1;
2261	mutex_enter(&port->fp_mutex);
2262	if (port->fp_npiv_portindex[i] == 0) {
2263		mutex_exit(&port->fp_mutex);
2264		return (vindex);
2265	}
2266	mutex_exit(&port->fp_mutex);
2267	return (0);
2268}
2269
2270int
2271fctl_get_npiv_portindex(dev_info_t *dip)
2272{
2273	int i, instance;
2274	fc_local_port_t *port;
2275
2276	instance = ddi_get_instance(dip);
2277	port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2278	if (!port) {
2279		return (0);
2280	}
2281
2282	mutex_enter(&port->fp_mutex);
2283	for (i = 0; i < FC_NPIV_MAX_PORT; i++) {
2284		if (port->fp_npiv_portindex[i] == 0) {
2285			mutex_exit(&port->fp_mutex);
2286			return (i+1);
2287		}
2288	}
2289	mutex_exit(&port->fp_mutex);
2290	return (0);
2291}
2292
2293
2294void
2295fctl_set_npiv_portindex(dev_info_t *dip, int index)
2296{
2297	int instance;
2298	fc_local_port_t *port;
2299
2300	instance = ddi_get_instance(dip);
2301	port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
2302	if (!port) {
2303		return;
2304	}
2305	mutex_enter(&port->fp_mutex);
2306	port->fp_npiv_portindex[index - 1] = 1;
2307	mutex_exit(&port->fp_mutex);
2308}
2309
2310
2311int
2312fctl_fca_create_npivport(dev_info_t *parent,
2313    dev_info_t *phydip, char *nname, char *pname, uint32_t *vindex)
2314{
2315	int rval = 0, devstrlen;
2316	char	*devname, *cname, *caddr, *devstr;
2317	dev_info_t	*child = NULL;
2318	int		portnum;
2319
2320	if (*vindex == 0) {
2321		portnum = fctl_get_npiv_portindex(phydip);
2322		*vindex = portnum;
2323	} else {
2324		portnum = fctl_check_npiv_portindex(phydip, *vindex);
2325	}
2326
2327	if (portnum == 0) {
2328		cmn_err(CE_WARN,
2329		    "Cann't find valid port index, fail to create devnode");
2330		return (NDI_FAILURE);
2331	}
2332
2333	devname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
2334	(void) sprintf(devname, "fp@%x,0", portnum);
2335	devstrlen = strlen(devname) + 1;
2336	devstr = i_ddi_strdup(devname, KM_SLEEP);
2337	i_ddi_parse_name(devstr, &cname, &caddr, NULL);
2338
2339	if (fctl_findchild(parent, cname, caddr) != NULL) {
2340		rval = NDI_FAILURE;
2341		goto freememory;
2342	}
2343
2344	ndi_devi_alloc_sleep(parent, cname, DEVI_PSEUDO_NODEID, &child);
2345	if (child == NULL) {
2346		cmn_err(CE_WARN,
2347		    "fctl_create_npiv_port fail to create new devinfo");
2348		rval = NDI_FAILURE;
2349		goto freememory;
2350	}
2351
2352	if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2353	    "bus-addr", caddr) != DDI_PROP_SUCCESS) {
2354		cmn_err(CE_WARN, "fctl%d: prop update bus-addr %s@%s failed",
2355		    ddi_get_instance(parent), cname, caddr);
2356		(void) ndi_devi_free(child);
2357		rval = NDI_FAILURE;
2358		goto freememory;
2359	}
2360
2361	if (strlen(nname) != 0) {
2362		if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2363		    "node-name", nname) != DDI_PROP_SUCCESS) {
2364			(void) ndi_devi_free(child);
2365			rval = NDI_FAILURE;
2366			goto freememory;
2367		}
2368	}
2369
2370	if (strlen(pname) != 0) {
2371		if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
2372		    "port-name", pname) != DDI_PROP_SUCCESS) {
2373			(void) ndi_devi_free(child);
2374			rval = NDI_FAILURE;
2375			goto freememory;
2376		}
2377	}
2378
2379	if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
2380	    "port", portnum) != DDI_PROP_SUCCESS) {
2381		cmn_err(CE_WARN, "fp%d: prop_update port %s@%s failed",
2382		    ddi_get_instance(parent), cname, caddr);
2383		(void) ndi_devi_free(child);
2384		rval = NDI_FAILURE;
2385		goto freememory;
2386	}
2387
2388	if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
2389	    "phyport-instance", ddi_get_instance(phydip)) != DDI_PROP_SUCCESS) {
2390		cmn_err(CE_WARN,
2391		    "fp%d: prop_update phyport-instance %s@%s failed",
2392		    ddi_get_instance(parent), cname, caddr);
2393		(void) ndi_devi_free(child);
2394		rval = NDI_FAILURE;
2395		goto freememory;
2396	}
2397
2398	rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
2399	if (rval != NDI_SUCCESS) {
2400		cmn_err(CE_WARN, "fp%d: online_driver %s failed",
2401		    ddi_get_instance(parent), cname);
2402		rval = NDI_FAILURE;
2403		goto freememory;
2404	}
2405
2406	fctl_set_npiv_portindex(phydip, portnum);
2407freememory:
2408	kmem_free(devstr, devstrlen);
2409	kmem_free(devname, MAXNAMELEN);
2410
2411	return (rval);
2412}
2413
2414
2415void
2416fctl_add_port(fc_local_port_t *port)
2417{
2418	fc_fca_port_t *new;
2419
2420	new = kmem_zalloc(sizeof (*new), KM_SLEEP);
2421
2422	mutex_enter(&fctl_port_lock);
2423	new->port_handle = port;
2424	new->port_next = fctl_fca_portlist;
2425	fctl_fca_portlist = new;
2426	mutex_exit(&fctl_port_lock);
2427}
2428
2429
2430void
2431fctl_remove_port(fc_local_port_t *port)
2432{
2433	fc_ulp_module_t		*mod;
2434	fc_fca_port_t		*prev;
2435	fc_fca_port_t		*list;
2436	fc_ulp_ports_t		*ulp_port;
2437
2438	rw_enter(&fctl_ulp_lock, RW_WRITER);
2439	rw_enter(&fctl_mod_ports_lock, RW_WRITER);
2440
2441	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2442		ulp_port = fctl_get_ulp_port(mod, port);
2443		if (ulp_port == NULL) {
2444			continue;
2445		}
2446
2447#ifndef	__lock_lint
2448		ASSERT((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0);
2449#endif /* __lock_lint */
2450
2451		(void) fctl_remove_ulp_port(mod, port);
2452	}
2453
2454	rw_exit(&fctl_mod_ports_lock);
2455	rw_exit(&fctl_ulp_lock);
2456
2457	mutex_enter(&fctl_port_lock);
2458
2459	list = fctl_fca_portlist;
2460	prev = NULL;
2461	while (list != NULL) {
2462		if (list->port_handle == port) {
2463			if (prev == NULL) {
2464				fctl_fca_portlist = list->port_next;
2465			} else {
2466				prev->port_next = list->port_next;
2467			}
2468			kmem_free(list, sizeof (*list));
2469			break;
2470		}
2471		prev = list;
2472		list = list->port_next;
2473	}
2474	mutex_exit(&fctl_port_lock);
2475}
2476
2477
2478void
2479fctl_attach_ulps(fc_local_port_t *port, fc_attach_cmd_t cmd,
2480    struct modlinkage *linkage)
2481{
2482	int			rval;
2483	uint32_t		s_id;
2484	uint32_t		state;
2485	fc_ulp_module_t		*mod;
2486	fc_ulp_port_info_t	info;
2487	fc_ulp_ports_t		*ulp_port;
2488
2489	ASSERT(!MUTEX_HELD(&port->fp_mutex));
2490
2491	info.port_linkage = linkage;
2492	info.port_dip = port->fp_port_dip;
2493	info.port_handle = (opaque_t)port;
2494	info.port_dma_behavior = port->fp_dma_behavior;
2495	info.port_fcp_dma = port->fp_fcp_dma;
2496	info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
2497	info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
2498	info.port_reset_action = port->fp_reset_action;
2499
2500	mutex_enter(&port->fp_mutex);
2501
2502	/*
2503	 * It is still possible that another thread could have gotten
2504	 * into the detach process before we got here.
2505	 */
2506	if (port->fp_soft_state & FP_SOFT_IN_DETACH) {
2507		mutex_exit(&port->fp_mutex);
2508		return;
2509	}
2510
2511	s_id = port->fp_port_id.port_id;
2512	if (port->fp_statec_busy) {
2513		info.port_state = port->fp_bind_state;
2514	} else {
2515		info.port_state = port->fp_state;
2516	}
2517
2518	switch (state = FC_PORT_STATE_MASK(info.port_state)) {
2519	case FC_STATE_LOOP:
2520	case FC_STATE_NAMESERVICE:
2521		info.port_state &= ~state;
2522		info.port_state |= FC_STATE_ONLINE;
2523		break;
2524
2525	default:
2526		break;
2527	}
2528	ASSERT((info.port_state & FC_STATE_LOOP) == 0);
2529
2530	info.port_flags = port->fp_topology;
2531	info.port_pwwn = port->fp_service_params.nport_ww_name;
2532	info.port_nwwn = port->fp_service_params.node_ww_name;
2533	mutex_exit(&port->fp_mutex);
2534
2535	rw_enter(&fctl_ulp_lock, RW_READER);
2536	rw_enter(&fctl_mod_ports_lock, RW_WRITER);
2537
2538	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2539		if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
2540		    (mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
2541			/*
2542			 * We don't support IP over FC on FCOE HBA
2543			 */
2544			continue;
2545		}
2546
2547		if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
2548			ulp_port = fctl_add_ulp_port(mod, port, KM_SLEEP);
2549			ASSERT(ulp_port != NULL);
2550
2551			mutex_enter(&ulp_port->port_mutex);
2552			ulp_port->port_statec = ((info.port_state &
2553			    FC_STATE_ONLINE) ? FC_ULP_STATEC_ONLINE :
2554			    FC_ULP_STATEC_OFFLINE);
2555			mutex_exit(&ulp_port->port_mutex);
2556		}
2557	}
2558
2559	rw_downgrade(&fctl_mod_ports_lock);
2560
2561	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2562		if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
2563		    (mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
2564			/*
2565			 * We don't support IP over FC on FCOE HBA
2566			 */
2567			continue;
2568		}
2569
2570		ulp_port = fctl_get_ulp_port(mod, port);
2571		ASSERT(ulp_port != NULL);
2572
2573		if (fctl_pre_attach(ulp_port, cmd) == FC_FAILURE) {
2574			continue;
2575		}
2576
2577		fctl_init_dma_attr(port, mod, &info);
2578
2579		rval = mod->mod_info->ulp_port_attach(
2580		    mod->mod_info->ulp_handle, &info, cmd, s_id);
2581
2582		fctl_post_attach(mod, ulp_port, cmd, rval);
2583
2584		if (rval == FC_SUCCESS && cmd == FC_CMD_ATTACH &&
2585		    strcmp(mod->mod_info->ulp_name, "fcp") == 0) {
2586			ASSERT(ddi_get_driver_private(info.port_dip) != NULL);
2587		}
2588	}
2589
2590	rw_exit(&fctl_mod_ports_lock);
2591	rw_exit(&fctl_ulp_lock);
2592}
2593
2594
2595static int
2596fctl_pre_attach(fc_ulp_ports_t *ulp_port, fc_attach_cmd_t cmd)
2597{
2598	int rval = FC_SUCCESS;
2599
2600	mutex_enter(&ulp_port->port_mutex);
2601
2602	switch (cmd) {
2603	case FC_CMD_ATTACH:
2604		if (ulp_port->port_dstate & ULP_PORT_ATTACH) {
2605			rval = FC_FAILURE;
2606		}
2607		break;
2608
2609	case FC_CMD_RESUME:
2610		ASSERT((ulp_port->port_dstate & ULP_PORT_POWER_DOWN) == 0);
2611		if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2612		    !(ulp_port->port_dstate & ULP_PORT_SUSPEND)) {
2613			rval = FC_FAILURE;
2614		}
2615		break;
2616
2617	case FC_CMD_POWER_UP:
2618		if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2619		    !(ulp_port->port_dstate & ULP_PORT_POWER_DOWN)) {
2620			rval = FC_FAILURE;
2621		}
2622		break;
2623	}
2624
2625	if (rval == FC_SUCCESS) {
2626		ulp_port->port_dstate |= ULP_PORT_BUSY;
2627	}
2628	mutex_exit(&ulp_port->port_mutex);
2629
2630	return (rval);
2631}
2632
2633
2634static void
2635fctl_post_attach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
2636    fc_attach_cmd_t cmd, int rval)
2637{
2638	int	be_chatty;
2639
2640	ASSERT(cmd == FC_CMD_ATTACH || cmd == FC_CMD_RESUME ||
2641	    cmd == FC_CMD_POWER_UP);
2642
2643	mutex_enter(&ulp_port->port_mutex);
2644	ulp_port->port_dstate &= ~ULP_PORT_BUSY;
2645
2646	be_chatty = (rval == FC_FAILURE_SILENT) ? 0 : 1;
2647
2648	if (rval != FC_SUCCESS) {
2649		caddr_t		op;
2650		fc_local_port_t *port = ulp_port->port_handle;
2651
2652		mutex_exit(&ulp_port->port_mutex);
2653
2654		switch (cmd) {
2655		case FC_CMD_ATTACH:
2656			op = "attach";
2657			break;
2658
2659		case FC_CMD_RESUME:
2660			op = "resume";
2661			break;
2662
2663		case FC_CMD_POWER_UP:
2664			op = "power up";
2665			break;
2666		}
2667
2668		if (be_chatty) {
2669			cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
2670			    port->fp_instance, op, mod->mod_info->ulp_name);
2671		}
2672
2673		return;
2674	}
2675
2676	switch (cmd) {
2677	case FC_CMD_ATTACH:
2678		ulp_port->port_dstate |= ULP_PORT_ATTACH;
2679		break;
2680
2681	case FC_CMD_RESUME:
2682		ulp_port->port_dstate &= ~ULP_PORT_SUSPEND;
2683		break;
2684
2685	case FC_CMD_POWER_UP:
2686		ulp_port->port_dstate &= ~ULP_PORT_POWER_DOWN;
2687		break;
2688	}
2689	mutex_exit(&ulp_port->port_mutex);
2690}
2691
2692
2693int
2694fctl_detach_ulps(fc_local_port_t *port, fc_detach_cmd_t cmd,
2695    struct modlinkage *linkage)
2696{
2697	int			rval = FC_SUCCESS;
2698	fc_ulp_module_t		*mod;
2699	fc_ulp_port_info_t	info;
2700	fc_ulp_ports_t		*ulp_port;
2701
2702	ASSERT(!MUTEX_HELD(&port->fp_mutex));
2703
2704	info.port_linkage = linkage;
2705	info.port_dip = port->fp_port_dip;
2706	info.port_handle = (opaque_t)port;
2707	info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
2708	info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
2709
2710	rw_enter(&fctl_ulp_lock, RW_READER);
2711	rw_enter(&fctl_mod_ports_lock, RW_READER);
2712
2713	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
2714		if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
2715			continue;
2716		}
2717
2718		if (fctl_pre_detach(ulp_port, cmd) != FC_SUCCESS) {
2719			continue;
2720		}
2721
2722		fctl_init_dma_attr(port, mod, &info);
2723
2724		rval = mod->mod_info->ulp_port_detach(
2725		    mod->mod_info->ulp_handle, &info, cmd);
2726
2727		fctl_post_detach(mod, ulp_port, cmd, rval);
2728
2729		if (rval != FC_SUCCESS) {
2730			break;
2731		}
2732
2733		if (cmd == FC_CMD_DETACH && strcmp(mod->mod_info->ulp_name,
2734		    "fcp") == 0) {
2735			ASSERT(ddi_get_driver_private(info.port_dip) == NULL);
2736		}
2737
2738		mutex_enter(&ulp_port->port_mutex);
2739		ulp_port->port_statec = FC_ULP_STATEC_DONT_CARE;
2740		mutex_exit(&ulp_port->port_mutex);
2741	}
2742
2743	rw_exit(&fctl_mod_ports_lock);
2744	rw_exit(&fctl_ulp_lock);
2745
2746	return (rval);
2747}
2748
2749static	void
2750fctl_init_dma_attr(fc_local_port_t *port, fc_ulp_module_t *mod,
2751    fc_ulp_port_info_t	*info)
2752{
2753
2754	if ((strcmp(mod->mod_info->ulp_name, "fcp") == 0) ||
2755	    (strcmp(mod->mod_info->ulp_name, "ltct") == 0)) {
2756		info->port_cmd_dma_attr =
2757		    port->fp_fca_tran->fca_dma_fcp_cmd_attr;
2758		info->port_data_dma_attr =
2759		    port->fp_fca_tran->fca_dma_fcp_data_attr;
2760		info->port_resp_dma_attr =
2761		    port->fp_fca_tran->fca_dma_fcp_rsp_attr;
2762	} else if (strcmp(mod->mod_info->ulp_name, "fcsm") == 0) {
2763		info->port_cmd_dma_attr =
2764		    port->fp_fca_tran->fca_dma_fcsm_cmd_attr;
2765		info->port_data_dma_attr =
2766		    port->fp_fca_tran->fca_dma_attr;
2767		info->port_resp_dma_attr =
2768		    port->fp_fca_tran->fca_dma_fcsm_rsp_attr;
2769	} else if (strcmp(mod->mod_info->ulp_name, "fcip") == 0) {
2770		info->port_cmd_dma_attr =
2771		    port->fp_fca_tran->fca_dma_fcip_cmd_attr;
2772		info->port_data_dma_attr =
2773		    port->fp_fca_tran->fca_dma_attr;
2774		info->port_resp_dma_attr =
2775		    port->fp_fca_tran->fca_dma_fcip_rsp_attr;
2776	} else {
2777		info->port_cmd_dma_attr = info->port_data_dma_attr =
2778		    info->port_resp_dma_attr =
2779		    port->fp_fca_tran->fca_dma_attr; /* default */
2780	}
2781}
2782
2783static int
2784fctl_pre_detach(fc_ulp_ports_t *ulp_port, fc_detach_cmd_t cmd)
2785{
2786	int rval = FC_SUCCESS;
2787
2788	mutex_enter(&ulp_port->port_mutex);
2789
2790	switch (cmd) {
2791	case FC_CMD_DETACH:
2792		if ((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0) {
2793			rval = FC_FAILURE;
2794		}
2795		break;
2796
2797	case FC_CMD_SUSPEND:
2798		if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2799		    ulp_port->port_dstate & ULP_PORT_SUSPEND) {
2800			rval = FC_FAILURE;
2801		}
2802		break;
2803
2804	case FC_CMD_POWER_DOWN:
2805		if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
2806		    ulp_port->port_dstate & ULP_PORT_POWER_DOWN) {
2807			rval = FC_FAILURE;
2808		}
2809		break;
2810	}
2811
2812	if (rval == FC_SUCCESS) {
2813		ulp_port->port_dstate |= ULP_PORT_BUSY;
2814	}
2815	mutex_exit(&ulp_port->port_mutex);
2816
2817	return (rval);
2818}
2819
2820
2821static void
2822fctl_post_detach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
2823    fc_detach_cmd_t cmd, int rval)
2824{
2825	ASSERT(cmd == FC_CMD_DETACH || cmd == FC_CMD_SUSPEND ||
2826	    cmd == FC_CMD_POWER_DOWN);
2827
2828	mutex_enter(&ulp_port->port_mutex);
2829	ulp_port->port_dstate &= ~ULP_PORT_BUSY;
2830
2831	if (rval != FC_SUCCESS) {
2832		caddr_t		op;
2833		fc_local_port_t *port = ulp_port->port_handle;
2834
2835		mutex_exit(&ulp_port->port_mutex);
2836
2837		switch (cmd) {
2838		case FC_CMD_DETACH:
2839			op = "detach";
2840			break;
2841
2842		case FC_CMD_SUSPEND:
2843			op = "suspend";
2844			break;
2845
2846		case FC_CMD_POWER_DOWN:
2847			op = "power down";
2848			break;
2849		}
2850
2851		cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
2852		    port->fp_instance, op, mod->mod_info->ulp_name);
2853
2854		return;
2855	}
2856
2857	switch (cmd) {
2858	case FC_CMD_DETACH:
2859		ulp_port->port_dstate &= ~ULP_PORT_ATTACH;
2860		break;
2861
2862	case FC_CMD_SUSPEND:
2863		ulp_port->port_dstate |= ULP_PORT_SUSPEND;
2864		break;
2865
2866	case FC_CMD_POWER_DOWN:
2867		ulp_port->port_dstate |= ULP_PORT_POWER_DOWN;
2868		break;
2869	}
2870	mutex_exit(&ulp_port->port_mutex);
2871}
2872
2873
2874static fc_ulp_ports_t *
2875fctl_add_ulp_port(fc_ulp_module_t *ulp_module, fc_local_port_t *port_handle,
2876    int sleep)
2877{
2878	fc_ulp_ports_t *last;
2879	fc_ulp_ports_t *next;
2880	fc_ulp_ports_t *new;
2881
2882	ASSERT(RW_READ_HELD(&fctl_ulp_lock));
2883	ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
2884
2885	last = NULL;
2886	next = ulp_module->mod_ports;
2887
2888	while (next != NULL) {
2889		last = next;
2890		next = next->port_next;
2891	}
2892
2893	new = fctl_alloc_ulp_port(sleep);
2894	if (new == NULL) {
2895		return (new);
2896	}
2897
2898	new->port_handle = port_handle;
2899	if (last == NULL) {
2900		ulp_module->mod_ports = new;
2901	} else {
2902		last->port_next = new;
2903	}
2904
2905	return (new);
2906}
2907
2908
2909static fc_ulp_ports_t *
2910fctl_alloc_ulp_port(int sleep)
2911{
2912	fc_ulp_ports_t *new;
2913
2914	new = kmem_zalloc(sizeof (*new), sleep);
2915	if (new == NULL) {
2916		return (new);
2917	}
2918	mutex_init(&new->port_mutex, NULL, MUTEX_DRIVER, NULL);
2919
2920	return (new);
2921}
2922
2923
2924static int
2925fctl_remove_ulp_port(struct ulp_module *ulp_module,
2926    fc_local_port_t *port_handle)
2927{
2928	fc_ulp_ports_t *last;
2929	fc_ulp_ports_t *next;
2930
2931	ASSERT(RW_WRITE_HELD(&fctl_ulp_lock));
2932	ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
2933
2934	last = NULL;
2935	next = ulp_module->mod_ports;
2936
2937	while (next != NULL) {
2938		if (next->port_handle == port_handle) {
2939			if (next->port_dstate & ULP_PORT_ATTACH) {
2940				return (FC_FAILURE);
2941			}
2942			break;
2943		}
2944		last = next;
2945		next = next->port_next;
2946	}
2947
2948	if (next != NULL) {
2949		ASSERT((next->port_dstate & ULP_PORT_ATTACH) == 0);
2950
2951		if (last == NULL) {
2952			ulp_module->mod_ports = next->port_next;
2953		} else {
2954			last->port_next = next->port_next;
2955		}
2956		fctl_dealloc_ulp_port(next);
2957
2958		return (FC_SUCCESS);
2959	} else {
2960		return (FC_FAILURE);
2961	}
2962}
2963
2964
2965static void
2966fctl_dealloc_ulp_port(fc_ulp_ports_t *next)
2967{
2968	mutex_destroy(&next->port_mutex);
2969	kmem_free(next, sizeof (*next));
2970}
2971
2972
2973static fc_ulp_ports_t *
2974fctl_get_ulp_port(struct ulp_module *ulp_module, fc_local_port_t *port_handle)
2975{
2976	fc_ulp_ports_t *next;
2977
2978	ASSERT(RW_LOCK_HELD(&fctl_ulp_lock));
2979	ASSERT(RW_LOCK_HELD(&fctl_mod_ports_lock));
2980
2981	for (next = ulp_module->mod_ports; next != NULL;
2982	    next = next->port_next) {
2983		if (next->port_handle == port_handle) {
2984			return (next);
2985		}
2986	}
2987
2988	return (NULL);
2989}
2990
2991
2992/*
2993 * Pass state change notfications on to registered ULPs.
2994 *
2995 * Can issue wakeups to client callers who might be waiting for completions
2996 * on other threads.
2997 *
2998 * Caution: will silently deallocate any fc_remote_port_t and/or
2999 * fc_remote_node_t structs it finds that are not in use.
3000 */
3001void
3002fctl_ulp_statec_cb(void *arg)
3003{
3004	uint32_t		s_id;
3005	uint32_t		new_state;
3006	fc_local_port_t		*port;
3007	fc_ulp_ports_t		*ulp_port;
3008	fc_ulp_module_t		*mod;
3009	fc_port_clist_t		*clist = (fc_port_clist_t *)arg;
3010
3011	ASSERT(clist != NULL);
3012
3013	port = clist->clist_port;
3014
3015	mutex_enter(&port->fp_mutex);
3016	s_id = port->fp_port_id.port_id;
3017	mutex_exit(&port->fp_mutex);
3018
3019	switch (clist->clist_state) {
3020	case FC_STATE_ONLINE:
3021		new_state = FC_ULP_STATEC_ONLINE;
3022		break;
3023
3024	case FC_STATE_OFFLINE:
3025		if (clist->clist_len) {
3026			new_state = FC_ULP_STATEC_OFFLINE_TIMEOUT;
3027		} else {
3028			new_state = FC_ULP_STATEC_OFFLINE;
3029		}
3030		break;
3031
3032	default:
3033		new_state = FC_ULP_STATEC_DONT_CARE;
3034		break;
3035	}
3036
3037#ifdef	DEBUG
3038	/*
3039	 * sanity check for presence of OLD devices in the hash lists
3040	 */
3041	if (clist->clist_size) {
3042		int			count;
3043		fc_remote_port_t	*pd;
3044
3045		ASSERT(clist->clist_map != NULL);
3046		for (count = 0; count < clist->clist_len; count++) {
3047			if (clist->clist_map[count].map_state ==
3048			    PORT_DEVICE_INVALID) {
3049				la_wwn_t	pwwn;
3050				fc_portid_t	d_id;
3051
3052				pd = clist->clist_map[count].map_pd;
3053				if (pd != NULL) {
3054					mutex_enter(&pd->pd_mutex);
3055					pwwn = pd->pd_port_name;
3056					d_id = pd->pd_port_id;
3057					mutex_exit(&pd->pd_mutex);
3058
3059					pd = fctl_get_remote_port_by_pwwn(port,
3060					    &pwwn);
3061
3062					ASSERT(pd != clist->clist_map[count].
3063					    map_pd);
3064
3065					pd = fctl_get_remote_port_by_did(port,
3066					    d_id.port_id);
3067					ASSERT(pd != clist->clist_map[count].
3068					    map_pd);
3069				}
3070			}
3071		}
3072	}
3073#endif
3074
3075	/*
3076	 * Check for duplicate map entries
3077	 */
3078	if (clist->clist_size) {
3079		int			count;
3080		fc_remote_port_t	*pd1, *pd2;
3081
3082		ASSERT(clist->clist_map != NULL);
3083		for (count = 0; count < clist->clist_len-1; count++) {
3084			int count2;
3085
3086			pd1 = clist->clist_map[count].map_pd;
3087			if (pd1 == NULL) {
3088				continue;
3089			}
3090
3091			for (count2 = count+1;
3092			    count2 < clist->clist_len;
3093			    count2++) {
3094
3095				pd2 = clist->clist_map[count2].map_pd;
3096				if (pd2 == NULL) {
3097					continue;
3098				}
3099
3100				if (pd1 == pd2) {
3101					clist->clist_map[count].map_flags |=
3102					    PORT_DEVICE_DUPLICATE_MAP_ENTRY;
3103					break;
3104				}
3105			}
3106		}
3107	}
3108
3109
3110	rw_enter(&fctl_ulp_lock, RW_READER);
3111	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
3112		rw_enter(&fctl_mod_ports_lock, RW_READER);
3113		ulp_port = fctl_get_ulp_port(mod, port);
3114		rw_exit(&fctl_mod_ports_lock);
3115
3116		if (ulp_port == NULL) {
3117			continue;
3118		}
3119
3120		mutex_enter(&ulp_port->port_mutex);
3121		if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
3122			mutex_exit(&ulp_port->port_mutex);
3123			continue;
3124		}
3125
3126		switch (ulp_port->port_statec) {
3127		case FC_ULP_STATEC_DONT_CARE:
3128			if (ulp_port->port_statec != new_state) {
3129				ulp_port->port_statec = new_state;
3130			}
3131			break;
3132
3133		case FC_ULP_STATEC_ONLINE:
3134		case FC_ULP_STATEC_OFFLINE:
3135			if (ulp_port->port_statec == new_state) {
3136				mutex_exit(&ulp_port->port_mutex);
3137				continue;
3138			}
3139			ulp_port->port_statec = new_state;
3140			break;
3141
3142		case FC_ULP_STATEC_OFFLINE_TIMEOUT:
3143			if (ulp_port->port_statec == new_state ||
3144			    new_state == FC_ULP_STATEC_OFFLINE) {
3145				mutex_exit(&ulp_port->port_mutex);
3146				continue;
3147			}
3148			ulp_port->port_statec = new_state;
3149			break;
3150
3151		default:
3152			ASSERT(0);
3153			break;
3154		}
3155
3156		mod->mod_info->ulp_statec_callback(
3157		    mod->mod_info->ulp_handle, (opaque_t)port,
3158		    clist->clist_state, clist->clist_flags,
3159		    clist->clist_map, clist->clist_len, s_id);
3160
3161		mutex_exit(&ulp_port->port_mutex);
3162	}
3163	rw_exit(&fctl_ulp_lock);
3164
3165	if (clist->clist_size) {
3166		int			count;
3167		fc_remote_node_t	*node;
3168		fc_remote_port_t	*pd;
3169
3170		ASSERT(clist->clist_map != NULL);
3171		for (count = 0; count < clist->clist_len; count++) {
3172
3173			if ((pd = clist->clist_map[count].map_pd) == NULL) {
3174				continue;
3175			}
3176
3177			mutex_enter(&pd->pd_mutex);
3178
3179			pd->pd_ref_count--;
3180			ASSERT(pd->pd_ref_count >= 0);
3181
3182			if (clist->clist_map[count].map_state !=
3183			    PORT_DEVICE_INVALID) {
3184				mutex_exit(&pd->pd_mutex);
3185				continue;
3186			}
3187
3188			node = pd->pd_remote_nodep;
3189			pd->pd_aux_flags &= ~PD_GIVEN_TO_ULPS;
3190
3191			mutex_exit(&pd->pd_mutex);
3192
3193			/*
3194			 * This fc_remote_port_t is no longer referenced
3195			 * by any ULPs. Deallocate it if its pd_ref_count
3196			 * has reached zero.
3197			 */
3198			if ((fctl_destroy_remote_port(port, pd) == 0) &&
3199			    (node != NULL)) {
3200				fctl_destroy_remote_node(node);
3201			}
3202		}
3203
3204		kmem_free(clist->clist_map,
3205		    sizeof (*(clist->clist_map)) * clist->clist_size);
3206	}
3207
3208	if (clist->clist_wait) {
3209		mutex_enter(&clist->clist_mutex);
3210		clist->clist_wait = 0;
3211		cv_signal(&clist->clist_cv);
3212		mutex_exit(&clist->clist_mutex);
3213	} else {
3214		kmem_free(clist, sizeof (*clist));
3215	}
3216}
3217
3218
3219/*
3220 * Allocate an fc_remote_node_t struct to represent a remote node for the
3221 * given nwwn.	This will also add the nwwn to the global nwwn table.
3222 *
3223 * Returns a pointer to the newly-allocated struct.  Returns NULL if
3224 * the kmem_zalloc fails or if the enlist_wwn attempt fails.
3225 */
3226fc_remote_node_t *
3227fctl_create_remote_node(la_wwn_t *nwwn, int sleep)
3228{
3229	fc_remote_node_t *rnodep;
3230
3231	if ((rnodep = kmem_zalloc(sizeof (*rnodep), sleep)) == NULL) {
3232		return (NULL);
3233	}
3234
3235	mutex_init(&rnodep->fd_mutex, NULL, MUTEX_DRIVER, NULL);
3236
3237	rnodep->fd_node_name = *nwwn;
3238	rnodep->fd_flags = FC_REMOTE_NODE_VALID;
3239	rnodep->fd_numports = 1;
3240
3241	if (fctl_enlist_nwwn_table(rnodep, sleep) != FC_SUCCESS) {
3242		mutex_destroy(&rnodep->fd_mutex);
3243		kmem_free(rnodep, sizeof (*rnodep));
3244		return (NULL);
3245	}
3246
3247	return (rnodep);
3248}
3249
3250/*
3251 * Deconstruct and free the given fc_remote_node_t struct (remote node struct).
3252 * Silently skips the deconstruct/free if there are any fc_remote_port_t
3253 * (remote port device) structs still referenced by the given
3254 * fc_remote_node_t struct.
3255 */
3256void
3257fctl_destroy_remote_node(fc_remote_node_t *rnodep)
3258{
3259	mutex_enter(&rnodep->fd_mutex);
3260
3261	/*
3262	 * Look at the count and linked list of of remote ports
3263	 * (fc_remote_port_t structs); bail if these indicate that
3264	 * given fc_remote_node_t may be in use.
3265	 */
3266	if (rnodep->fd_numports != 0 || rnodep->fd_portlistp) {
3267		mutex_exit(&rnodep->fd_mutex);
3268		return;
3269	}
3270
3271	mutex_exit(&rnodep->fd_mutex);
3272
3273	mutex_destroy(&rnodep->fd_mutex);
3274	kmem_free(rnodep, sizeof (*rnodep));
3275}
3276
3277
3278/*
3279 * Add the given fc_remote_node_t to the global fctl_nwwn_hash_table[]. This
3280 * uses the nwwn in the fd_node_name.raw_wwn of the given struct.
3281 * This only fails if the kmem_zalloc fails.  This does not check for a
3282 * unique or pre-existing nwwn in the fctl_nwwn_hash_table[].
3283 * This is only called from fctl_create_remote_node().
3284 */
3285int
3286fctl_enlist_nwwn_table(fc_remote_node_t *rnodep, int sleep)
3287{
3288	int			index;
3289	fctl_nwwn_elem_t	*new;
3290	fctl_nwwn_list_t	*head;
3291
3292	ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
3293
3294	if ((new = kmem_zalloc(sizeof (*new), sleep)) == NULL) {
3295		return (FC_FAILURE);
3296	}
3297
3298	mutex_enter(&fctl_nwwn_hash_mutex);
3299	new->fne_nodep = rnodep;
3300
3301	mutex_enter(&rnodep->fd_mutex);
3302	ASSERT(fctl_is_wwn_zero(&rnodep->fd_node_name) == FC_FAILURE);
3303	index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
3304	    fctl_nwwn_table_size);
3305	mutex_exit(&rnodep->fd_mutex);
3306
3307	head = &fctl_nwwn_hash_table[index];
3308
3309	/* Link it in at the head of the hash list */
3310	new->fne_nextp = head->fnl_headp;
3311	head->fnl_headp = new;
3312
3313	mutex_exit(&fctl_nwwn_hash_mutex);
3314
3315	return (FC_SUCCESS);
3316}
3317
3318
3319/*
3320 * Remove the given fc_remote_node_t from the global fctl_nwwn_hash_table[].
3321 * This uses the nwwn in the fd_node_name.raw_wwn of the given struct.
3322 */
3323void
3324fctl_delist_nwwn_table(fc_remote_node_t *rnodep)
3325{
3326	int			index;
3327	fctl_nwwn_list_t	*head;
3328	fctl_nwwn_elem_t	*elem;
3329	fctl_nwwn_elem_t	*prev;
3330
3331	ASSERT(MUTEX_HELD(&fctl_nwwn_hash_mutex));
3332	ASSERT(MUTEX_HELD(&rnodep->fd_mutex));
3333
3334	index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
3335	    fctl_nwwn_table_size);
3336
3337	head = &fctl_nwwn_hash_table[index];
3338	elem = head->fnl_headp;
3339	prev = NULL;
3340
3341	while (elem != NULL) {
3342		if (elem->fne_nodep == rnodep) {
3343			/*
3344			 * Found it -- unlink it from the list & decrement
3345			 * the count for the hash chain.
3346			 */
3347			if (prev == NULL) {
3348				head->fnl_headp = elem->fne_nextp;
3349			} else {
3350				prev->fne_nextp = elem->fne_nextp;
3351			}
3352			break;
3353		}
3354		prev = elem;
3355		elem = elem->fne_nextp;
3356	}
3357
3358	if (elem != NULL) {
3359		kmem_free(elem, sizeof (*elem));
3360	}
3361}
3362
3363
3364/*
3365 * Returns a reference to an fc_remote_node_t struct for the given node_wwn.
3366 * Looks in the global fctl_nwwn_hash_table[]. Identical to the
3367 * fctl_lock_remote_node_by_nwwn() function, except that this does NOT increment
3368 * the fc_count reference count in the f_device_t before returning.
3369 *
3370 * This function is called by: fctl_create_remote_port_t().
3371 *
3372 * OLD COMMENT:
3373 * Note: The calling thread needs to make sure it isn't holding any device
3374 * mutex (more so the fc_remote_node_t that could potentially have this wwn).
3375 */
3376fc_remote_node_t *
3377fctl_get_remote_node_by_nwwn(la_wwn_t *node_wwn)
3378{
3379	int			index;
3380	fctl_nwwn_elem_t	*elem;
3381	fc_remote_node_t	*next;
3382	fc_remote_node_t	*rnodep = NULL;
3383
3384	index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
3385	    fctl_nwwn_table_size);
3386	ASSERT(index >= 0 && index < fctl_nwwn_table_size);
3387
3388	mutex_enter(&fctl_nwwn_hash_mutex);
3389	elem = fctl_nwwn_hash_table[index].fnl_headp;
3390	while (elem != NULL) {
3391		next = elem->fne_nodep;
3392		if (next != NULL) {
3393			mutex_enter(&next->fd_mutex);
3394			if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
3395				rnodep = next;
3396				mutex_exit(&next->fd_mutex);
3397				break;
3398			}
3399			mutex_exit(&next->fd_mutex);
3400		}
3401		elem = elem->fne_nextp;
3402	}
3403	mutex_exit(&fctl_nwwn_hash_mutex);
3404
3405	return (rnodep);
3406}
3407
3408
3409/*
3410 * Returns a reference to an fc_remote_node_t struct for the given node_wwn.
3411 * Looks in the global fctl_nwwn_hash_table[]. Increments the fd_numports
3412 * reference count in the f_device_t before returning.
3413 *
3414 * This function is only called by fctl_create_remote_port_t().
3415 */
3416fc_remote_node_t *
3417fctl_lock_remote_node_by_nwwn(la_wwn_t *node_wwn)
3418{
3419	int			index;
3420	fctl_nwwn_elem_t	*elem;
3421	fc_remote_node_t	*next;
3422	fc_remote_node_t	*rnodep = NULL;
3423
3424	index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
3425	    fctl_nwwn_table_size);
3426	ASSERT(index >= 0 && index < fctl_nwwn_table_size);
3427
3428	mutex_enter(&fctl_nwwn_hash_mutex);
3429	elem = fctl_nwwn_hash_table[index].fnl_headp;
3430	while (elem != NULL) {
3431		next = elem->fne_nodep;
3432		if (next != NULL) {
3433			mutex_enter(&next->fd_mutex);
3434			if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
3435				rnodep = next;
3436				rnodep->fd_numports++;
3437				mutex_exit(&next->fd_mutex);
3438				break;
3439			}
3440			mutex_exit(&next->fd_mutex);
3441		}
3442		elem = elem->fne_nextp;
3443	}
3444	mutex_exit(&fctl_nwwn_hash_mutex);
3445
3446	return (rnodep);
3447}
3448
3449
3450/*
3451 * Allocate and initialize an fc_remote_port_t struct & returns a pointer to
3452 * the newly allocated struct.	Only fails if the kmem_zalloc() fails.
3453 */
3454fc_remote_port_t *
3455fctl_alloc_remote_port(fc_local_port_t *port, la_wwn_t *port_wwn,
3456    uint32_t d_id, uchar_t recepient, int sleep)
3457{
3458	fc_remote_port_t *pd;
3459
3460	ASSERT(MUTEX_HELD(&port->fp_mutex));
3461	ASSERT(FC_IS_REAL_DEVICE(d_id));
3462
3463	if ((pd = kmem_zalloc(sizeof (*pd), sleep)) == NULL) {
3464		return (NULL);
3465	}
3466	fctl_tc_constructor(&pd->pd_logo_tc, FC_LOGO_TOLERANCE_LIMIT,
3467	    FC_LOGO_TOLERANCE_TIME_LIMIT);
3468
3469	mutex_init(&pd->pd_mutex, NULL, MUTEX_DRIVER, NULL);
3470
3471	pd->pd_port_id.port_id = d_id;
3472	pd->pd_port_name = *port_wwn;
3473	pd->pd_port = port;
3474	pd->pd_state = PORT_DEVICE_VALID;
3475	pd->pd_type = PORT_DEVICE_NEW;
3476	pd->pd_recepient = recepient;
3477
3478	return (pd);
3479}
3480
3481
3482/*
3483 * Deconstruct and free the given fc_remote_port_t struct (unconditionally).
3484 */
3485void
3486fctl_dealloc_remote_port(fc_remote_port_t *pd)
3487{
3488	ASSERT(!MUTEX_HELD(&pd->pd_mutex));
3489
3490	fctl_tc_destructor(&pd->pd_logo_tc);
3491	mutex_destroy(&pd->pd_mutex);
3492	kmem_free(pd, sizeof (*pd));
3493}
3494
3495/*
3496 * Add the given fc_remote_port_t onto the linked list of remote port
3497 * devices associated with the given fc_remote_node_t. Does NOT add the
3498 * fc_remote_port_t to the list if already exists on the list.
3499 */
3500void
3501fctl_link_remote_port_to_remote_node(fc_remote_node_t *rnodep,
3502    fc_remote_port_t *pd)
3503{
3504	fc_remote_port_t *last;
3505	fc_remote_port_t *ports;
3506
3507	mutex_enter(&rnodep->fd_mutex);
3508
3509	last = NULL;
3510	for (ports = rnodep->fd_portlistp; ports != NULL;
3511	    ports = ports->pd_port_next) {
3512		if (ports == pd) {
3513			/*
3514			 * The given fc_remote_port_t is already on the linked
3515			 * list chain for the given remote node, so bail now.
3516			 */
3517			mutex_exit(&rnodep->fd_mutex);
3518			return;
3519		}
3520		last = ports;
3521	}
3522
3523	/* Add the fc_remote_port_t to the tail of the linked list */
3524	if (last != NULL) {
3525		last->pd_port_next = pd;
3526	} else {
3527		rnodep->fd_portlistp = pd;
3528	}
3529	pd->pd_port_next = NULL;
3530
3531	/*
3532	 * Link the fc_remote_port_t back to the associated fc_remote_node_t.
3533	 */
3534	mutex_enter(&pd->pd_mutex);
3535	pd->pd_remote_nodep = rnodep;
3536	mutex_exit(&pd->pd_mutex);
3537
3538	mutex_exit(&rnodep->fd_mutex);
3539}
3540
3541
3542/*
3543 * Remove the specified fc_remote_port_t from the linked list of remote ports
3544 * for the given fc_remote_node_t.
3545 *
3546 * Returns a count of the _remaining_ fc_remote_port_t structs on the linked
3547 * list of the fc_remote_node_t.
3548 *
3549 * The fd_numports on the given fc_remote_node_t is decremented, and if
3550 * it hits zero then this function also removes the fc_remote_node_t from the
3551 * global fctl_nwwn_hash_table[]. This appears to be the ONLY WAY that entries
3552 * are removed from the fctl_nwwn_hash_table[].
3553 */
3554int
3555fctl_unlink_remote_port_from_remote_node(fc_remote_node_t *rnodep,
3556    fc_remote_port_t *pd)
3557{
3558	int			rcount = 0;
3559	fc_remote_port_t	*last;
3560	fc_remote_port_t	*ports;
3561
3562	ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
3563	ASSERT(!MUTEX_HELD(&pd->pd_mutex));
3564
3565	last = NULL;
3566
3567	mutex_enter(&fctl_nwwn_hash_mutex);
3568
3569	mutex_enter(&rnodep->fd_mutex);
3570
3571	/*
3572	 * Go thru the linked list of fc_remote_port_t structs for the given
3573	 * fc_remote_node_t; try to find the specified fc_remote_port_t (pd).
3574	 */
3575	ports = rnodep->fd_portlistp;
3576	while (ports != NULL) {
3577		if (ports == pd) {
3578			break;	/* Found the requested fc_remote_port_t */
3579		}
3580		last = ports;
3581		ports = ports->pd_port_next;
3582	}
3583
3584	if (ports) {
3585		rcount = --rnodep->fd_numports;
3586		if (rcount == 0) {
3587			/* Note: this is only ever called from here */
3588			fctl_delist_nwwn_table(rnodep);
3589		}
3590		if (last) {
3591			last->pd_port_next = pd->pd_port_next;
3592		} else {
3593			rnodep->fd_portlistp = pd->pd_port_next;
3594		}
3595		mutex_enter(&pd->pd_mutex);
3596		pd->pd_remote_nodep = NULL;
3597		mutex_exit(&pd->pd_mutex);
3598	}
3599
3600	pd->pd_port_next = NULL;
3601
3602	mutex_exit(&rnodep->fd_mutex);
3603	mutex_exit(&fctl_nwwn_hash_mutex);
3604
3605	return (rcount);
3606}
3607
3608
3609/*
3610 * Add the given fc_remote_port_t struct to the d_id table in the given
3611 * fc_local_port_t struct.  Hashes based upon the pd->pd_port_id.port_id in the
3612 * fc_remote_port_t.
3613 *
3614 * No memory allocs are required, so this never fails, but it does use the
3615 * (pd->pd_aux_flags & PD_IN_DID_QUEUE) to keep duplicates off the list.
3616 * (There does not seem to be a way to tell the caller that a duplicate
3617 * exists.)
3618 */
3619void
3620fctl_enlist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
3621{
3622	struct d_id_hash *head;
3623
3624	ASSERT(MUTEX_HELD(&port->fp_mutex));
3625	ASSERT(MUTEX_HELD(&pd->pd_mutex));
3626
3627	if (pd->pd_aux_flags & PD_IN_DID_QUEUE) {
3628		return;
3629	}
3630
3631	head = &port->fp_did_table[D_ID_HASH_FUNC(pd->pd_port_id.port_id,
3632	    did_table_size)];
3633
3634#ifdef	DEBUG
3635	{
3636		int			index;
3637		fc_remote_port_t	*tmp_pd;
3638		struct d_id_hash	*tmp_head;
3639
3640		/*
3641		 * Search down in each bucket for a duplicate pd
3642		 * Also search for duplicate D_IDs
3643		 * This DEBUG code will force an ASSERT if a duplicate
3644		 * is ever found.
3645		 */
3646		for (index = 0; index < did_table_size; index++) {
3647			tmp_head = &port->fp_did_table[index];
3648
3649			tmp_pd = tmp_head->d_id_head;
3650			while (tmp_pd != NULL) {
3651				ASSERT(tmp_pd != pd);
3652
3653				if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
3654				    tmp_pd->pd_type != PORT_DEVICE_OLD) {
3655					ASSERT(tmp_pd->pd_port_id.port_id !=
3656					    pd->pd_port_id.port_id);
3657				}
3658
3659				tmp_pd = tmp_pd->pd_did_hnext;
3660			}
3661		}
3662	}
3663
3664	bzero(pd->pd_d_stack, sizeof (pd->pd_d_stack));
3665	pd->pd_d_depth = getpcstack(pd->pd_d_stack, FC_STACK_DEPTH);
3666#endif
3667
3668	pd->pd_did_hnext = head->d_id_head;
3669	head->d_id_head = pd;
3670
3671	pd->pd_aux_flags |= PD_IN_DID_QUEUE;
3672	head->d_id_count++;
3673}
3674
3675
3676/*
3677 * Remove the given fc_remote_port_t struct from the d_id table in the given
3678 * fc_local_port_t struct.  Hashes based upon the pd->pd_port_id.port_id in the
3679 * fc_remote_port_t.
3680 *
3681 * Does nothing if the requested fc_remote_port_t was not found.
3682 */
3683void
3684fctl_delist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
3685{
3686	uint32_t		d_id;
3687	struct d_id_hash	*head;
3688	fc_remote_port_t	*pd_next;
3689	fc_remote_port_t	*last;
3690
3691	ASSERT(MUTEX_HELD(&port->fp_mutex));
3692	ASSERT(MUTEX_HELD(&pd->pd_mutex));
3693
3694	d_id = pd->pd_port_id.port_id;
3695	head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3696
3697	pd_next = head->d_id_head;
3698	last = NULL;
3699	while (pd_next != NULL) {
3700		if (pd == pd_next) {
3701			break;	/* Found the given fc_remote_port_t */
3702		}
3703		last = pd_next;
3704		pd_next = pd_next->pd_did_hnext;
3705	}
3706
3707	if (pd_next) {
3708		/*
3709		 * Found the given fc_remote_port_t; now remove it from the
3710		 * d_id list.
3711		 */
3712		head->d_id_count--;
3713		if (last == NULL) {
3714			head->d_id_head = pd->pd_did_hnext;
3715		} else {
3716			last->pd_did_hnext = pd->pd_did_hnext;
3717		}
3718		pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
3719		pd->pd_did_hnext = NULL;
3720	}
3721}
3722
3723
3724/*
3725 * Add the given fc_remote_port_t struct to the pwwn table in the given
3726 * fc_local_port_t struct.  Hashes based upon the pd->pd_port_name.raw_wwn
3727 * in the fc_remote_port_t.
3728 *
3729 * No memory allocs are required, so this never fails.
3730 */
3731void
3732fctl_enlist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
3733{
3734	int index;
3735	struct pwwn_hash *head;
3736
3737	ASSERT(MUTEX_HELD(&port->fp_mutex));
3738	ASSERT(MUTEX_HELD(&pd->pd_mutex));
3739
3740	ASSERT(fctl_is_wwn_zero(&pd->pd_port_name) == FC_FAILURE);
3741
3742	index = HASH_FUNC(WWN_HASH_KEY(pd->pd_port_name.raw_wwn),
3743	    pwwn_table_size);
3744
3745	head = &port->fp_pwwn_table[index];
3746
3747#ifdef	DEBUG
3748	{
3749		int			index;
3750		fc_remote_port_t	*tmp_pd;
3751		struct pwwn_hash	*tmp_head;
3752
3753		/*
3754		 * Search down in each bucket for a duplicate pd
3755		 * Search also for a duplicate WWN
3756		 * Throw an ASSERT if any duplicate is found.
3757		 */
3758		for (index = 0; index < pwwn_table_size; index++) {
3759			tmp_head = &port->fp_pwwn_table[index];
3760
3761			tmp_pd = tmp_head->pwwn_head;
3762			while (tmp_pd != NULL) {
3763				ASSERT(tmp_pd != pd);
3764
3765				if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
3766				    tmp_pd->pd_type != PORT_DEVICE_OLD) {
3767					ASSERT(fctl_wwn_cmp(
3768					    &tmp_pd->pd_port_name,
3769					    &pd->pd_port_name) != 0);
3770				}
3771
3772				tmp_pd = tmp_pd->pd_wwn_hnext;
3773			}
3774		}
3775	}
3776
3777	bzero(pd->pd_w_stack, sizeof (pd->pd_w_stack));
3778	pd->pd_w_depth = getpcstack(pd->pd_w_stack, FC_STACK_DEPTH);
3779#endif /* DEBUG */
3780
3781	pd->pd_wwn_hnext = head->pwwn_head;
3782	head->pwwn_head = pd;
3783
3784	head->pwwn_count++;
3785	/*
3786	 * Make sure we tie fp_dev_count to the size of the
3787	 * pwwn_table
3788	 */
3789	port->fp_dev_count++;
3790}
3791
3792
3793/*
3794 * Remove the given fc_remote_port_t struct from the pwwn table in the given
3795 * fc_local_port_t struct.  Hashes based upon the pd->pd_port_name.raw_wwn
3796 * in the fc_remote_port_t.
3797 *
3798 * Does nothing if the requested fc_remote_port_t was not found.
3799 */
3800void
3801fctl_delist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
3802{
3803	int			index;
3804	la_wwn_t		pwwn;
3805	struct pwwn_hash	*head;
3806	fc_remote_port_t	*pd_next;
3807	fc_remote_port_t	*last;
3808
3809	ASSERT(MUTEX_HELD(&port->fp_mutex));
3810	ASSERT(MUTEX_HELD(&pd->pd_mutex));
3811
3812	pwwn = pd->pd_port_name;
3813	index = HASH_FUNC(WWN_HASH_KEY(pwwn.raw_wwn), pwwn_table_size);
3814
3815	head = &port->fp_pwwn_table[index];
3816
3817	last = NULL;
3818	pd_next = head->pwwn_head;
3819	while (pd_next != NULL) {
3820		if (pd_next == pd) {
3821			break;	/* Found the given fc_remote_port_t */
3822		}
3823		last = pd_next;
3824		pd_next = pd_next->pd_wwn_hnext;
3825	}
3826
3827	if (pd_next) {
3828		/*
3829		 * Found the given fc_remote_port_t; now remove it from the
3830		 * pwwn list.
3831		 */
3832		head->pwwn_count--;
3833		/*
3834		 * Make sure we tie fp_dev_count to the size of the
3835		 * pwwn_table
3836		 */
3837		port->fp_dev_count--;
3838		if (last == NULL) {
3839			head->pwwn_head = pd->pd_wwn_hnext;
3840		} else {
3841			last->pd_wwn_hnext = pd->pd_wwn_hnext;
3842		}
3843		pd->pd_wwn_hnext = NULL;
3844	}
3845}
3846
3847
3848/*
3849 * Looks in the d_id table of the specified fc_local_port_t for the
3850 * fc_remote_port_t that matches the given d_id.  Hashes based upon
3851 * the given d_id.
3852 * Returns a pointer to the fc_remote_port_t struct, but does not update any
3853 * reference counts or otherwise indicate that the fc_remote_port_t is in
3854 * use.
3855 */
3856fc_remote_port_t *
3857fctl_get_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
3858{
3859	struct d_id_hash	*head;
3860	fc_remote_port_t	*pd;
3861
3862	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3863
3864	mutex_enter(&port->fp_mutex);
3865
3866	head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3867
3868	pd = head->d_id_head;
3869	while (pd != NULL) {
3870		mutex_enter(&pd->pd_mutex);
3871		if (pd->pd_port_id.port_id == d_id) {
3872			/* Match found -- break out of the loop */
3873			mutex_exit(&pd->pd_mutex);
3874			break;
3875		}
3876		mutex_exit(&pd->pd_mutex);
3877		pd = pd->pd_did_hnext;
3878	}
3879
3880	mutex_exit(&port->fp_mutex);
3881
3882	return (pd);
3883}
3884
3885
3886#ifndef	__lock_lint		/* uncomment when there is a consumer */
3887
3888void
3889fc_ulp_hold_remote_port(opaque_t port_handle)
3890{
3891	fc_remote_port_t *pd = port_handle;
3892
3893	mutex_enter(&pd->pd_mutex);
3894	pd->pd_ref_count++;
3895	mutex_exit(&pd->pd_mutex);
3896}
3897
3898/*
3899 * Looks in the d_id table of the specified fc_local_port_t for the
3900 * fc_remote_port_t that matches the given d_id.  Hashes based upon
3901 * the given d_id. Returns a pointer to the fc_remote_port_t struct.
3902 *
3903 * Increments pd_ref_count in the fc_remote_port_t if the
3904 * fc_remote_port_t is found at the given d_id.
3905 *
3906 * The fc_remote_port_t is ignored (treated as non-existent) if either
3907 * its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
3908 */
3909fc_remote_port_t *
3910fctl_hold_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
3911{
3912	struct d_id_hash	*head;
3913	fc_remote_port_t	*pd;
3914
3915	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3916
3917	mutex_enter(&port->fp_mutex);
3918
3919	head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
3920
3921	pd = head->d_id_head;
3922	while (pd != NULL) {
3923		mutex_enter(&pd->pd_mutex);
3924		if (pd->pd_port_id.port_id == d_id && pd->pd_state !=
3925		    PORT_DEVICE_INVALID && pd->pd_type != PORT_DEVICE_OLD) {
3926			ASSERT(pd->pd_ref_count >= 0);
3927			pd->pd_ref_count++;
3928			mutex_exit(&pd->pd_mutex);
3929			break;
3930		}
3931		mutex_exit(&pd->pd_mutex);
3932		pd = pd->pd_did_hnext;
3933	}
3934
3935	mutex_exit(&port->fp_mutex);
3936
3937	return (pd);
3938}
3939
3940#endif /* __lock_lint */
3941
3942/*
3943 * Looks in the pwwn table of the specified fc_local_port_t for the
3944 * fc_remote_port_t that matches the given pwwn.  Hashes based upon the
3945 * given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct,
3946 * but does not update any reference counts or otherwise indicate that
3947 * the fc_remote_port_t is in use.
3948 */
3949fc_remote_port_t *
3950fctl_get_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
3951{
3952	int			index;
3953	struct pwwn_hash	*head;
3954	fc_remote_port_t	*pd;
3955
3956	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3957
3958	mutex_enter(&port->fp_mutex);
3959
3960	index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
3961	head = &port->fp_pwwn_table[index];
3962
3963	pd = head->pwwn_head;
3964	while (pd != NULL) {
3965		mutex_enter(&pd->pd_mutex);
3966		if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
3967			mutex_exit(&pd->pd_mutex);
3968			break;
3969		}
3970		mutex_exit(&pd->pd_mutex);
3971		pd = pd->pd_wwn_hnext;
3972	}
3973
3974	mutex_exit(&port->fp_mutex);
3975
3976	return (pd);
3977}
3978
3979
3980/*
3981 * Basically the same as fctl_get_remote_port_by_pwwn(), but requires that
3982 * the caller already hold the fp_mutex in the fc_local_port_t struct.
3983 */
3984fc_remote_port_t *
3985fctl_get_remote_port_by_pwwn_mutex_held(fc_local_port_t *port, la_wwn_t *pwwn)
3986{
3987	int			index;
3988	struct pwwn_hash	*head;
3989	fc_remote_port_t	*pd;
3990
3991	ASSERT(MUTEX_HELD(&port->fp_mutex));
3992
3993	index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
3994	head = &port->fp_pwwn_table[index];
3995
3996	pd = head->pwwn_head;
3997	while (pd != NULL) {
3998		mutex_enter(&pd->pd_mutex);
3999		if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
4000			mutex_exit(&pd->pd_mutex);
4001			break;
4002		}
4003		mutex_exit(&pd->pd_mutex);
4004		pd = pd->pd_wwn_hnext;
4005	}
4006
4007	return (pd);
4008}
4009
4010
4011/*
4012 * Looks in the pwwn table of the specified fc_local_port_t for the
4013 * fc_remote_port_t that matches the given d_id.  Hashes based upon the
4014 * given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct.
4015 *
4016 * Increments pd_ref_count in the fc_remote_port_t if the
4017 * fc_remote_port_t is found at the given pwwn.
4018 *
4019 * The fc_remote_port_t is ignored (treated as non-existent) if either
4020 * its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
4021 */
4022fc_remote_port_t *
4023fctl_hold_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
4024{
4025	int			index;
4026	struct pwwn_hash	*head;
4027	fc_remote_port_t	*pd;
4028
4029	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4030
4031	mutex_enter(&port->fp_mutex);
4032
4033	index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
4034	head = &port->fp_pwwn_table[index];
4035
4036	pd = head->pwwn_head;
4037	while (pd != NULL) {
4038		mutex_enter(&pd->pd_mutex);
4039		if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0 &&
4040		    pd->pd_state != PORT_DEVICE_INVALID &&
4041		    pd->pd_type != PORT_DEVICE_OLD) {
4042			ASSERT(pd->pd_ref_count >= 0);
4043			pd->pd_ref_count++;
4044			mutex_exit(&pd->pd_mutex);
4045			break;
4046		}
4047		mutex_exit(&pd->pd_mutex);
4048		pd = pd->pd_wwn_hnext;
4049	}
4050
4051	mutex_exit(&port->fp_mutex);
4052
4053	return (pd);
4054}
4055
4056
4057/*
4058 * Unconditionally decrement pd_ref_count in the given fc_remote_port_t
4059 * struct.
4060 *
4061 * If pd_ref_count reaches zero, then this function will see if the
4062 * fc_remote_port_t has been marked for deallocation. If so (and also if there
4063 * are no other potential operations in progress, as indicated by the
4064 * PD_ELS_IN_PROGRESS & PD_ELS_MARK settings in the pd_flags), then
4065 * fctl_destroy_remote_port_t() is called to deconstruct/free the given
4066 * fc_remote_port_t (which will also remove it from the d_id and pwwn tables
4067 * on the associated fc_local_port_t).	If the associated fc_remote_node_t is no
4068 * longer in use, then it too is deconstructed/freed.
4069 */
4070void
4071fctl_release_remote_port(fc_remote_port_t *pd)
4072{
4073	int			remove = 0;
4074	fc_remote_node_t	*node;
4075	fc_local_port_t		*port;
4076
4077	mutex_enter(&pd->pd_mutex);
4078	port = pd->pd_port;
4079
4080	ASSERT(pd->pd_ref_count > 0);
4081	pd->pd_ref_count--;
4082	if (pd->pd_ref_count == 0 &&
4083	    (pd->pd_aux_flags & PD_NEEDS_REMOVAL) &&
4084	    (pd->pd_flags != PD_ELS_IN_PROGRESS) &&
4085	    (pd->pd_flags != PD_ELS_MARK)) {
4086		remove = 1;
4087		pd->pd_aux_flags &= ~PD_NEEDS_REMOVAL;
4088	}
4089	node = pd->pd_remote_nodep;
4090	ASSERT(node != NULL);
4091
4092	mutex_exit(&pd->pd_mutex);
4093
4094	if (remove) {
4095		/*
4096		 * The fc_remote_port_t struct has to go away now, so call the
4097		 * cleanup function to get it off the various lists and remove
4098		 * references to it in any other associated structs.
4099		 */
4100		if (fctl_destroy_remote_port(port, pd) == 0) {
4101			/*
4102			 * No more fc_remote_port_t references found in the
4103			 * associated fc_remote_node_t, so deallocate the
4104			 * fc_remote_node_t (if it even exists).
4105			 */
4106			if (node) {
4107				fctl_destroy_remote_node(node);
4108			}
4109		}
4110	}
4111}
4112
4113
4114void
4115fctl_fillout_map(fc_local_port_t *port, fc_portmap_t **map, uint32_t *len,
4116    int whole_map, int justcopy, int orphan)
4117{
4118	int			index;
4119	int			listlen;
4120	int			full_list;
4121	int			initiator;
4122	uint32_t		topology;
4123	struct pwwn_hash	*head;
4124	fc_remote_port_t	*pd;
4125	fc_remote_port_t	*old_pd;
4126	fc_remote_port_t	*last_pd;
4127	fc_portmap_t		*listptr;
4128
4129	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4130
4131	mutex_enter(&port->fp_mutex);
4132
4133	topology = port->fp_topology;
4134
4135	if (orphan) {
4136		ASSERT(!FC_IS_TOP_SWITCH(topology));
4137	}
4138
4139	for (full_list = listlen = index = 0;
4140	    index < pwwn_table_size; index++) {
4141		head = &port->fp_pwwn_table[index];
4142		pd = head->pwwn_head;
4143		while (pd != NULL) {
4144			full_list++;
4145			mutex_enter(&pd->pd_mutex);
4146			if (pd->pd_type != PORT_DEVICE_NOCHANGE) {
4147				listlen++;
4148			}
4149			mutex_exit(&pd->pd_mutex);
4150			pd = pd->pd_wwn_hnext;
4151		}
4152	}
4153
4154	if (whole_map == 0) {
4155		if (listlen == 0 && *len == 0) {
4156			*map = NULL;
4157			*len = listlen;
4158			mutex_exit(&port->fp_mutex);
4159			return;
4160		}
4161	} else {
4162		if (full_list == 0 && *len == 0) {
4163			*map = NULL;
4164			*len = full_list;
4165			mutex_exit(&port->fp_mutex);
4166			return;
4167		}
4168	}
4169
4170	if (*len == 0) {
4171		ASSERT(*map == NULL);
4172		if (whole_map == 0) {
4173			listptr = *map = kmem_zalloc(
4174			    sizeof (*listptr) * listlen, KM_SLEEP);
4175			*len = listlen;
4176		} else {
4177			listptr = *map = kmem_zalloc(
4178			    sizeof (*listptr) * full_list, KM_SLEEP);
4179			*len = full_list;
4180		}
4181	} else {
4182		/*
4183		 * By design this routine mandates the callers to
4184		 * ask for a whole map when they specify the length
4185		 * and the listptr.
4186		 */
4187		ASSERT(whole_map == 1);
4188		if (*len < full_list) {
4189			*len = full_list;
4190			mutex_exit(&port->fp_mutex);
4191			return;
4192		}
4193		listptr = *map;
4194		*len = full_list;
4195	}
4196
4197	for (index = 0; index < pwwn_table_size; index++) {
4198		head = &port->fp_pwwn_table[index];
4199		last_pd = NULL;
4200		pd = head->pwwn_head;
4201		while (pd != NULL) {
4202			mutex_enter(&pd->pd_mutex);
4203			if ((whole_map == 0 &&
4204			    pd->pd_type == PORT_DEVICE_NOCHANGE) ||
4205			    pd->pd_state == PORT_DEVICE_INVALID) {
4206				mutex_exit(&pd->pd_mutex);
4207				last_pd = pd;
4208				pd = pd->pd_wwn_hnext;
4209				continue;
4210			}
4211			mutex_exit(&pd->pd_mutex);
4212
4213			fctl_copy_portmap(listptr, pd);
4214
4215			if (justcopy) {
4216				last_pd = pd;
4217				pd = pd->pd_wwn_hnext;
4218				listptr++;
4219				continue;
4220			}
4221
4222			mutex_enter(&pd->pd_mutex);
4223			ASSERT(pd->pd_state != PORT_DEVICE_INVALID);
4224			if (pd->pd_type == PORT_DEVICE_OLD) {
4225				listptr->map_pd = pd;
4226				listptr->map_state = pd->pd_state =
4227				    PORT_DEVICE_INVALID;
4228				/*
4229				 * Remove this from the PWWN hash table.
4230				 */
4231				old_pd = pd;
4232				pd = old_pd->pd_wwn_hnext;
4233
4234				if (last_pd == NULL) {
4235					ASSERT(old_pd == head->pwwn_head);
4236
4237					head->pwwn_head = pd;
4238				} else {
4239					last_pd->pd_wwn_hnext = pd;
4240				}
4241				head->pwwn_count--;
4242				/*
4243				 * Make sure we tie fp_dev_count to the size
4244				 * of the pwwn_table
4245				 */
4246				port->fp_dev_count--;
4247				old_pd->pd_wwn_hnext = NULL;
4248
4249				if (port->fp_topology == FC_TOP_PRIVATE_LOOP &&
4250				    port->fp_statec_busy && !orphan) {
4251					fctl_check_alpa_list(port, old_pd);
4252				}
4253
4254				/*
4255				 * Remove if the port device has stealthily
4256				 * present in the D_ID hash table
4257				 */
4258				fctl_delist_did_table(port, old_pd);
4259
4260				ASSERT(old_pd->pd_remote_nodep != NULL);
4261
4262				initiator = (old_pd->pd_recepient ==
4263				    PD_PLOGI_INITIATOR) ? 1 : 0;
4264
4265				mutex_exit(&old_pd->pd_mutex);
4266				mutex_exit(&port->fp_mutex);
4267
4268				if (orphan) {
4269					fctl_print_if_not_orphan(port, old_pd);
4270
4271					(void) fctl_add_orphan(port, old_pd,
4272					    KM_NOSLEEP);
4273				}
4274
4275				if (FC_IS_TOP_SWITCH(topology) && initiator) {
4276					(void) fctl_add_orphan(port, old_pd,
4277					    KM_NOSLEEP);
4278				}
4279				mutex_enter(&port->fp_mutex);
4280			} else {
4281				listptr->map_pd = pd;
4282				pd->pd_type = PORT_DEVICE_NOCHANGE;
4283				mutex_exit(&pd->pd_mutex);
4284				last_pd = pd;
4285				pd = pd->pd_wwn_hnext;
4286			}
4287			listptr++;
4288		}
4289	}
4290	mutex_exit(&port->fp_mutex);
4291}
4292
4293
4294job_request_t *
4295fctl_alloc_job(int job_code, int job_flags, void (*comp) (opaque_t, uchar_t),
4296    opaque_t arg, int sleep)
4297{
4298	job_request_t *job;
4299
4300	job = (job_request_t *)kmem_cache_alloc(fctl_job_cache, sleep);
4301	if (job != NULL) {
4302		job->job_result = FC_SUCCESS;
4303		job->job_code = job_code;
4304		job->job_flags = job_flags;
4305		job->job_cb_arg = arg;
4306		job->job_comp = comp;
4307		job->job_private = NULL;
4308		job->job_ulp_pkts = NULL;
4309		job->job_ulp_listlen = 0;
4310#ifndef __lock_lint
4311		job->job_counter = 0;
4312		job->job_next = NULL;
4313#endif /* __lock_lint */
4314	}
4315
4316	return (job);
4317}
4318
4319
4320void
4321fctl_dealloc_job(job_request_t *job)
4322{
4323	kmem_cache_free(fctl_job_cache, (void *)job);
4324}
4325
4326
4327void
4328fctl_enque_job(fc_local_port_t *port, job_request_t *job)
4329{
4330	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4331
4332	mutex_enter(&port->fp_mutex);
4333
4334	if (port->fp_job_tail == NULL) {
4335		ASSERT(port->fp_job_head == NULL);
4336		port->fp_job_head = port->fp_job_tail = job;
4337	} else {
4338		port->fp_job_tail->job_next = job;
4339		port->fp_job_tail = job;
4340	}
4341	job->job_next = NULL;
4342
4343	cv_signal(&port->fp_cv);
4344	mutex_exit(&port->fp_mutex);
4345}
4346
4347
4348job_request_t *
4349fctl_deque_job(fc_local_port_t *port)
4350{
4351	job_request_t *job;
4352
4353	ASSERT(MUTEX_HELD(&port->fp_mutex));
4354
4355	if (port->fp_job_head == NULL) {
4356		ASSERT(port->fp_job_tail == NULL);
4357		job = NULL;
4358	} else {
4359		job = port->fp_job_head;
4360		if (job->job_next == NULL) {
4361			ASSERT(job == port->fp_job_tail);
4362			port->fp_job_tail = NULL;
4363		}
4364		port->fp_job_head = job->job_next;
4365	}
4366
4367	return (job);
4368}
4369
4370
4371void
4372fctl_priority_enque_job(fc_local_port_t *port, job_request_t *job)
4373{
4374	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4375
4376	mutex_enter(&port->fp_mutex);
4377	if (port->fp_job_tail == NULL) {
4378		ASSERT(port->fp_job_head == NULL);
4379		port->fp_job_head = port->fp_job_tail = job;
4380		job->job_next = NULL;
4381	} else {
4382		job->job_next = port->fp_job_head;
4383		port->fp_job_head = job;
4384	}
4385	cv_signal(&port->fp_cv);
4386	mutex_exit(&port->fp_mutex);
4387}
4388
4389
4390void
4391fctl_jobwait(job_request_t *job)
4392{
4393	ASSERT(!(job->job_flags & JOB_TYPE_FCTL_ASYNC));
4394	sema_p(&job->job_fctl_sema);
4395	ASSERT(!MUTEX_HELD(&job->job_mutex));
4396}
4397
4398
4399void
4400fctl_jobdone(job_request_t *job)
4401{
4402	if (job->job_flags & JOB_TYPE_FCTL_ASYNC) {
4403		if (job->job_comp) {
4404			job->job_comp(job->job_cb_arg, job->job_result);
4405		}
4406		fctl_dealloc_job(job);
4407	} else {
4408		sema_v(&job->job_fctl_sema);
4409	}
4410}
4411
4412
4413/*
4414 * Compare two WWNs.
4415 * The NAA can't be omitted for comparison.
4416 *
4417 * Return Values:
4418 *   if src == dst return  0
4419 *   if src > dst  return  1
4420 *   if src < dst  return -1
4421 */
4422int
4423fctl_wwn_cmp(la_wwn_t *src, la_wwn_t *dst)
4424{
4425	uint8_t *l, *r;
4426	int i;
4427	uint64_t wl, wr;
4428
4429	l = (uint8_t *)src;
4430	r = (uint8_t *)dst;
4431
4432	for (i = 0, wl = 0; i < 8; i++) {
4433		wl <<= 8;
4434		wl |= l[i];
4435	}
4436	for (i = 0, wr = 0; i < 8; i++) {
4437		wr <<= 8;
4438		wr |= r[i];
4439	}
4440
4441	if (wl > wr) {
4442		return (1);
4443	} else if (wl == wr) {
4444		return (0);
4445	} else {
4446		return (-1);
4447	}
4448}
4449
4450
4451/*
4452 * ASCII to Integer goodie with support for base 16, 10, 2 and 8
4453 */
4454int
4455fctl_atoi(char *s, int base)
4456{
4457	int val;
4458	int ch;
4459
4460	for (val = 0; *s != '\0'; s++) {
4461		switch (base) {
4462		case 16:
4463			if (*s >= '0' && *s <= '9') {
4464				ch = *s - '0';
4465			} else if (*s >= 'a' && *s <= 'f') {
4466				ch = *s - 'a' + 10;
4467			} else if (*s >= 'A' && *s <= 'F') {
4468				ch = *s - 'A' + 10;
4469			} else {
4470				return (-1);
4471			}
4472			break;
4473
4474		case 10:
4475			if (*s < '0' || *s > '9') {
4476				return (-1);
4477			}
4478			ch = *s - '0';
4479			break;
4480
4481		case 2:
4482			if (*s < '0' || *s > '1') {
4483				return (-1);
4484			}
4485			ch = *s - '0';
4486			break;
4487
4488		case 8:
4489			if (*s < '0' || *s > '7') {
4490				return (-1);
4491			}
4492			ch = *s - '0';
4493			break;
4494
4495		default:
4496			return (-1);
4497		}
4498		val = (val * base) + ch;
4499	}
4500	return (val);
4501}
4502
4503
4504/*
4505 * Create the fc_remote_port_t struct for the given port_wwn and d_id.
4506 *
4507 * If the struct already exists (and is "valid"), then use it. Before using
4508 * it, the code below also checks: (a) if the d_id has changed, and (b) if
4509 * the device is maked as PORT_DEVICE_OLD.
4510 *
4511 * If no fc_remote_node_t struct exists for the given node_wwn, then that
4512 * struct is also created (and linked with the fc_remote_port_t).
4513 *
4514 * The given fc_local_port_t struct is updated with the info on the new
4515 * struct(s). The d_id and pwwn hash tables in the port_wwn are updated.
4516 * The global node_hash_table[] is updated (if necessary).
4517 */
4518fc_remote_port_t *
4519fctl_create_remote_port(fc_local_port_t *port, la_wwn_t *node_wwn,
4520    la_wwn_t *port_wwn, uint32_t d_id, uchar_t recepient, int sleep)
4521{
4522	int			invalid = 0;
4523	fc_remote_node_t	*rnodep;
4524	fc_remote_port_t	*pd;
4525
4526	rnodep = fctl_get_remote_node_by_nwwn(node_wwn);
4527	if (rnodep) {
4528		/*
4529		 * We found an fc_remote_node_t for the remote node -- see if
4530		 * anyone has marked it as going away or gone.
4531		 */
4532		mutex_enter(&rnodep->fd_mutex);
4533		invalid = (rnodep->fd_flags == FC_REMOTE_NODE_INVALID) ? 1 : 0;
4534		mutex_exit(&rnodep->fd_mutex);
4535	}
4536	if (rnodep == NULL || invalid) {
4537		/*
4538		 * No valid remote node struct found -- create it.
4539		 * Note: this is the only place that this func is called.
4540		 */
4541		rnodep = fctl_create_remote_node(node_wwn, sleep);
4542		if (rnodep == NULL) {
4543			return (NULL);
4544		}
4545	}
4546
4547	mutex_enter(&port->fp_mutex);
4548
4549	/*
4550	 * See if there already is an fc_remote_port_t struct in existence
4551	 * on the specified fc_local_port_t for the given pwwn.	 If so, then
4552	 * grab a reference to it. The 'held' here just means that fp_mutex
4553	 * is held by the caller -- no reference counts are updated.
4554	 */
4555	pd = fctl_get_remote_port_by_pwwn_mutex_held(port, port_wwn);
4556	if (pd) {
4557		/*
4558		 * An fc_remote_port_t struct was found -- see if anyone has
4559		 * marked it as "invalid", which means that it is in the
4560		 * process of going away & we don't want to use it.
4561		 */
4562		mutex_enter(&pd->pd_mutex);
4563		invalid = (pd->pd_state == PORT_DEVICE_INVALID) ? 1 : 0;
4564		mutex_exit(&pd->pd_mutex);
4565	}
4566
4567	if (pd == NULL || invalid) {
4568		/*
4569		 * No fc_remote_port_t was found (or the existing one is
4570		 * marked as "invalid".) Allocate a new one and use that.
4571		 * This call will also update the d_id and pwwn hash tables
4572		 * in the given fc_local_port_t struct with the newly allocated
4573		 * fc_remote_port_t.
4574		 */
4575		if ((pd = fctl_alloc_remote_port(port, port_wwn, d_id,
4576		    recepient, sleep)) == NULL) {
4577			/* Just give up if the allocation fails. */
4578			mutex_exit(&port->fp_mutex);
4579			fctl_destroy_remote_node(rnodep);
4580			return (pd);
4581		}
4582
4583		/*
4584		 * Add the new fc_remote_port_t struct to the d_id and pwwn
4585		 * hash tables on the associated fc_local_port_t struct.
4586		 */
4587		mutex_enter(&pd->pd_mutex);
4588		pd->pd_remote_nodep = rnodep;
4589		fctl_enlist_did_table(port, pd);
4590		fctl_enlist_pwwn_table(port, pd);
4591		mutex_exit(&pd->pd_mutex);
4592		mutex_exit(&port->fp_mutex);
4593
4594		/*
4595		 * Retrieve a pointer to the fc_remote_node_t (i.e., remote
4596		 * node) specified by the given node_wwn.  This looks in the
4597		 * global fctl_nwwn_hash_table[]. The fd_numports reference
4598		 * count in the fc_remote_node_t struct is incremented.
4599		 */
4600		rnodep = fctl_lock_remote_node_by_nwwn(node_wwn);
4601
4602	} else {
4603		/*
4604		 * An existing and valid fc_remote_port_t struct already
4605		 * exists on the fc_local_port_t for the given pwwn.
4606		 */
4607
4608		mutex_enter(&pd->pd_mutex);
4609		ASSERT(pd->pd_remote_nodep != NULL);
4610
4611		if (pd->pd_port_id.port_id != d_id) {
4612			/*
4613			 * A very unlikely occurance in a well
4614			 * behaved environment.
4615			 */
4616
4617			/*
4618			 * The existing fc_remote_port_t has a different
4619			 * d_id than what we were given. This code will
4620			 * update the existing one with the one that was
4621			 * just given.
4622			 */
4623			char string[(FCTL_WWN_SIZE(port_wwn) << 1) + 1];
4624			uint32_t old_id;
4625
4626			fc_wwn_to_str(port_wwn, string);
4627
4628			old_id = pd->pd_port_id.port_id;
4629
4630			fctl_delist_did_table(port, pd);
4631
4632			cmn_err(CE_NOTE, "!fctl(%d): D_ID of a device"
4633			    " with PWWN %s changed. New D_ID = %x,"
4634			    " OLD D_ID = %x", port->fp_instance, string,
4635			    d_id, old_id);
4636
4637			pd->pd_port_id.port_id = d_id;
4638
4639			/*
4640			 * Looks like we have to presume here that the
4641			 * remote port could be something entirely different
4642			 * from what was previously existing & valid at this
4643			 * pwwn.
4644			 */
4645			pd->pd_type = PORT_DEVICE_CHANGED;
4646
4647			/* Record (update) the new d_id for the remote port */
4648			fctl_enlist_did_table(port, pd);
4649
4650		} else if (pd->pd_type == PORT_DEVICE_OLD) {
4651			/*
4652			 * OK at least the old & new d_id's match. So for
4653			 * PORT_DEVICE_OLD, this assumes that the remote
4654			 * port had disappeared but now has come back.
4655			 * Update the pd_type and pd_state to put the
4656			 * remote port back into service.
4657			 */
4658			pd->pd_type = PORT_DEVICE_NOCHANGE;
4659			pd->pd_state = PORT_DEVICE_VALID;
4660
4661			fctl_enlist_did_table(port, pd);
4662
4663		} else {
4664			/*
4665			 * OK the old & new d_id's match, and the remote
4666			 * port struct is not marked as PORT_DEVICE_OLD, so
4667			 * presume that it's still the same device and is
4668			 * still in good shape.	 Also this presumes that we
4669			 * do not need to update d_id or pwwn hash tables.
4670			 */
4671			/* sanitize device values */
4672			pd->pd_type = PORT_DEVICE_NOCHANGE;
4673			pd->pd_state = PORT_DEVICE_VALID;
4674		}
4675
4676		mutex_exit(&pd->pd_mutex);
4677		mutex_exit(&port->fp_mutex);
4678
4679		if (rnodep != pd->pd_remote_nodep) {
4680			if ((rnodep != NULL) &&
4681			    (fctl_wwn_cmp(&pd->pd_remote_nodep->fd_node_name,
4682			    node_wwn) != 0)) {
4683				/*
4684				 * Rut-roh, there is an fc_remote_node_t remote
4685				 * node struct for the given node_wwn, but the
4686				 * fc_remote_port_t remote port struct doesn't
4687				 * know about it.  This just prints a warning
4688				 * message & fails the fc_remote_port_t
4689				 * allocation (possible leak here?).
4690				 */
4691				char	ww1_name[17];
4692				char	ww2_name[17];
4693
4694				fc_wwn_to_str(
4695				    &pd->pd_remote_nodep->fd_node_name,
4696				    ww1_name);
4697				fc_wwn_to_str(node_wwn, ww2_name);
4698
4699				cmn_err(CE_WARN, "fctl(%d) NWWN Mismatch: "
4700				    "Expected %s Got %s", port->fp_instance,
4701				    ww1_name, ww2_name);
4702			}
4703
4704			return (NULL);
4705		}
4706	}
4707
4708	/*
4709	 * Add	the fc_remote_port_t onto the linked list of remote port
4710	 * devices associated with the given fc_remote_node_t (remote node).
4711	 */
4712	fctl_link_remote_port_to_remote_node(rnodep, pd);
4713
4714	return (pd);
4715}
4716
4717
4718/*
4719 * Disassociate the given fc_local_port_t and fc_remote_port_t structs. Removes
4720 * the fc_remote_port_t from the associated fc_remote_node_t. Also removes any
4721 * references to the fc_remote_port_t from the d_id and pwwn tables in the
4722 * given fc_local_port_t.  Deallocates the given fc_remote_port_t.
4723 *
4724 * Returns a count of the number of remaining fc_remote_port_t structs
4725 * associated with the fc_remote_node_t struct.
4726 *
4727 * If pd_ref_count in the given fc_remote_port_t is nonzero, then this
4728 * function just sets the pd->pd_aux_flags |= PD_NEEDS_REMOVAL and the
4729 * pd->pd_type = PORT_DEVICE_OLD and lets some other function(s) worry about
4730 * the cleanup.	 The function then also returns '1'
4731 * instead of the actual number of remaining fc_remote_port_t structs
4732 *
4733 * If there are no more remote ports on the remote node, return 0.
4734 * Otherwise, return non-zero.
4735 */
4736int
4737fctl_destroy_remote_port(fc_local_port_t *port, fc_remote_port_t *pd)
4738{
4739	fc_remote_node_t	*rnodep;
4740	int			rcount = 0;
4741
4742	mutex_enter(&pd->pd_mutex);
4743
4744	/*
4745	 * If pd_ref_count > 0, we can't pull the rug out from any
4746	 * current users of this fc_remote_port_t.  We'll mark it as old
4747	 * and in need of removal.  The same goes for any fc_remote_port_t
4748	 * that has a reference handle(s) in a ULP(s) but for which the ULP(s)
4749	 * have not yet been notified that the handle is no longer valid
4750	 * (i.e., PD_GIVEN_TO_ULPS is set).
4751	 */
4752	if ((pd->pd_ref_count > 0) ||
4753	    (pd->pd_aux_flags & PD_GIVEN_TO_ULPS)) {
4754		pd->pd_aux_flags |= PD_NEEDS_REMOVAL;
4755		pd->pd_type = PORT_DEVICE_OLD;
4756		mutex_exit(&pd->pd_mutex);
4757		return (1);
4758	}
4759
4760	pd->pd_type = PORT_DEVICE_OLD;
4761
4762	rnodep = pd->pd_remote_nodep;
4763
4764	mutex_exit(&pd->pd_mutex);
4765
4766	if (rnodep != NULL) {
4767		/*
4768		 * Remove the fc_remote_port_t from the linked list of remote
4769		 * ports for the given fc_remote_node_t. This is only called
4770		 * here and in fctl_destroy_all_remote_ports().
4771		 */
4772		rcount = fctl_unlink_remote_port_from_remote_node(rnodep, pd);
4773	}
4774
4775	mutex_enter(&port->fp_mutex);
4776	mutex_enter(&pd->pd_mutex);
4777
4778	fctl_delist_did_table(port, pd);
4779	fctl_delist_pwwn_table(port, pd);
4780
4781	mutex_exit(&pd->pd_mutex);
4782
4783	/*
4784	 * Deconstruct & free the fc_remote_port_t. This is only called
4785	 * here and in fctl_destroy_all_remote_ports().
4786	 */
4787	fctl_dealloc_remote_port(pd);
4788
4789	mutex_exit(&port->fp_mutex);
4790
4791	return (rcount);
4792}
4793
4794
4795/*
4796 * This goes thru the d_id table on the given fc_local_port_t.
4797 * For each fc_remote_port_t found, this will:
4798 *
4799 *  - Remove the fc_remote_port_t from the linked list of remote ports for
4800 *    the associated fc_remote_node_t.	If the linked list goes empty, then this
4801 *    tries to deconstruct & free the fc_remote_node_t (that also removes the
4802 *    fc_remote_node_t from the global fctl_nwwn_hash_table[]).
4803 *
4804 *  - Remove the fc_remote_port_t from the pwwn list on the given
4805 *    fc_local_port_t.
4806 *
4807 *  - Deconstruct and free the fc_remote_port_t.
4808 *
4809 *  - Removes the link to the fc_remote_port_t in the d_id table. Note, this
4810 *    does not appear to correctle decrement the d_id_count tho.
4811 */
4812void
4813fctl_destroy_all_remote_ports(fc_local_port_t *port)
4814{
4815	int			index;
4816	fc_remote_port_t	*pd;
4817	fc_remote_node_t	*rnodep;
4818	struct d_id_hash	*head;
4819
4820	mutex_enter(&port->fp_mutex);
4821
4822	for (index = 0; index < did_table_size; index++) {
4823
4824		head = &port->fp_did_table[index];
4825
4826		while (head->d_id_head != NULL) {
4827			pd = head->d_id_head;
4828
4829			/*
4830			 * See if this remote port (fc_remote_port_t) has a
4831			 * reference to a remote node (fc_remote_node_t) in its
4832			 * pd->pd_remote_nodep pointer.
4833			 */
4834			mutex_enter(&pd->pd_mutex);
4835			rnodep = pd->pd_remote_nodep;
4836			mutex_exit(&pd->pd_mutex);
4837
4838			if (rnodep != NULL) {
4839				/*
4840				 * An fc_remote_node_t reference exists. Remove
4841				 * the fc_remote_port_t from the linked list of
4842				 * remote ports for fc_remote_node_t.
4843				 */
4844				if (fctl_unlink_remote_port_from_remote_node(
4845				    rnodep, pd) == 0) {
4846					/*
4847					 * The fd_numports reference count
4848					 * in the fc_remote_node_t has come
4849					 * back as zero, so we can free the
4850					 * fc_remote_node_t. This also means
4851					 * that the fc_remote_node_t was
4852					 * removed from the
4853					 * fctl_nwwn_hash_table[].
4854					 *
4855					 * This will silently skip the
4856					 * kmem_free() if either the
4857					 * fd_numports is nonzero or
4858					 * the fd_port is not NULL in
4859					 * the fc_remote_node_t.
4860					 */
4861					fctl_destroy_remote_node(rnodep);
4862				}
4863			}
4864
4865			/*
4866			 * Clean up the entry in the fc_local_port_t's pwwn
4867			 * table for the given fc_remote_port_t (i.e., the pd).
4868			 */
4869			mutex_enter(&pd->pd_mutex);
4870			fctl_delist_pwwn_table(port, pd);
4871			pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
4872			mutex_exit(&pd->pd_mutex);
4873
4874			/*
4875			 * Remove the current entry from the d_id list.
4876			 */
4877			head->d_id_head = pd->pd_did_hnext;
4878
4879			/*
4880			 * Deconstruct & free the fc_remote_port_t (pd)
4881			 * Note: this is only called here and in
4882			 * fctl_destroy_remote_port_t().
4883			 */
4884			fctl_dealloc_remote_port(pd);
4885		}
4886	}
4887
4888	mutex_exit(&port->fp_mutex);
4889}
4890
4891
4892int
4893fctl_is_wwn_zero(la_wwn_t *wwn)
4894{
4895	int count;
4896
4897	for (count = 0; count < sizeof (la_wwn_t); count++) {
4898		if (wwn->raw_wwn[count] != 0) {
4899			return (FC_FAILURE);
4900		}
4901	}
4902
4903	return (FC_SUCCESS);
4904}
4905
4906
4907void
4908fctl_ulp_unsol_cb(fc_local_port_t *port, fc_unsol_buf_t *buf, uchar_t type)
4909{
4910	int			data_cb;
4911	int			check_type;
4912	int			rval;
4913	uint32_t		claimed;
4914	fc_ulp_module_t		*mod;
4915	fc_ulp_ports_t		*ulp_port;
4916
4917	claimed = 0;
4918	check_type = 1;
4919
4920	switch ((buf->ub_frame.r_ctl) & R_CTL_ROUTING) {
4921	case R_CTL_DEVICE_DATA:
4922		data_cb = 1;
4923		break;
4924
4925	case R_CTL_EXTENDED_SVC:
4926		check_type = 0;
4927		/* FALLTHROUGH */
4928
4929	case R_CTL_FC4_SVC:
4930		data_cb = 0;
4931		break;
4932
4933	default:
4934		mutex_enter(&port->fp_mutex);
4935		ASSERT(port->fp_active_ubs > 0);
4936		if (--(port->fp_active_ubs) == 0) {
4937			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4938		}
4939		mutex_exit(&port->fp_mutex);
4940		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
4941		    1, &buf->ub_token);
4942		return;
4943	}
4944
4945	rw_enter(&fctl_ulp_lock, RW_READER);
4946	for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
4947		if (check_type && mod->mod_info->ulp_type != type) {
4948			continue;
4949		}
4950
4951		rw_enter(&fctl_mod_ports_lock, RW_READER);
4952		ulp_port = fctl_get_ulp_port(mod, port);
4953		rw_exit(&fctl_mod_ports_lock);
4954
4955		if (ulp_port == NULL) {
4956			continue;
4957		}
4958
4959		mutex_enter(&ulp_port->port_mutex);
4960		if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
4961			mutex_exit(&ulp_port->port_mutex);
4962			continue;
4963		}
4964		mutex_exit(&ulp_port->port_mutex);
4965
4966		if (data_cb == 1) {
4967			rval = mod->mod_info->ulp_data_callback(
4968			    mod->mod_info->ulp_handle,
4969			    (opaque_t)port, buf, claimed);
4970		} else {
4971			rval = mod->mod_info->ulp_els_callback(
4972			    mod->mod_info->ulp_handle,
4973			    (opaque_t)port, buf, claimed);
4974		}
4975
4976		if (rval == FC_SUCCESS && claimed == 0) {
4977			claimed = 1;
4978		}
4979	}
4980	rw_exit(&fctl_ulp_lock);
4981
4982	if (claimed == 0) {
4983		/*
4984		 * We should actually RJT since nobody claimed it.
4985		 */
4986		mutex_enter(&port->fp_mutex);
4987		ASSERT(port->fp_active_ubs > 0);
4988		if (--(port->fp_active_ubs) == 0) {
4989			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4990		}
4991		mutex_exit(&port->fp_mutex);
4992		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
4993		    1, &buf->ub_token);
4994
4995	} else {
4996		mutex_enter(&port->fp_mutex);
4997		if (--port->fp_active_ubs == 0) {
4998			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
4999		}
5000		mutex_exit(&port->fp_mutex);
5001	}
5002}
5003
5004
5005/*
5006 * Both fd_mutex and pd_mutex are held (in that order) coming in to this func
5007 *
5008 * With all these mutexes held, we should make sure this function does not eat
5009 * up much time.
5010 */
5011void
5012fctl_copy_portmap_held(fc_portmap_t *map, fc_remote_port_t *pd)
5013{
5014	fc_remote_node_t *node;
5015
5016	ASSERT(MUTEX_HELD(&pd->pd_mutex));
5017
5018	map->map_pwwn = pd->pd_port_name;
5019	map->map_did = pd->pd_port_id;
5020	map->map_hard_addr = pd->pd_hard_addr;
5021	map->map_state = pd->pd_state;
5022	map->map_type = pd->pd_type;
5023	map->map_flags = 0;
5024
5025	ASSERT(map->map_type <= PORT_DEVICE_DELETE);
5026
5027	bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
5028
5029	node = pd->pd_remote_nodep;
5030
5031	ASSERT(MUTEX_HELD(&node->fd_mutex));
5032
5033	if (node) {
5034		map->map_nwwn = node->fd_node_name;
5035	}
5036	map->map_pd = pd;
5037}
5038
5039void
5040fctl_copy_portmap(fc_portmap_t *map, fc_remote_port_t *pd)
5041{
5042	fc_remote_node_t *node;
5043
5044	ASSERT(!MUTEX_HELD(&pd->pd_mutex));
5045
5046	mutex_enter(&pd->pd_mutex);
5047	map->map_pwwn = pd->pd_port_name;
5048	map->map_did = pd->pd_port_id;
5049	map->map_hard_addr = pd->pd_hard_addr;
5050	map->map_state = pd->pd_state;
5051	map->map_type = pd->pd_type;
5052	map->map_flags = 0;
5053
5054	ASSERT(map->map_type <= PORT_DEVICE_DELETE);
5055
5056	bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
5057
5058	node = pd->pd_remote_nodep;
5059	mutex_exit(&pd->pd_mutex);
5060
5061	if (node) {
5062		mutex_enter(&node->fd_mutex);
5063		map->map_nwwn = node->fd_node_name;
5064		mutex_exit(&node->fd_mutex);
5065	}
5066	map->map_pd = pd;
5067}
5068
5069
5070static int
5071fctl_update_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
5072{
5073	int	rval = FC_SUCCESS;
5074
5075	switch (ns_req->ns_cmd) {
5076	case NS_RFT_ID: {
5077		int		count;
5078		uint32_t	*src;
5079		uint32_t	*dst;
5080		ns_rfc_type_t	*rfc;
5081
5082		rfc = (ns_rfc_type_t *)ns_req->ns_req_payload;
5083
5084		mutex_enter(&port->fp_mutex);
5085		src = (uint32_t *)port->fp_fc4_types;
5086		dst = (uint32_t *)rfc->rfc_types;
5087
5088		for (count = 0; count < 8; count++) {
5089			*src++ |= *dst++;
5090		}
5091		mutex_exit(&port->fp_mutex);
5092
5093		break;
5094	}
5095
5096	case NS_RSPN_ID: {
5097		ns_spn_t *spn;
5098
5099		spn = (ns_spn_t *)ns_req->ns_req_payload;
5100
5101		mutex_enter(&port->fp_mutex);
5102		port->fp_sym_port_namelen = spn->spn_len;
5103		if (spn->spn_len) {
5104			bcopy((caddr_t)spn + sizeof (ns_spn_t),
5105			    port->fp_sym_port_name, spn->spn_len);
5106		}
5107		mutex_exit(&port->fp_mutex);
5108
5109		break;
5110	}
5111
5112	case NS_RSNN_NN: {
5113		ns_snn_t *snn;
5114
5115		snn = (ns_snn_t *)ns_req->ns_req_payload;
5116
5117		mutex_enter(&port->fp_mutex);
5118		port->fp_sym_node_namelen = snn->snn_len;
5119		if (snn->snn_len) {
5120			bcopy((caddr_t)snn + sizeof (ns_snn_t),
5121			    port->fp_sym_node_name, snn->snn_len);
5122		}
5123		mutex_exit(&port->fp_mutex);
5124
5125		break;
5126	}
5127
5128	case NS_RIP_NN: {
5129		ns_rip_t *rip;
5130
5131		rip = (ns_rip_t *)ns_req->ns_req_payload;
5132
5133		mutex_enter(&port->fp_mutex);
5134		bcopy(rip->rip_ip_addr, port->fp_ip_addr,
5135		    sizeof (rip->rip_ip_addr));
5136		mutex_exit(&port->fp_mutex);
5137
5138		break;
5139	}
5140
5141	case NS_RIPA_NN: {
5142		ns_ipa_t *ipa;
5143
5144		ipa = (ns_ipa_t *)ns_req->ns_req_payload;
5145
5146		mutex_enter(&port->fp_mutex);
5147		bcopy(ipa->ipa_value, port->fp_ipa, sizeof (ipa->ipa_value));
5148		mutex_exit(&port->fp_mutex);
5149
5150		break;
5151	}
5152
5153	default:
5154		rval = FC_BADOBJECT;
5155		break;
5156	}
5157
5158	return (rval);
5159}
5160
5161
5162static int
5163fctl_retrieve_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
5164{
5165	int	rval = FC_SUCCESS;
5166
5167	switch (ns_req->ns_cmd) {
5168	case NS_GFT_ID: {
5169		ns_rfc_type_t *rfc;
5170
5171		rfc = (ns_rfc_type_t *)ns_req->ns_resp_payload;
5172
5173		mutex_enter(&port->fp_mutex);
5174		bcopy(port->fp_fc4_types, rfc->rfc_types,
5175		    sizeof (rfc->rfc_types));
5176		mutex_exit(&port->fp_mutex);
5177		break;
5178	}
5179
5180	case NS_GSPN_ID: {
5181		ns_spn_t *spn;
5182
5183		spn = (ns_spn_t *)ns_req->ns_resp_payload;
5184
5185		mutex_enter(&port->fp_mutex);
5186		spn->spn_len = port->fp_sym_port_namelen;
5187		if (spn->spn_len) {
5188			bcopy(port->fp_sym_port_name, (caddr_t)spn +
5189			    sizeof (ns_spn_t), spn->spn_len);
5190		}
5191		mutex_exit(&port->fp_mutex);
5192
5193		break;
5194	}
5195
5196	case NS_GSNN_NN: {
5197		ns_snn_t *snn;
5198
5199		snn = (ns_snn_t *)ns_req->ns_resp_payload;
5200
5201		mutex_enter(&port->fp_mutex);
5202		snn->snn_len = port->fp_sym_node_namelen;
5203		if (snn->snn_len) {
5204			bcopy(port->fp_sym_node_name, (caddr_t)snn +
5205			    sizeof (ns_snn_t), snn->snn_len);
5206		}
5207		mutex_exit(&port->fp_mutex);
5208
5209		break;
5210	}
5211
5212	case NS_GIP_NN: {
5213		ns_rip_t *rip;
5214
5215		rip = (ns_rip_t *)ns_req->ns_resp_payload;
5216
5217		mutex_enter(&port->fp_mutex);
5218		bcopy(port->fp_ip_addr, rip->rip_ip_addr,
5219		    sizeof (rip->rip_ip_addr));
5220		mutex_exit(&port->fp_mutex);
5221
5222		break;
5223	}
5224
5225	case NS_GIPA_NN: {
5226		ns_ipa_t *ipa;
5227
5228		ipa = (ns_ipa_t *)ns_req->ns_resp_payload;
5229
5230		mutex_enter(&port->fp_mutex);
5231		bcopy(port->fp_ipa, ipa->ipa_value, sizeof (ipa->ipa_value));
5232		mutex_exit(&port->fp_mutex);
5233
5234		break;
5235	}
5236
5237	default:
5238		rval = FC_BADOBJECT;
5239		break;
5240	}
5241
5242	return (rval);
5243}
5244
5245
5246fctl_ns_req_t *
5247fctl_alloc_ns_cmd(uint32_t cmd_len, uint32_t resp_len, uint32_t data_len,
5248    uint32_t ns_flags, int sleep)
5249{
5250	fctl_ns_req_t *ns_cmd;
5251
5252	ns_cmd = kmem_zalloc(sizeof (*ns_cmd), sleep);
5253	if (ns_cmd == NULL) {
5254		return (NULL);
5255	}
5256
5257	if (cmd_len) {
5258		ns_cmd->ns_cmd_buf = kmem_zalloc(cmd_len, sleep);
5259		if (ns_cmd->ns_cmd_buf == NULL) {
5260			kmem_free(ns_cmd, sizeof (*ns_cmd));
5261			return (NULL);
5262		}
5263		ns_cmd->ns_cmd_size = cmd_len;
5264	}
5265
5266	ns_cmd->ns_resp_size = resp_len;
5267
5268	if (data_len) {
5269		ns_cmd->ns_data_buf = kmem_zalloc(data_len, sleep);
5270		if (ns_cmd->ns_data_buf == NULL) {
5271			if (ns_cmd->ns_cmd_buf && cmd_len) {
5272				kmem_free(ns_cmd->ns_cmd_buf, cmd_len);
5273			}
5274			kmem_free(ns_cmd, sizeof (*ns_cmd));
5275			return (NULL);
5276		}
5277		ns_cmd->ns_data_len = data_len;
5278	}
5279	ns_cmd->ns_flags = ns_flags;
5280
5281	return (ns_cmd);
5282}
5283
5284
5285void
5286fctl_free_ns_cmd(fctl_ns_req_t *ns_cmd)
5287{
5288	if (ns_cmd->ns_cmd_size && ns_cmd->ns_cmd_buf) {
5289		kmem_free(ns_cmd->ns_cmd_buf, ns_cmd->ns_cmd_size);
5290	}
5291	if (ns_cmd->ns_data_len && ns_cmd->ns_data_buf) {
5292		kmem_free(ns_cmd->ns_data_buf, ns_cmd->ns_data_len);
5293	}
5294	kmem_free(ns_cmd, sizeof (*ns_cmd));
5295}
5296
5297
5298int
5299fctl_ulp_port_ioctl(fc_local_port_t *port, dev_t dev, int cmd,
5300    intptr_t data, int mode, cred_t *credp, int *rval)
5301{
5302	int			ret;
5303	int			save;
5304	uint32_t		claimed;
5305	fc_ulp_module_t		*mod;
5306	fc_ulp_ports_t		*ulp_port;
5307
5308	save = *rval;
5309	*rval = ENOTTY;
5310
5311	rw_enter(&fctl_ulp_lock, RW_READER);
5312	for (claimed = 0, mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
5313		rw_enter(&fctl_mod_ports_lock, RW_READER);
5314		ulp_port = fctl_get_ulp_port(mod, port);
5315		rw_exit(&fctl_mod_ports_lock);
5316
5317		if (ulp_port == NULL) {
5318			continue;
5319		}
5320
5321		mutex_enter(&ulp_port->port_mutex);
5322		if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate) ||
5323		    mod->mod_info->ulp_port_ioctl == NULL) {
5324			mutex_exit(&ulp_port->port_mutex);
5325			continue;
5326		}
5327		mutex_exit(&ulp_port->port_mutex);
5328
5329		ret = mod->mod_info->ulp_port_ioctl(
5330		    mod->mod_info->ulp_handle, (opaque_t)port,
5331		    dev, cmd, data, mode, credp, rval, claimed);
5332
5333		if (ret == FC_SUCCESS && claimed == 0) {
5334			claimed = 1;
5335		}
5336	}
5337	rw_exit(&fctl_ulp_lock);
5338
5339	ret = *rval;
5340	*rval = save;
5341
5342	return (ret);
5343}
5344
5345/*
5346 * raise power if necessary, and set the port busy
5347 *
5348 * this may cause power to be raised, so no power related locks should
5349 * be held
5350 */
5351int
5352fc_ulp_busy_port(opaque_t port_handle)
5353{
5354	fc_local_port_t *port = port_handle;
5355
5356	return (fctl_busy_port(port));
5357}
5358
5359void
5360fc_ulp_idle_port(opaque_t port_handle)
5361{
5362	fc_local_port_t *port = port_handle;
5363	fctl_idle_port(port);
5364}
5365
5366void
5367fc_ulp_copy_portmap(fc_portmap_t *map, opaque_t pd)
5368{
5369	fctl_copy_portmap(map, (fc_remote_port_t *)pd);
5370}
5371
5372
5373int
5374fc_ulp_get_npiv_port_num(opaque_t port_handle)
5375{
5376	int portsnum = 0;
5377	fc_local_port_t *port = port_handle;
5378	fc_local_port_t *tmpport;
5379
5380	mutex_enter(&port->fp_mutex);
5381	tmpport = port->fp_port_next;
5382	if (!tmpport) {
5383		mutex_exit(&port->fp_mutex);
5384		return (portsnum);
5385	}
5386	while (tmpport != port) {
5387		portsnum ++;
5388		tmpport = tmpport->fp_port_next;
5389	}
5390	mutex_exit(&port->fp_mutex);
5391	return (portsnum);
5392}
5393
5394fc_local_port_t *
5395fc_get_npiv_port(fc_local_port_t *phyport, la_wwn_t *pwwn)
5396{
5397	fc_fca_port_t	*fca_port;
5398	fc_local_port_t	*tmpPort = phyport;
5399
5400	mutex_enter(&fctl_port_lock);
5401
5402	for (fca_port = fctl_fca_portlist; fca_port != NULL;
5403	    fca_port = fca_port->port_next) {
5404		tmpPort = fca_port->port_handle;
5405		if (tmpPort == NULL) {
5406			continue;
5407		}
5408		mutex_enter(&tmpPort->fp_mutex);
5409		if (bcmp(tmpPort->fp_service_params.nport_ww_name.raw_wwn,
5410		    pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) {
5411			mutex_exit(&tmpPort->fp_mutex);
5412			mutex_exit(&fctl_port_lock);
5413			return (tmpPort);
5414		}
5415		mutex_exit(&tmpPort->fp_mutex);
5416	}
5417
5418	mutex_exit(&fctl_port_lock);
5419
5420	return (NULL);
5421}
5422
5423int
5424fc_ulp_get_npiv_port_list(opaque_t port_handle, char *pathList)
5425{
5426	int portsnum = 0;
5427	fc_local_port_t *port = port_handle;
5428	fc_local_port_t *tmpport;
5429
5430	mutex_enter(&port->fp_mutex);
5431	tmpport = port->fp_port_next;
5432	if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
5433		mutex_exit(&port->fp_mutex);
5434		return (portsnum);
5435	}
5436
5437	while (tmpport != port) {
5438		(void) ddi_pathname(tmpport->fp_port_dip,
5439		    &pathList[MAXPATHLEN * portsnum]);
5440		portsnum ++;
5441		tmpport = tmpport->fp_port_next;
5442	}
5443	mutex_exit(&port->fp_mutex);
5444
5445	return (portsnum);
5446}
5447
5448
5449fc_local_port_t *
5450fc_delete_npiv_port(fc_local_port_t *port, la_wwn_t *pwwn)
5451{
5452	fc_local_port_t *tmpport;
5453
5454	mutex_enter(&port->fp_mutex);
5455	tmpport = port->fp_port_next;
5456	if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
5457		mutex_exit(&port->fp_mutex);
5458		return (NULL);
5459	}
5460
5461	while (tmpport != port) {
5462		if ((bcmp(tmpport->fp_service_params.nport_ww_name.raw_wwn,
5463		    pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) &&
5464		    (tmpport->fp_npiv_state == 0)) {
5465			tmpport->fp_npiv_state = FC_NPIV_DELETING;
5466			mutex_exit(&port->fp_mutex);
5467			return (tmpport);
5468		}
5469		tmpport = tmpport->fp_port_next;
5470	}
5471
5472	mutex_exit(&port->fp_mutex);
5473	return (NULL);
5474}
5475
5476/*
5477 * Get the list of Adapters.  On multi-ported adapters,
5478 * only ONE port on the adapter will be returned.
5479 * pathList should be (count * MAXPATHLEN) long.
5480 * The return value will be set to the number of
5481 * HBAs that were found on the system.	If the value
5482 * is greater than count, the routine should be retried
5483 * with a larger buffer.
5484 */
5485int
5486fc_ulp_get_adapter_paths(char *pathList, int count)
5487{
5488	fc_fca_port_t	*fca_port;
5489	int		in = 0, out = 0, check, skip, maxPorts = 0;
5490	fc_local_port_t		**portList;
5491	fc_local_port_t		*new_port, *stored_port;
5492	fca_hba_fru_details_t	*new_fru, *stored_fru;
5493
5494	ASSERT(pathList != NULL);
5495
5496	/* First figure out how many ports we have */
5497	mutex_enter(&fctl_port_lock);
5498
5499	for (fca_port = fctl_fca_portlist; fca_port != NULL;
5500	    fca_port = fca_port->port_next) {
5501		maxPorts ++;
5502	}
5503
5504	if (maxPorts == 0) {
5505		mutex_exit(&fctl_port_lock);
5506		return (0);
5507	}
5508
5509	/* Now allocate a buffer to store all the pointers for comparisons */
5510	portList = kmem_zalloc(sizeof (fc_local_port_t *) * maxPorts, KM_SLEEP);
5511
5512	for (fca_port = fctl_fca_portlist; fca_port != NULL;
5513	    fca_port = fca_port->port_next) {
5514		skip = 0;
5515
5516		/* Lock the new port for subsequent comparisons */
5517		new_port = fca_port->port_handle;
5518		mutex_enter(&new_port->fp_mutex);
5519		new_fru = &new_port->fp_hba_port_attrs.hba_fru_details;
5520
5521		/* Filter out secondary ports from the list */
5522		for (check = 0; check < out; check++) {
5523			if (portList[check] == NULL) {
5524				continue;
5525			}
5526			/* Guard against duplicates (should never happen) */
5527			if (portList[check] == fca_port->port_handle) {
5528				/* Same port */
5529				skip = 1;
5530				break;
5531			}
5532
5533			/* Lock the already stored port for comparison */
5534			stored_port = portList[check];
5535			mutex_enter(&stored_port->fp_mutex);
5536			stored_fru =
5537			    &stored_port->fp_hba_port_attrs.hba_fru_details;
5538
5539			/* Are these ports on the same HBA? */
5540			if (new_fru->high == stored_fru->high &&
5541			    new_fru->low == stored_fru->low) {
5542				/* Now double check driver */
5543				if (strncmp(
5544				    new_port->fp_hba_port_attrs.driver_name,
5545				    stored_port->fp_hba_port_attrs.driver_name,
5546				    FCHBA_DRIVER_NAME_LEN) == 0) {
5547					/* we don't need to grow the list */
5548					skip = 1;
5549					/* looking at a lower port index? */
5550					if (new_fru->port_index <
5551					    stored_fru->port_index) {
5552						/* Replace the port in list */
5553						mutex_exit(
5554						    &stored_port->fp_mutex);
5555						if (new_port->fp_npiv_type ==
5556						    FC_NPIV_PORT) {
5557							break;
5558						}
5559						portList[check] = new_port;
5560						break;
5561					} /* Else, just skip this port */
5562				}
5563			}
5564
5565			mutex_exit(&stored_port->fp_mutex);
5566		}
5567		mutex_exit(&new_port->fp_mutex);
5568
5569		if (!skip) {
5570			/*
5571			 * Either this is the first port for this HBA, or
5572			 * it's a secondary port and we haven't stored the
5573			 * primary/first port for that HBA.  In the latter case,
5574			 * will just filter it out as we proceed to loop.
5575			 */
5576			if (fca_port->port_handle->fp_npiv_type ==
5577			    FC_NPIV_PORT) {
5578				continue;
5579			} else {
5580				portList[out++] = fca_port->port_handle;
5581			}
5582		}
5583	}
5584
5585	if (out <= count) {
5586		for (in = 0; in < out; in++) {
5587			(void) ddi_pathname(portList[in]->fp_port_dip,
5588			    &pathList[MAXPATHLEN * in]);
5589		}
5590	}
5591	mutex_exit(&fctl_port_lock);
5592	kmem_free(portList, sizeof (*portList) * maxPorts);
5593	return (out);
5594}
5595
5596uint32_t
5597fc_ulp_get_rscn_count(opaque_t port_handle)
5598{
5599	uint32_t	count;
5600	fc_local_port_t	*port;
5601
5602	port = (fc_local_port_t *)port_handle;
5603	mutex_enter(&port->fp_mutex);
5604	count = port->fp_rscn_count;
5605	mutex_exit(&port->fp_mutex);
5606
5607	return (count);
5608}
5609
5610
5611/*
5612 * This function is a very similar to fctl_add_orphan except that it expects
5613 * that the fp_mutex and pd_mutex of the pd passed in are held coming in.
5614 *
5615 * Note that there is a lock hierarchy here (fp_mutex should be held first) but
5616 * since this function could be called with a different pd's pd_mutex held, we
5617 * should take care not to release fp_mutex in this function.
5618 */
5619int
5620fctl_add_orphan_held(fc_local_port_t *port, fc_remote_port_t *pd)
5621{
5622	int		rval = FC_FAILURE;
5623	la_wwn_t	pwwn;
5624	fc_orphan_t	*orp;
5625	fc_orphan_t	*orphan;
5626
5627	ASSERT(MUTEX_HELD(&port->fp_mutex));
5628	ASSERT(MUTEX_HELD(&pd->pd_mutex));
5629
5630	pwwn = pd->pd_port_name;
5631
5632	for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5633		if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5634			return (FC_SUCCESS);
5635		}
5636	}
5637
5638	orphan = kmem_zalloc(sizeof (*orphan), KM_NOSLEEP);
5639	if (orphan) {
5640		orphan->orp_pwwn = pwwn;
5641		orphan->orp_tstamp = ddi_get_lbolt();
5642
5643		if (port->fp_orphan_list) {
5644			ASSERT(port->fp_orphan_count > 0);
5645			orphan->orp_next = port->fp_orphan_list;
5646		}
5647		port->fp_orphan_list = orphan;
5648		port->fp_orphan_count++;
5649
5650		rval = FC_SUCCESS;
5651	}
5652
5653	return (rval);
5654}
5655
5656int
5657fctl_add_orphan(fc_local_port_t *port, fc_remote_port_t *pd, int sleep)
5658{
5659	int		rval = FC_FAILURE;
5660	la_wwn_t	pwwn;
5661	fc_orphan_t	*orp;
5662	fc_orphan_t	*orphan;
5663
5664	mutex_enter(&port->fp_mutex);
5665
5666	mutex_enter(&pd->pd_mutex);
5667	pwwn = pd->pd_port_name;
5668	mutex_exit(&pd->pd_mutex);
5669
5670	for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5671		if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5672			mutex_exit(&port->fp_mutex);
5673			return (FC_SUCCESS);
5674		}
5675	}
5676	mutex_exit(&port->fp_mutex);
5677
5678	orphan = kmem_zalloc(sizeof (*orphan), sleep);
5679	if (orphan != NULL) {
5680		mutex_enter(&port->fp_mutex);
5681
5682		orphan->orp_pwwn = pwwn;
5683		orphan->orp_tstamp = ddi_get_lbolt();
5684
5685		if (port->fp_orphan_list) {
5686			ASSERT(port->fp_orphan_count > 0);
5687			orphan->orp_next = port->fp_orphan_list;
5688		}
5689		port->fp_orphan_list = orphan;
5690		port->fp_orphan_count++;
5691		mutex_exit(&port->fp_mutex);
5692
5693		rval = FC_SUCCESS;
5694	}
5695
5696	return (rval);
5697}
5698
5699
5700int
5701fctl_remove_if_orphan(fc_local_port_t *port, la_wwn_t *pwwn)
5702{
5703	int		rval = FC_FAILURE;
5704	fc_orphan_t	*prev = NULL;
5705	fc_orphan_t	*orp;
5706
5707	mutex_enter(&port->fp_mutex);
5708	for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5709		if (fctl_wwn_cmp(&orp->orp_pwwn, pwwn) == 0) {
5710			if (prev) {
5711				prev->orp_next = orp->orp_next;
5712			} else {
5713				ASSERT(port->fp_orphan_list == orp);
5714				port->fp_orphan_list = orp->orp_next;
5715			}
5716			port->fp_orphan_count--;
5717			rval = FC_SUCCESS;
5718			break;
5719		}
5720		prev = orp;
5721	}
5722	mutex_exit(&port->fp_mutex);
5723
5724	if (rval == FC_SUCCESS) {
5725		kmem_free(orp, sizeof (*orp));
5726	}
5727
5728	return (rval);
5729}
5730
5731
5732static void
5733fctl_print_if_not_orphan(fc_local_port_t *port, fc_remote_port_t *pd)
5734{
5735	char		ww_name[17];
5736	la_wwn_t	pwwn;
5737	fc_orphan_t	*orp;
5738
5739	mutex_enter(&port->fp_mutex);
5740
5741	mutex_enter(&pd->pd_mutex);
5742	pwwn = pd->pd_port_name;
5743	mutex_exit(&pd->pd_mutex);
5744
5745	for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
5746		if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
5747			mutex_exit(&port->fp_mutex);
5748			return;
5749		}
5750	}
5751	mutex_exit(&port->fp_mutex);
5752
5753	fc_wwn_to_str(&pwwn, ww_name);
5754
5755	cmn_err(CE_WARN, "!fctl(%d): N_x Port with D_ID=%x, PWWN=%s"
5756	    " disappeared from fabric", port->fp_instance,
5757	    pd->pd_port_id.port_id, ww_name);
5758}
5759
5760
5761/* ARGSUSED */
5762static void
5763fctl_link_reset_done(opaque_t port_handle, uchar_t result)
5764{
5765	fc_local_port_t *port = port_handle;
5766
5767	mutex_enter(&port->fp_mutex);
5768	port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
5769	mutex_exit(&port->fp_mutex);
5770
5771	fctl_idle_port(port);
5772}
5773
5774
5775static int
5776fctl_error(int fc_errno, char **errmsg)
5777{
5778	int count;
5779
5780	for (count = 0; count < sizeof (fc_errlist) /
5781	    sizeof (fc_errlist[0]); count++) {
5782		if (fc_errlist[count].fc_errno == fc_errno) {
5783			*errmsg = fc_errlist[count].fc_errname;
5784			return (FC_SUCCESS);
5785		}
5786	}
5787	*errmsg = fctl_undefined;
5788
5789	return (FC_FAILURE);
5790}
5791
5792
5793/*
5794 * Return number of successful translations.
5795 *	Anybody with some userland programming experience would have
5796 *	figured it by now that the return value exactly resembles that
5797 *	of scanf(3c). This function returns a count of successful
5798 *	translations. It could range from 0 (no match for state, reason,
5799 *	action, expln) to 4 (successful matches for all state, reason,
5800 *	action, expln) and where translation isn't successful into a
5801 *	friendlier message the relevent field is set to "Undefined"
5802 */
5803static int
5804fctl_pkt_error(fc_packet_t *pkt, char **state, char **reason,
5805    char **action, char **expln)
5806{
5807	int		ret;
5808	int		len;
5809	int		index;
5810	fc_pkt_error_t	*error;
5811	fc_pkt_reason_t	*reason_b;	/* Base pointer */
5812	fc_pkt_action_t	*action_b;	/* Base pointer */
5813	fc_pkt_expln_t	*expln_b;	/* Base pointer */
5814
5815	ret = 0;
5816	*state = *reason = *action = *expln = fctl_undefined;
5817
5818	len = sizeof (fc_pkt_errlist) / sizeof fc_pkt_errlist[0];
5819	for (index = 0; index < len; index++) {
5820		error = fc_pkt_errlist + index;
5821		if (pkt->pkt_state == error->pkt_state) {
5822			*state = error->pkt_msg;
5823			ret++;
5824
5825			reason_b = error->pkt_reason;
5826			action_b = error->pkt_action;
5827			expln_b = error->pkt_expln;
5828
5829			while (reason_b != NULL &&
5830			    reason_b->reason_val != FC_REASON_INVALID) {
5831				if (reason_b->reason_val == pkt->pkt_reason) {
5832					*reason = reason_b->reason_msg;
5833					ret++;
5834					break;
5835				}
5836				reason_b++;
5837			}
5838
5839			while (action_b != NULL &&
5840			    action_b->action_val != FC_ACTION_INVALID) {
5841				if (action_b->action_val == pkt->pkt_action) {
5842					*action = action_b->action_msg;
5843					ret++;
5844					break;
5845				}
5846				action_b++;
5847			}
5848
5849			while (expln_b != NULL &&
5850			    expln_b->expln_val != FC_EXPLN_INVALID) {
5851				if (expln_b->expln_val == pkt->pkt_expln) {
5852					*expln = expln_b->expln_msg;
5853					ret++;
5854					break;
5855				}
5856				expln_b++;
5857			}
5858			break;
5859		}
5860	}
5861
5862	return (ret);
5863}
5864
5865
5866/*
5867 * Remove all port devices that are marked OLD, remove
5868 * corresponding node devices (fc_remote_node_t)
5869 */
5870void
5871fctl_remove_oldies(fc_local_port_t *port)
5872{
5873	int			index;
5874	int			initiator;
5875	fc_remote_node_t	*node;
5876	struct pwwn_hash	*head;
5877	fc_remote_port_t	*pd;
5878	fc_remote_port_t	*old_pd;
5879	fc_remote_port_t	*last_pd;
5880
5881	/*
5882	 * Nuke all OLD devices
5883	 */
5884	mutex_enter(&port->fp_mutex);
5885
5886	for (index = 0; index < pwwn_table_size; index++) {
5887		head = &port->fp_pwwn_table[index];
5888		last_pd = NULL;
5889		pd = head->pwwn_head;
5890
5891		while (pd != NULL) {
5892			mutex_enter(&pd->pd_mutex);
5893			if (pd->pd_type != PORT_DEVICE_OLD) {
5894				mutex_exit(&pd->pd_mutex);
5895				last_pd = pd;
5896				pd = pd->pd_wwn_hnext;
5897				continue;
5898			}
5899
5900			/*
5901			 * Remove this from the PWWN hash table
5902			 */
5903			old_pd = pd;
5904			pd = old_pd->pd_wwn_hnext;
5905
5906			if (last_pd == NULL) {
5907				ASSERT(old_pd == head->pwwn_head);
5908				head->pwwn_head = pd;
5909			} else {
5910				last_pd->pd_wwn_hnext = pd;
5911			}
5912			head->pwwn_count--;
5913			/*
5914			 * Make sure we tie fp_dev_count to the size of the
5915			 * pwwn_table
5916			 */
5917			port->fp_dev_count--;
5918			old_pd->pd_wwn_hnext = NULL;
5919
5920			fctl_delist_did_table(port, old_pd);
5921			node = old_pd->pd_remote_nodep;
5922			ASSERT(node != NULL);
5923
5924			initiator = (old_pd->pd_recepient ==
5925			    PD_PLOGI_INITIATOR) ? 1 : 0;
5926
5927			mutex_exit(&old_pd->pd_mutex);
5928
5929			if (FC_IS_TOP_SWITCH(port->fp_topology) && initiator) {
5930				mutex_exit(&port->fp_mutex);
5931
5932				(void) fctl_add_orphan(port, old_pd,
5933				    KM_NOSLEEP);
5934			} else {
5935				mutex_exit(&port->fp_mutex);
5936			}
5937
5938			if (fctl_destroy_remote_port(port, old_pd) == 0) {
5939				if (node) {
5940					fctl_destroy_remote_node(node);
5941				}
5942			}
5943
5944			mutex_enter(&port->fp_mutex);
5945		}
5946	}
5947
5948	mutex_exit(&port->fp_mutex);
5949}
5950
5951
5952static void
5953fctl_check_alpa_list(fc_local_port_t *port, fc_remote_port_t *pd)
5954{
5955	ASSERT(MUTEX_HELD(&port->fp_mutex));
5956	ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
5957
5958	if (fctl_is_alpa_present(port, pd->pd_port_id.port_id) == FC_SUCCESS) {
5959		return;
5960	}
5961
5962	cmn_err(CE_WARN, "!fctl(%d): AL_PA=0x%x doesn't exist in LILP map",
5963	    port->fp_instance, pd->pd_port_id.port_id);
5964}
5965
5966
5967static int
5968fctl_is_alpa_present(fc_local_port_t *port, uchar_t alpa)
5969{
5970	int index;
5971
5972	ASSERT(MUTEX_HELD(&port->fp_mutex));
5973	ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
5974
5975	for (index = 0; index < port->fp_lilp_map.lilp_length; index++) {
5976		if (port->fp_lilp_map.lilp_alpalist[index] == alpa) {
5977			return (FC_SUCCESS);
5978		}
5979	}
5980
5981	return (FC_FAILURE);
5982}
5983
5984
5985fc_remote_port_t *
5986fctl_lookup_pd_by_did(fc_local_port_t *port, uint32_t d_id)
5987{
5988	int			index;
5989	struct pwwn_hash	*head;
5990	fc_remote_port_t	*pd;
5991
5992	ASSERT(MUTEX_HELD(&port->fp_mutex));
5993
5994	for (index = 0; index < pwwn_table_size; index++) {
5995		head = &port->fp_pwwn_table[index];
5996		pd = head->pwwn_head;
5997
5998		while (pd != NULL) {
5999			mutex_enter(&pd->pd_mutex);
6000			if (pd->pd_port_id.port_id == d_id) {
6001				mutex_exit(&pd->pd_mutex);
6002				return (pd);
6003			}
6004			mutex_exit(&pd->pd_mutex);
6005			pd = pd->pd_wwn_hnext;
6006		}
6007	}
6008
6009	return (pd);
6010}
6011
6012
6013/*
6014 * trace debugging
6015 */
6016void
6017fc_trace_debug(fc_trace_logq_t *logq, caddr_t name, int dflag, int dlevel,
6018    int errno, const char *fmt, ...)
6019{
6020	char	buf[FC_MAX_TRACE_BUF_LEN + 3]; /* 3 is for "\n" */
6021	char	*bufptr = buf;
6022	va_list	ap;
6023	int	cnt = 0;
6024
6025	if ((dlevel & dflag) == 0) {
6026		return;
6027	}
6028
6029	if (name) {
6030		cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>%s::",
6031		    logq->il_id++, name);
6032	} else {
6033		cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>trace::",
6034		    logq->il_id++);
6035	}
6036
6037	if (cnt < FC_MAX_TRACE_BUF_LEN) {
6038		va_start(ap, fmt);
6039		cnt += vsnprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
6040		    fmt, ap);
6041		va_end(ap);
6042	}
6043
6044	if (cnt > FC_MAX_TRACE_BUF_LEN) {
6045		cnt = FC_MAX_TRACE_BUF_LEN;
6046	}
6047	if (errno && (cnt < FC_MAX_TRACE_BUF_LEN)) {
6048		cnt += snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
6049		    "error=0x%x\n", errno);
6050	}
6051	(void) snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 3 - cnt, "\n");
6052
6053	if (logq && (dlevel & FC_TRACE_LOG_BUF) != 0) {
6054		fc_trace_logmsg(logq, buf, dlevel);
6055	}
6056
6057	/*
6058	 * We do not want to print the log numbers that appear as
6059	 * random numbers at the console and messages files, to
6060	 * the user.
6061	 */
6062	if ((bufptr = strchr(buf, '>')) == NULL) {
6063		/*
6064		 * We would have added the a string with "=>" above and so,
6065		 * ideally, we should not get here at all. But, if we do,
6066		 * we'll just use the full buf.
6067		 */
6068		bufptr = buf;
6069	} else {
6070		bufptr++;
6071	}
6072
6073	switch (dlevel & FC_TRACE_LOG_MASK) {
6074	case FC_TRACE_LOG_CONSOLE:
6075		cmn_err(CE_WARN, "%s", bufptr);
6076		break;
6077
6078	case FC_TRACE_LOG_CONSOLE_MSG:
6079		cmn_err(CE_WARN, "%s", bufptr);
6080		break;
6081
6082	case FC_TRACE_LOG_MSG:
6083		cmn_err(CE_WARN, "!%s", bufptr);
6084		break;
6085
6086	default:
6087		break;
6088	}
6089}
6090
6091
6092/*
6093 * This function can block
6094 */
6095fc_trace_logq_t *
6096fc_trace_alloc_logq(int maxsize)
6097{
6098	fc_trace_logq_t *logq;
6099
6100	logq = kmem_zalloc(sizeof (*logq), KM_SLEEP);
6101
6102	mutex_init(&logq->il_lock, NULL, MUTEX_DRIVER, NULL);
6103	logq->il_hiwat = maxsize;
6104	logq->il_flags |= FC_TRACE_LOGQ_V2;
6105
6106	return (logq);
6107}
6108
6109
6110void
6111fc_trace_free_logq(fc_trace_logq_t *logq)
6112{
6113	mutex_enter(&logq->il_lock);
6114	while (logq->il_msgh) {
6115		fc_trace_freemsg(logq);
6116	}
6117	mutex_exit(&logq->il_lock);
6118
6119	mutex_destroy(&logq->il_lock);
6120	kmem_free(logq, sizeof (*logq));
6121}
6122
6123
6124/* ARGSUSED */
6125void
6126fc_trace_logmsg(fc_trace_logq_t *logq, caddr_t buf, int level)
6127{
6128	int		qfull = 0;
6129	fc_trace_dmsg_t	*dmsg;
6130
6131	dmsg = kmem_alloc(sizeof (*dmsg), KM_NOSLEEP);
6132	if (dmsg == NULL) {
6133		mutex_enter(&logq->il_lock);
6134		logq->il_afail++;
6135		mutex_exit(&logq->il_lock);
6136
6137		return;
6138	}
6139
6140	gethrestime(&dmsg->id_time);
6141
6142	dmsg->id_size = strlen(buf) + 1;
6143	dmsg->id_buf = kmem_alloc(dmsg->id_size, KM_NOSLEEP);
6144	if (dmsg->id_buf == NULL) {
6145		kmem_free(dmsg, sizeof (*dmsg));
6146
6147		mutex_enter(&logq->il_lock);
6148		logq->il_afail++;
6149		mutex_exit(&logq->il_lock);
6150
6151		return;
6152	}
6153	bcopy(buf, dmsg->id_buf, strlen(buf));
6154	dmsg->id_buf[strlen(buf)] = '\0';
6155
6156	mutex_enter(&logq->il_lock);
6157
6158	logq->il_size += dmsg->id_size;
6159	if (logq->il_size >= logq->il_hiwat) {
6160		qfull = 1;
6161	}
6162
6163	if (qfull) {
6164		fc_trace_freemsg(logq);
6165	}
6166
6167	dmsg->id_next = NULL;
6168	if (logq->il_msgt) {
6169		logq->il_msgt->id_next = dmsg;
6170	} else {
6171		ASSERT(logq->il_msgh == NULL);
6172		logq->il_msgh = dmsg;
6173	}
6174	logq->il_msgt = dmsg;
6175
6176	mutex_exit(&logq->il_lock);
6177}
6178
6179
6180static void
6181fc_trace_freemsg(fc_trace_logq_t *logq)
6182{
6183	fc_trace_dmsg_t	*dmsg;
6184
6185	ASSERT(MUTEX_HELD(&logq->il_lock));
6186
6187	if ((dmsg = logq->il_msgh) != NULL) {
6188		logq->il_msgh = dmsg->id_next;
6189		if (logq->il_msgh == NULL) {
6190			logq->il_msgt = NULL;
6191		}
6192
6193		logq->il_size -= dmsg->id_size;
6194		kmem_free(dmsg->id_buf, dmsg->id_size);
6195		kmem_free(dmsg, sizeof (*dmsg));
6196	} else {
6197		ASSERT(logq->il_msgt == NULL);
6198	}
6199}
6200
6201/*
6202 * Used by T11 FC-HBA to fetch discovered ports by index.
6203 * Returns NULL if the index isn't valid.
6204 */
6205fc_remote_port_t *
6206fctl_lookup_pd_by_index(fc_local_port_t *port, uint32_t index)
6207{
6208	int			outer;
6209	int			match = 0;
6210	struct pwwn_hash	*head;
6211	fc_remote_port_t	*pd;
6212
6213	ASSERT(MUTEX_HELD(&port->fp_mutex));
6214
6215	for (outer = 0;
6216	    outer < pwwn_table_size && match <= index;
6217	    outer++) {
6218		head = &port->fp_pwwn_table[outer];
6219		pd = head->pwwn_head;
6220		if (pd != NULL) match ++;
6221
6222		while (pd != NULL && match <= index) {
6223			pd = pd->pd_wwn_hnext;
6224			if (pd != NULL) match ++;
6225		}
6226	}
6227
6228	return (pd);
6229}
6230
6231/*
6232 * Search for a matching Node or Port WWN in the discovered port list
6233 */
6234fc_remote_port_t *
6235fctl_lookup_pd_by_wwn(fc_local_port_t *port, la_wwn_t wwn)
6236{
6237	int			index;
6238	struct pwwn_hash	*head;
6239	fc_remote_port_t	*pd;
6240
6241	ASSERT(MUTEX_HELD(&port->fp_mutex));
6242
6243	for (index = 0; index < pwwn_table_size; index++) {
6244		head = &port->fp_pwwn_table[index];
6245		pd = head->pwwn_head;
6246
6247		while (pd != NULL) {
6248			mutex_enter(&pd->pd_mutex);
6249			if (bcmp(pd->pd_port_name.raw_wwn, wwn.raw_wwn,
6250			    sizeof (la_wwn_t)) == 0) {
6251				mutex_exit(&pd->pd_mutex);
6252				return (pd);
6253			}
6254			if (bcmp(pd->pd_remote_nodep->fd_node_name.raw_wwn,
6255			    wwn.raw_wwn, sizeof (la_wwn_t)) == 0) {
6256				mutex_exit(&pd->pd_mutex);
6257				return (pd);
6258			}
6259			mutex_exit(&pd->pd_mutex);
6260			pd = pd->pd_wwn_hnext;
6261		}
6262	}
6263	/* No match */
6264	return (NULL);
6265}
6266
6267
6268/*
6269 * Count the number of ports on this adapter.
6270 * This routine will walk the port list and count up the number of adapters
6271 * with matching fp_hba_port_attrs.hba_fru_details.high and
6272 * fp_hba_port_attrs.hba_fru_details.low.
6273 *
6274 * port->fp_mutex must not be held.
6275 */
6276int
6277fctl_count_fru_ports(fc_local_port_t *port, int npivflag)
6278{
6279	fca_hba_fru_details_t	*fru;
6280	fc_fca_port_t	*fca_port;
6281	fc_local_port_t	*tmpPort = NULL;
6282	uint32_t	count = 1;
6283
6284	mutex_enter(&fctl_port_lock);
6285
6286	mutex_enter(&port->fp_mutex);
6287	fru = &port->fp_hba_port_attrs.hba_fru_details;
6288
6289	/* Detect FCA drivers that don't support linking HBA ports */
6290	if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
6291		mutex_exit(&port->fp_mutex);
6292		mutex_exit(&fctl_port_lock);
6293		return (1);
6294	}
6295
6296	for (fca_port = fctl_fca_portlist; fca_port != NULL;
6297	    fca_port = fca_port->port_next) {
6298		tmpPort = fca_port->port_handle;
6299		if (tmpPort == port) {
6300			continue;
6301		}
6302		mutex_enter(&tmpPort->fp_mutex);
6303
6304		/*
6305		 * If an FCA driver returns unique fru->high and fru->low for
6306		 * ports on the same card, there is no way for the transport
6307		 * layer to determine that the two ports on the same FRU. So,
6308		 * the discovery of the ports on a same FRU  is limited to what
6309		 * the FCA driver can report back.
6310		 */
6311		if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
6312		    fru->high &&
6313		    tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
6314		    fru->low) {
6315			/* Now double check driver */
6316			if (strncmp(port->fp_hba_port_attrs.driver_name,
6317			    tmpPort->fp_hba_port_attrs.driver_name,
6318			    FCHBA_DRIVER_NAME_LEN) == 0) {
6319				if (!npivflag ||
6320				    (tmpPort->fp_npiv_type != FC_NPIV_PORT)) {
6321					count++;
6322				}
6323			} /* Else, different FCA driver */
6324		} /* Else not the same HBA FRU */
6325		mutex_exit(&tmpPort->fp_mutex);
6326	}
6327
6328	mutex_exit(&port->fp_mutex);
6329	mutex_exit(&fctl_port_lock);
6330
6331	return (count);
6332}
6333
6334fc_fca_port_t *
6335fctl_local_port_list_add(fc_fca_port_t *list, fc_local_port_t *port)
6336{
6337	fc_fca_port_t *tmp = list, *newentry = NULL;
6338
6339	newentry = kmem_zalloc(sizeof (fc_fca_port_t), KM_NOSLEEP);
6340	if (newentry == NULL) {
6341		return (list);
6342	}
6343	newentry->port_handle = port;
6344
6345	if (tmp == NULL) {
6346		return (newentry);
6347	}
6348	while (tmp->port_next != NULL)	tmp = tmp->port_next;
6349	tmp->port_next = newentry;
6350
6351	return (list);
6352}
6353
6354void
6355fctl_local_port_list_free(fc_fca_port_t *list)
6356{
6357	fc_fca_port_t *tmp = list, *nextentry;
6358
6359	if (tmp == NULL) {
6360		return;
6361	}
6362
6363	while (tmp != NULL) {
6364		nextentry = tmp->port_next;
6365		kmem_free(tmp, sizeof (*tmp));
6366		tmp = nextentry;
6367	}
6368}
6369
6370/*
6371 * Fetch another port on the HBA FRU based on index.
6372 * Returns NULL if index not found.
6373 *
6374 * port->fp_mutex must not be held.
6375 */
6376fc_local_port_t *
6377fctl_get_adapter_port_by_index(fc_local_port_t *port, uint32_t port_index)
6378{
6379	fca_hba_fru_details_t	*fru;
6380	fc_fca_port_t	*fca_port;
6381	fc_local_port_t	*tmpPort = NULL;
6382	fc_fca_port_t	*list = NULL, *tmpEntry;
6383	fc_local_port_t		*phyPort, *virPort = NULL;
6384	int	index, phyPortNum = 0;
6385
6386	mutex_enter(&fctl_port_lock);
6387
6388	mutex_enter(&port->fp_mutex);
6389	fru = &port->fp_hba_port_attrs.hba_fru_details;
6390
6391	/* Are we looking for this port? */
6392	if (fru->port_index == port_index) {
6393		mutex_exit(&port->fp_mutex);
6394		mutex_exit(&fctl_port_lock);
6395		return (port);
6396	}
6397
6398	/* Detect FCA drivers that don't support linking HBA ports */
6399	if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
6400		mutex_exit(&port->fp_mutex);
6401		mutex_exit(&fctl_port_lock);
6402		return (NULL);
6403	}
6404
6405	list = fctl_local_port_list_add(list, port);
6406	phyPortNum++;
6407	/* Loop through all known ports */
6408	for (fca_port = fctl_fca_portlist; fca_port != NULL;
6409	    fca_port = fca_port->port_next) {
6410		tmpPort = fca_port->port_handle;
6411		if (tmpPort == port) {
6412			/* Skip the port that was passed in as the argument */
6413			continue;
6414		}
6415		mutex_enter(&tmpPort->fp_mutex);
6416
6417		/* See if this port is on the same HBA FRU (fast check) */
6418		if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
6419		    fru->high &&
6420		    tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
6421		    fru->low) {
6422			/* Now double check driver (slower check) */
6423			if (strncmp(port->fp_hba_port_attrs.driver_name,
6424			    tmpPort->fp_hba_port_attrs.driver_name,
6425			    FCHBA_DRIVER_NAME_LEN) == 0) {
6426
6427				fru =
6428				    &tmpPort->fp_hba_port_attrs.hba_fru_details;
6429				/* Check for the matching port_index */
6430				if ((tmpPort->fp_npiv_type != FC_NPIV_PORT) &&
6431				    (fru->port_index == port_index)) {
6432					/* Found it! */
6433					mutex_exit(&tmpPort->fp_mutex);
6434					mutex_exit(&port->fp_mutex);
6435					mutex_exit(&fctl_port_lock);
6436					fctl_local_port_list_free(list);
6437					return (tmpPort);
6438				}
6439				if (tmpPort->fp_npiv_type != FC_NPIV_PORT) {
6440					(void) fctl_local_port_list_add(list,
6441					    tmpPort);
6442					phyPortNum++;
6443				}
6444			} /* Else, different FCA driver */
6445		} /* Else not the same HBA FRU */
6446		mutex_exit(&tmpPort->fp_mutex);
6447
6448	}
6449
6450	/* scan all physical port on same chip to find virtual port */
6451	tmpEntry = list;
6452	index = phyPortNum - 1;
6453	virPort = NULL;
6454	while (index < port_index) {
6455		if (tmpEntry == NULL) {
6456			break;
6457		}
6458		if (virPort == NULL) {
6459			phyPort = tmpEntry->port_handle;
6460			virPort = phyPort->fp_port_next;
6461			if (virPort == NULL) {
6462				tmpEntry = tmpEntry->port_next;
6463				continue;
6464			}
6465		} else {
6466			virPort = virPort->fp_port_next;
6467		}
6468		if (virPort == phyPort) {
6469			tmpEntry = tmpEntry->port_next;
6470			virPort = NULL;
6471		} else {
6472			index++;
6473		}
6474	}
6475	mutex_exit(&port->fp_mutex);
6476	mutex_exit(&fctl_port_lock);
6477
6478	fctl_local_port_list_free(list);
6479	if (virPort) {
6480		return (virPort);
6481	}
6482	return (NULL);
6483}
6484
6485int
6486fctl_busy_port(fc_local_port_t *port)
6487{
6488	ASSERT(!MUTEX_HELD(&port->fp_mutex));
6489
6490	mutex_enter(&port->fp_mutex);
6491	if (port->fp_soft_state & FP_SOFT_NO_PMCOMP) {
6492		/*
6493		 * If fctl_busy_port() is called before we've registered our
6494		 * PM components, we return success. We need to be aware of
6495		 * this because the caller will eventually call fctl_idle_port.
6496		 * This wouldn't be a problem except that if we have
6497		 * registered our PM components in the meantime, we will
6498		 * then be idling a component that was never busied.  PM
6499		 * will be very unhappy if we do this.	Thus, we keep
6500		 * track of this with port->fp_pm_busy_nocomp.
6501		 */
6502		port->fp_pm_busy_nocomp++;
6503		mutex_exit(&port->fp_mutex);
6504		return (0);
6505	}
6506	port->fp_pm_busy++;
6507	mutex_exit(&port->fp_mutex);
6508
6509	if (pm_busy_component(port->fp_port_dip,
6510	    FP_PM_COMPONENT) != DDI_SUCCESS) {
6511		mutex_enter(&port->fp_mutex);
6512		port->fp_pm_busy--;
6513		mutex_exit(&port->fp_mutex);
6514		return (ENXIO);
6515	}
6516
6517	mutex_enter(&port->fp_mutex);
6518	if (port->fp_pm_level == FP_PM_PORT_DOWN) {
6519		mutex_exit(&port->fp_mutex);
6520		if (pm_raise_power(port->fp_port_dip, FP_PM_COMPONENT,
6521		    FP_PM_PORT_UP) != DDI_SUCCESS) {
6522
6523			mutex_enter(&port->fp_mutex);
6524			port->fp_pm_busy--;
6525			mutex_exit(&port->fp_mutex);
6526
6527			(void) pm_idle_component(port->fp_port_dip,
6528			    FP_PM_COMPONENT);
6529			return (EIO);
6530		}
6531		return (0);
6532	}
6533	mutex_exit(&port->fp_mutex);
6534	return (0);
6535}
6536
6537void
6538fctl_idle_port(fc_local_port_t *port)
6539{
6540	ASSERT(!MUTEX_HELD(&port->fp_mutex));
6541
6542	mutex_enter(&port->fp_mutex);
6543
6544	/*
6545	 * If port->fp_pm_busy_nocomp is > 0, that means somebody had
6546	 * called fctl_busy_port prior to us registering our PM components.
6547	 * In that case, we just decrement fp_pm_busy_nocomp and return.
6548	 */
6549
6550	if (port->fp_pm_busy_nocomp > 0) {
6551		port->fp_pm_busy_nocomp--;
6552		mutex_exit(&port->fp_mutex);
6553		return;
6554	}
6555
6556	port->fp_pm_busy--;
6557	mutex_exit(&port->fp_mutex);
6558
6559	(void) pm_idle_component(port->fp_port_dip, FP_PM_COMPONENT);
6560}
6561
6562/*
6563 *     Function: fctl_tc_timer
6564 *
6565 *  Description: Resets the value of the timed counter.
6566 *
6567 *    Arguments: *tc		Timed counter
6568 *
6569 * Return Value: Nothing