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