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