xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/impl/fctl.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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!  */
108 int did_table_size = D_ID_HASH_TABLE_SIZE;
109 int pwwn_table_size = PWWN_HASH_TABLE_SIZE;
110 
111 static fc_ulp_module_t	*fctl_ulp_modules;
112 static fc_fca_port_t	*fctl_fca_portlist;
113 static fc_ulp_list_t	*fctl_ulp_list;
114 
115 static char fctl_greeting[] =
116 	"fctl: %s ULP same type (0x%x) as existing module.\n";
117 
118 static char *fctl_undefined = "Undefined";
119 
120 /*
121  * This lock protects the fc_ulp_module_t linked list (i.e. mod_next field)
122  */
123 
124 static 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 
131 static 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 
139 static kmutex_t fctl_port_lock;
140 static kmutex_t	fctl_ulp_list_mutex;
141 
142 static fctl_nwwn_list_t		*fctl_nwwn_hash_table;
143 static kmutex_t			fctl_nwwn_hash_mutex;
144 int 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 
160 char *fctl_version = FCTL_NAME_VERSION;
161 
162 extern struct mod_ops mod_miscops;
163 
164 static struct modlmisc modlmisc = {
165 	&mod_miscops,			/* type of module */
166 	FCTL_NAME_VERSION		/* Module name */
167 };
168 
169 static struct modlinkage modlinkage = {
170 	MODREV_1, (void *)&modlmisc, NULL
171 };
172 
173 static 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 
205 struct kmem_cache *fctl_job_cache;
206 
207 static 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 
256 fc_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 
262 fc_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 
292 fc_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 
326 fc_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 
333 fc_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 
339 fc_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 
350 fc_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 
363 fc_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 
369 fc_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 
375 fc_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 
382 fc_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 
503 int
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  */
552 int
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 
573 int
574 _info(struct modinfo *modinfo_p)
575 {
576 	return (mod_info(&modlinkage, modinfo_p));
577 }
578 
579 
580 /* ARGSUSED */
581 static int
582 fctl_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 */
595 static void
596 fctl_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  */
621 int
622 fc_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  */
743 int
744 fc_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  */
844 int
845 fc_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  */
882 int
883 fc_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 
934 int
935 fc_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 
1039 int
1040 fc_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 
1155 opaque_t
1156 fc_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  */
1275 int
1276 fc_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 
1359 int
1360 fc_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 
1482 int
1483 fc_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 
1547 int
1548 fc_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 
1558 int
1559 fc_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 
1568 int
1569 fc_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 
1578 int
1579 fc_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  */
1596 int
1597 fc_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 
1670 int
1671 fc_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 
1705 int
1706 fc_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 
1716 int
1717 fc_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 
1725 opaque_t
1726 fc_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 
1744 int
1745 fc_ulp_error(int fc_errno, char **errmsg)
1746 {
1747 	return (fctl_error(fc_errno, errmsg));
1748 }
1749 
1750 
1751 int
1752 fc_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  */
1762 int
1763 fc_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  */
1784 int
1785 fc_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  */
1806 int
1807 fc_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 
1844 opaque_t
1845 fc_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 
1857 int
1858 fc_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 
1881 void
1882 fc_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 
1895 void
1896 fc_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  */
1917 void
1918 fc_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  */
1929 int
1930 fc_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  */
1955 int
1956 fc_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  */
1972 int
1973 fc_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 
2050 int
2051 fc_fca_error(int fc_errno, char **errmsg)
2052 {
2053 	return (fctl_error(fc_errno, errmsg));
2054 }
2055 
2056 
2057 int
2058 fc_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  */
2072 void
2073 fc_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 
2087 void
2088 fc_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  */
2105 static int
2106 fctl_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  */
2136 static int
2137 fctl_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 */
2205 static int
2206 fctl_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 
2213 static dev_info_t *
2214 fctl_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 
2248 int
2249 fctl_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 
2270 int
2271 fctl_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 
2294 void
2295 fctl_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 
2311 int
2312 fctl_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);
2407 freememory:
2408 	kmem_free(devstr, devstrlen);
2409 	kmem_free(devname, MAXNAMELEN);
2410 
2411 	return (rval);
2412 }
2413 
2414 
2415 void
2416 fctl_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 
2430 void
2431 fctl_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 
2478 void
2479 fctl_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 
2595 static int
2596 fctl_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 
2634 static void
2635 fctl_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 
2693 int
2694 fctl_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 
2749 static	void
2750 fctl_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 
2783 static int
2784 fctl_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 
2821 static void
2822 fctl_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 
2874 static fc_ulp_ports_t *
2875 fctl_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 
2909 static fc_ulp_ports_t *
2910 fctl_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 
2924 static int
2925 fctl_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 
2965 static void
2966 fctl_dealloc_ulp_port(fc_ulp_ports_t *next)
2967 {
2968 	mutex_destroy(&next->port_mutex);
2969 	kmem_free(next, sizeof (*next));
2970 }
2971 
2972 
2973 static fc_ulp_ports_t *
2974 fctl_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  */
3001 void
3002 fctl_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  */
3226 fc_remote_node_t *
3227 fctl_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  */
3256 void
3257 fctl_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  */
3285 int
3286 fctl_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  */
3323 void
3324 fctl_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  */
3376 fc_remote_node_t *
3377 fctl_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  */
3416 fc_remote_node_t *
3417 fctl_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  */
3454 fc_remote_port_t *
3455 fctl_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  */
3485 void
3486 fctl_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  */
3500 void
3501 fctl_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  */
3554 int
3555 fctl_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  */
3619 void
3620 fctl_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  */
3683 void
3684 fctl_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  */
3731 void
3732 fctl_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  */
3800 void
3801 fctl_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  */
3856 fc_remote_port_t *
3857 fctl_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 
3888 void
3889 fc_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  */
3909 fc_remote_port_t *
3910 fctl_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  */
3949 fc_remote_port_t *
3950 fctl_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  */
3984 fc_remote_port_t *
3985 fctl_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  */
4022 fc_remote_port_t *
4023 fctl_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  */
4070 void
4071 fctl_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 
4114 void
4115 fctl_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 
4294 job_request_t *
4295 fctl_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 
4320 void
4321 fctl_dealloc_job(job_request_t *job)
4322 {
4323 	kmem_cache_free(fctl_job_cache, (void *)job);
4324 }
4325 
4326 
4327 void
4328 fctl_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 
4348 job_request_t *
4349 fctl_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 
4371 void
4372 fctl_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 
4390 void
4391 fctl_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 
4399 void
4400 fctl_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  */
4422 int
4423 fctl_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  */
4454 int
4455 fctl_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  */
4518 fc_remote_port_t *
4519 fctl_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  */
4736 int
4737 fctl_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  */
4812 void
4813 fctl_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 
4892 int
4893 fctl_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 
4907 void
4908 fctl_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  */
5011 void
5012 fctl_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 
5039 void
5040 fctl_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 
5070 static int
5071 fctl_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 
5162 static int
5163 fctl_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 
5246 fctl_ns_req_t *
5247 fctl_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 
5285 void
5286 fctl_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 
5298 int
5299 fctl_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  */
5351 int
5352 fc_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 
5359 void
5360 fc_ulp_idle_port(opaque_t port_handle)
5361 {
5362 	fc_local_port_t *port = port_handle;
5363 	fctl_idle_port(port);
5364 }
5365 
5366 void
5367 fc_ulp_copy_portmap(fc_portmap_t *map, opaque_t pd)
5368 {
5369 	fctl_copy_portmap(map, (fc_remote_port_t *)pd);
5370 }
5371 
5372 
5373 int
5374 fc_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 
5394 fc_local_port_t *
5395 fc_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 
5423 int
5424 fc_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 
5449 fc_local_port_t *
5450 fc_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  */
5485 int
5486 fc_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 
5596 uint32_t
5597 fc_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  */
5619 int
5620 fctl_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 
5656 int
5657 fctl_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 
5700 int
5701 fctl_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 
5732 static void
5733 fctl_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 */
5762 static void
5763 fctl_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 
5775 static int
5776 fctl_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  */
5803 static int
5804 fctl_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  */
5870 void
5871 fctl_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 
5952 static void
5953 fctl_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 
5967 static int
5968 fctl_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 
5985 fc_remote_port_t *
5986 fctl_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  */
6016 void
6017 fc_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  */
6095 fc_trace_logq_t *
6096 fc_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 
6110 void
6111 fc_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 */
6125 void
6126 fc_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 
6180 static void
6181 fc_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  */
6205 fc_remote_port_t *
6206 fctl_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  */
6234 fc_remote_port_t *
6235 fctl_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  */
6276 int
6277 fctl_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 
6334 fc_fca_port_t *
6335 fctl_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 
6354 void
6355 fctl_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  */
6376 fc_local_port_t *
6377 fctl_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 
6485 int
6486 fctl_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 
6537 void
6538 fctl_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
6570  *
6571  *	Context: Kernel context.
6572  */
6573 static void
6574 fctl_tc_timer(void *arg)
6575 {
6576 	timed_counter_t	*tc = (timed_counter_t *)arg;
6577 
6578 	ASSERT(tc != NULL);
6579 	ASSERT(tc->sig == tc);
6580 
6581 	mutex_enter(&tc->mutex);
6582 	if (tc->active) {
6583 		tc->active = B_FALSE;
6584 		tc->counter = 0;
6585 	}
6586 	mutex_exit(&tc->mutex);
6587 }
6588 
6589 /*
6590  *     Function: fctl_tc_constructor
6591  *
6592  *  Description: Constructs a timed counter.
6593  *
6594  *    Arguments: *tc		Address where the timed counter will reside.
6595  *		 max_value	Maximum value the counter is allowed to take.
6596  *		 timer		Number of microseconds after which the counter
6597  *				will be reset. The timer is started when the
6598  *				value of the counter goes from 0 to 1.
6599  *
6600  * Return Value: Nothing
6601  *
6602  *	Context: Kernel context.
6603  */
6604 void
6605 fctl_tc_constructor(timed_counter_t *tc, uint32_t max_value, clock_t timer)
6606 {
6607 	ASSERT(tc != NULL);
6608 	ASSERT(tc->sig != tc);
6609 
6610 	bzero(tc, sizeof (*tc));
6611 	mutex_init(&tc->mutex, NULL, MUTEX_DRIVER, NULL);
6612 	tc->timer = drv_usectohz(timer);
6613 	tc->active = B_FALSE;
6614 	tc->maxed_out = B_FALSE;
6615 	tc->max_value = max_value;
6616 	tc->sig = tc;
6617 }
6618 
6619 /*
6620  *     Function: fctl_tc_destructor
6621  *
6622  *  Description: Destroyes a timed counter.
6623  *
6624  *    Arguments: *tc		Timed counter to destroy.
6625  *
6626  * Return Value: Nothing
6627  *
6628  *	Context: Kernel context.
6629  */
6630 void
6631 fctl_tc_destructor(timed_counter_t *tc)
6632 {
6633 	ASSERT(tc != NULL);
6634 	ASSERT(tc->sig == tc);
6635 	ASSERT(!mutex_owned(&tc->mutex));
6636 
6637 	mutex_enter(&tc->mutex);
6638 	if (tc->active) {
6639 		tc->active = B_FALSE;
6640 		mutex_exit(&tc->mutex);
6641 		(void) untimeout(tc->tid);
6642 		mutex_enter(&tc->mutex);
6643 		tc->sig = NULL;
6644 	}
6645 	mutex_exit(&tc->mutex);
6646 	mutex_destroy(&tc->mutex);
6647 }
6648 
6649 /*
6650  *     Function: fctl_tc_increment
6651  *
6652  *  Description: Increments a timed counter
6653  *
6654  *    Arguments: *tc		Timed counter to increment.
6655  *
6656  * Return Value: B_TRUE		Counter reached the max value.
6657  *		 B_FALSE	Counter hasn't reached the max value.
6658  *
6659  *	Context: Kernel or interrupt context.
6660  */
6661 boolean_t
6662 fctl_tc_increment(timed_counter_t *tc)
6663 {
6664 	ASSERT(tc != NULL);
6665 	ASSERT(tc->sig == tc);
6666 
6667 	mutex_enter(&tc->mutex);
6668 	if (!tc->maxed_out) {
6669 		/* Hasn't maxed out yet. */
6670 		++tc->counter;
6671 		if (tc->counter >= tc->max_value) {
6672 			/* Just maxed out. */
6673 			tc->maxed_out = B_TRUE;
6674 		}
6675 		if (!tc->active) {
6676 			tc->tid = timeout(fctl_tc_timer, tc, tc->timer);
6677 			tc->active = B_TRUE;
6678 		}
6679 	}
6680 	mutex_exit(&tc->mutex);
6681 
6682 	return (tc->maxed_out);
6683 }
6684 
6685 /*
6686  *     Function: fctl_tc_reset
6687  *
6688  *  Description: Resets a timed counter.  The caller of this function has to
6689  *		 to make sure that while in fctl_tc_reset() fctl_tc_increment()
6690  *		 is not called.
6691  *
6692  *    Arguments: *tc		Timed counter to reset.
6693  *
6694  * Return Value: 0		Counter reached the max value.
6695  *		 Not 0		Counter hasn't reached the max value.
6696  *
6697  *	Context: Kernel or interrupt context.
6698  */
6699 void
6700 fctl_tc_reset(timed_counter_t *tc)
6701 {
6702 	ASSERT(tc != NULL);
6703 	ASSERT(tc->sig == tc);
6704 
6705 	mutex_enter(&tc->mutex);
6706 	tc->counter = 0;
6707 	tc->maxed_out = B_FALSE;
6708 	if (tc->active) {
6709 		tc->active = B_FALSE;
6710 		(void) untimeout(tc->tid);
6711 	}
6712 	mutex_exit(&tc->mutex);
6713 }
6714 
6715 void
6716 fc_ulp_log_device_event(opaque_t port_handle, int type)
6717 {
6718 	fc_local_port_t *port = port_handle;
6719 	nvlist_t *attr_list;
6720 
6721 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
6722 	    KM_SLEEP) != DDI_SUCCESS) {
6723 		return;
6724 	}
6725 
6726 	if (nvlist_add_uint32(attr_list, "instance",
6727 	    port->fp_instance) != DDI_SUCCESS) {
6728 		goto error;
6729 	}
6730 
6731 	if (nvlist_add_byte_array(attr_list, "port-wwn",
6732 	    port->fp_service_params.nport_ww_name.raw_wwn,
6733 	    sizeof (la_wwn_t)) != DDI_SUCCESS) {
6734 		goto error;
6735 	}
6736 
6737 	(void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW, EC_SUNFC,
6738 	    (type == FC_ULP_DEVICE_ONLINE) ?
6739 	    ESC_SUNFC_DEVICE_ONLINE : ESC_SUNFC_DEVICE_OFFLINE,
6740 	    attr_list, NULL, DDI_SLEEP);
6741 	nvlist_free(attr_list);
6742 	return;
6743 
6744 error:
6745 	nvlist_free(attr_list);
6746 }
6747