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