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