fp.c revision 8201:dc6d67c22e5b
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * NOT a DDI compliant Sun Fibre Channel port driver(fp)
26 *
27 */
28
29#include <sys/types.h>
30#include <sys/varargs.h>
31#include <sys/param.h>
32#include <sys/errno.h>
33#include <sys/uio.h>
34#include <sys/buf.h>
35#include <sys/modctl.h>
36#include <sys/open.h>
37#include <sys/file.h>
38#include <sys/kmem.h>
39#include <sys/poll.h>
40#include <sys/conf.h>
41#include <sys/thread.h>
42#include <sys/var.h>
43#include <sys/cmn_err.h>
44#include <sys/stat.h>
45#include <sys/ddi.h>
46#include <sys/sunddi.h>
47#include <sys/promif.h>
48#include <sys/nvpair.h>
49#include <sys/byteorder.h>
50#include <sys/scsi/scsi.h>
51#include <sys/fibre-channel/fc.h>
52#include <sys/fibre-channel/impl/fc_ulpif.h>
53#include <sys/fibre-channel/impl/fc_fcaif.h>
54#include <sys/fibre-channel/impl/fctl_private.h>
55#include <sys/fibre-channel/impl/fc_portif.h>
56#include <sys/fibre-channel/impl/fp.h>
57
58/* These are defined in fctl.c! */
59extern int did_table_size;
60extern int pwwn_table_size;
61
62static struct cb_ops fp_cb_ops = {
63	fp_open,			/* open */
64	fp_close,			/* close */
65	nodev,				/* strategy */
66	nodev,				/* print */
67	nodev,				/* dump */
68	nodev,				/* read */
69	nodev,				/* write */
70	fp_ioctl,			/* ioctl */
71	nodev,				/* devmap */
72	nodev,				/* mmap */
73	nodev,				/* segmap */
74	nochpoll,			/* chpoll */
75	ddi_prop_op,			/* cb_prop_op */
76	0,				/* streamtab */
77	D_NEW | D_MP | D_HOTPLUG,	/* cb_flag */
78	CB_REV,				/* rev */
79	nodev,				/* aread */
80	nodev				/* awrite */
81};
82
83static struct dev_ops fp_ops = {
84	DEVO_REV,	/* build revision */
85	0,		/* reference count */
86	fp_getinfo,	/* getinfo */
87	nulldev,	/* identify - Obsoleted */
88	nulldev,	/* probe */
89	fp_attach,	/* attach */
90	fp_detach,	/* detach */
91	nodev,		/* reset */
92	&fp_cb_ops,	/* cb_ops */
93	NULL,		/* bus_ops */
94	fp_power	/* power */
95};
96
97#define	FP_VERSION		"1.96"
98#define	FP_NAME_VERSION		"SunFC Port v" FP_VERSION
99
100char *fp_version = FP_NAME_VERSION;
101
102static struct modldrv modldrv = {
103	&mod_driverops,			/* Type of Module */
104	FP_NAME_VERSION,		/* Name/Version of fp */
105	&fp_ops				/* driver ops */
106};
107
108static struct modlinkage modlinkage = {
109	MODREV_1,	/* Rev of the loadable modules system */
110	&modldrv,	/* NULL terminated list of */
111	NULL		/* Linkage structures */
112};
113
114
115
116static uint16_t ns_reg_cmds[] = {
117	NS_RPN_ID,
118	NS_RNN_ID,
119	NS_RCS_ID,
120	NS_RFT_ID,
121	NS_RPT_ID,
122	NS_RSPN_ID,
123	NS_RSNN_NN
124};
125
126struct fp_xlat {
127	uchar_t	xlat_state;
128	int	xlat_rval;
129} fp_xlat [] = {
130	{ FC_PKT_SUCCESS,	FC_SUCCESS },
131	{ FC_PKT_REMOTE_STOP,	FC_FAILURE },
132	{ FC_PKT_LOCAL_RJT,	FC_FAILURE },
133	{ FC_PKT_NPORT_RJT,	FC_ELS_PREJECT },
134	{ FC_PKT_FABRIC_RJT,	FC_ELS_FREJECT },
135	{ FC_PKT_LOCAL_BSY,	FC_TRAN_BUSY },
136	{ FC_PKT_TRAN_BSY,	FC_TRAN_BUSY },
137	{ FC_PKT_NPORT_BSY,	FC_PBUSY },
138	{ FC_PKT_FABRIC_BSY,	FC_FBUSY },
139	{ FC_PKT_LS_RJT,	FC_FAILURE },
140	{ FC_PKT_BA_RJT,	FC_FAILURE },
141	{ FC_PKT_TIMEOUT,	FC_FAILURE },
142	{ FC_PKT_TRAN_ERROR,	FC_TRANSPORT_ERROR },
143	{ FC_PKT_FAILURE,	FC_FAILURE },
144	{ FC_PKT_PORT_OFFLINE,	FC_OFFLINE }
145};
146
147static uchar_t fp_valid_alpas[] = {
148	0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, 0x17, 0x18, 0x1B,
149	0x1D, 0x1E, 0x1F, 0x23, 0x25, 0x26, 0x27, 0x29, 0x2A,
150	0x2B, 0x2C, 0x2D, 0x2E, 0x31, 0x32, 0x33, 0x34, 0x35,
151	0x36, 0x39, 0x3A, 0x3C, 0x43, 0x45, 0x46, 0x47, 0x49,
152	0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x51, 0x52, 0x53, 0x54,
153	0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x67,
154	0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x71, 0x72, 0x73,
155	0x74, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x80, 0x81, 0x82,
156	0x84, 0x88, 0x8F, 0x90, 0x97, 0x98, 0x9B, 0x9D, 0x9E,
157	0x9F, 0xA3, 0xA5, 0xA6, 0xA7, 0xA9, 0xAA, 0xAB, 0xAC,
158	0xAD, 0xAE, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB9,
159	0xBA, 0xBC, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB,
160	0xCC, 0xCD, 0xCE, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
161	0xD9, 0xDA, 0xDC, 0xE0, 0xE1, 0xE2, 0xE4, 0xE8, 0xEF
162};
163
164static struct fp_perms {
165	uint16_t	fp_ioctl_cmd;
166	uchar_t		fp_open_flag;
167} fp_perm_list [] = {
168	{ FCIO_GET_NUM_DEVS, 		FP_OPEN },
169	{ FCIO_GET_DEV_LIST,		FP_OPEN },
170	{ FCIO_GET_SYM_PNAME,		FP_OPEN },
171	{ FCIO_GET_SYM_NNAME,		FP_OPEN },
172	{ FCIO_SET_SYM_PNAME,		FP_EXCL },
173	{ FCIO_SET_SYM_NNAME,		FP_EXCL },
174	{ FCIO_GET_LOGI_PARAMS,		FP_OPEN },
175	{ FCIO_DEV_LOGIN,		FP_EXCL },
176	{ FCIO_DEV_LOGOUT,		FP_EXCL },
177	{ FCIO_GET_STATE,		FP_OPEN },
178	{ FCIO_DEV_REMOVE,		FP_EXCL },
179	{ FCIO_GET_FCODE_REV,		FP_OPEN },
180	{ FCIO_GET_FW_REV,		FP_OPEN },
181	{ FCIO_GET_DUMP_SIZE,		FP_OPEN },
182	{ FCIO_FORCE_DUMP,		FP_EXCL },
183	{ FCIO_GET_DUMP,		FP_OPEN },
184	{ FCIO_GET_TOPOLOGY,		FP_OPEN },
185	{ FCIO_RESET_LINK,		FP_EXCL },
186	{ FCIO_RESET_HARD,		FP_EXCL },
187	{ FCIO_RESET_HARD_CORE,		FP_EXCL },
188	{ FCIO_DIAG,			FP_OPEN },
189	{ FCIO_NS,			FP_EXCL },
190	{ FCIO_DOWNLOAD_FW,		FP_EXCL },
191	{ FCIO_DOWNLOAD_FCODE,		FP_EXCL },
192	{ FCIO_LINK_STATUS,		FP_OPEN },
193	{ FCIO_GET_HOST_PARAMS,		FP_OPEN },
194	{ FCIO_GET_NODE_ID,		FP_OPEN },
195	{ FCIO_SET_NODE_ID,		FP_EXCL },
196	{ FCIO_SEND_NODE_ID,		FP_OPEN },
197	{ FCIO_GET_ADAPTER_ATTRIBUTES,	FP_OPEN },
198	{ FCIO_GET_OTHER_ADAPTER_PORTS,	FP_OPEN },
199	{ FCIO_GET_ADAPTER_PORT_ATTRIBUTES,	FP_OPEN },
200	{ FCIO_GET_DISCOVERED_PORT_ATTRIBUTES,	FP_OPEN },
201	{ FCIO_GET_PORT_ATTRIBUTES,	FP_OPEN },
202	{ FCIO_GET_ADAPTER_PORT_STATS,	FP_OPEN },
203	{ FCIO_GET_ADAPTER_PORT_NPIV_ATTRIBUTES, FP_OPEN },
204	{ FCIO_GET_NPIV_PORT_LIST, FP_OPEN },
205	{ FCIO_DELETE_NPIV_PORT, FP_OPEN },
206	{ FCIO_GET_NPIV_ATTRIBUTES, FP_OPEN },
207	{ FCIO_CREATE_NPIV_PORT, FP_OPEN },
208	{ FCIO_NPIV_GET_ADAPTER_ATTRIBUTES, FP_OPEN }
209};
210
211static char *fp_pm_comps[] = {
212	"NAME=FC Port",
213	"0=Port Down",
214	"1=Port Up"
215};
216
217
218#ifdef	_LITTLE_ENDIAN
219#define	MAKE_BE_32(x)	{ \
220	uint32_t	*ptr1, i; \
221		ptr1 = (uint32_t *)(x); \
222		for (i = 0; i < sizeof (*(x)) / sizeof (uint32_t); i++) { \
223			*ptr1 = BE_32(*ptr1); \
224			ptr1++; \
225		} \
226	}
227#else
228#define	MAKE_BE_32(x)
229#endif
230
231static uchar_t fp_verbosity = (FP_WARNING_MESSAGES | FP_FATAL_MESSAGES);
232static uint32_t fp_options = 0;
233
234static int fp_cmd_wait_cnt = FP_CMDWAIT_DELAY;
235static int fp_retry_delay = FP_RETRY_DELAY;	/* retry after this delay */
236static int fp_retry_count = FP_RETRY_COUNT;	/* number of retries */
237unsigned int fp_offline_ticker;			/* seconds */
238
239/*
240 * Driver global variable to anchor the list of soft state structs for
241 * all fp driver instances.  Used with the Solaris DDI soft state functions.
242 */
243static void *fp_driver_softstate;
244
245static clock_t	fp_retry_ticks;
246static clock_t	fp_offline_ticks;
247
248static int fp_retry_ticker;
249static uint32_t fp_unsol_buf_count = FP_UNSOL_BUF_COUNT;
250static uint32_t fp_unsol_buf_size = FP_UNSOL_BUF_SIZE;
251
252static int		fp_log_size = FP_LOG_SIZE;
253static int		fp_trace = FP_TRACE_DEFAULT;
254static fc_trace_logq_t	*fp_logq = NULL;
255
256int fp_get_adapter_paths(char *pathList, int count);
257static void fp_log_port_event(fc_local_port_t *port, char *subclass);
258static void fp_log_target_event(fc_local_port_t *port, char *subclass,
259	    la_wwn_t tgt_pwwn, uint32_t port_id);
260static uint32_t fp_map_remote_port_state(uint32_t rm_state);
261static void fp_init_symbolic_names(fc_local_port_t *port);
262
263
264/*
265 * Perform global initialization
266 */
267int
268_init(void)
269{
270	int ret;
271
272	if ((ret = ddi_soft_state_init(&fp_driver_softstate,
273	    sizeof (struct fc_local_port), 8)) != 0) {
274		return (ret);
275	}
276
277	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
278		ddi_soft_state_fini(&fp_driver_softstate);
279		return (ret);
280	}
281
282	fp_logq = fc_trace_alloc_logq(fp_log_size);
283
284	if ((ret = mod_install(&modlinkage)) != 0) {
285		fc_trace_free_logq(fp_logq);
286		ddi_soft_state_fini(&fp_driver_softstate);
287		scsi_hba_fini(&modlinkage);
288	}
289
290	return (ret);
291}
292
293
294/*
295 * Prepare for driver unload
296 */
297int
298_fini(void)
299{
300	int ret;
301
302	if ((ret = mod_remove(&modlinkage)) == 0) {
303		fc_trace_free_logq(fp_logq);
304		ddi_soft_state_fini(&fp_driver_softstate);
305		scsi_hba_fini(&modlinkage);
306	}
307
308	return (ret);
309}
310
311
312/*
313 * Request mod_info() to handle all cases
314 */
315int
316_info(struct modinfo *modinfo)
317{
318	return (mod_info(&modlinkage, modinfo));
319}
320
321
322/*
323 * fp_attach:
324 *
325 * The respective cmd handlers take care of performing
326 * ULP related invocations
327 */
328static int
329fp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
330{
331	int rval;
332
333	/*
334	 * We check the value of fp_offline_ticker at this
335	 * point. The variable is global for the driver and
336	 * not specific to an instance.
337	 *
338	 * If there is no user-defined value found in /etc/system
339	 * or fp.conf, then we use 90 seconds (FP_OFFLINE_TICKER).
340	 * The minimum setting for this offline timeout according
341	 * to the FC-FS2 standard (Fibre Channel Framing and
342	 * Signalling-2, see www.t11.org) is R_T_TOV == 100msec.
343	 *
344	 * We do not recommend setting the value to less than 10
345	 * seconds (RA_TOV) or more than 90 seconds. If this
346	 * variable is greater than 90 seconds then drivers above
347	 * fp (fcp, sd, scsi_vhci, vxdmp et al) might complain.
348	 */
349
350	fp_offline_ticker = ddi_prop_get_int(DDI_DEV_T_ANY,
351	    dip, DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "fp_offline_ticker",
352	    FP_OFFLINE_TICKER);
353
354	if ((fp_offline_ticker < 10) ||
355	    (fp_offline_ticker > 90)) {
356		cmn_err(CE_WARN, "Setting fp_offline_ticker to "
357		    "%d second(s). This is outside the "
358		    "recommended range of 10..90 seconds",
359		    fp_offline_ticker);
360	}
361
362	/*
363	 * Tick every second when there are commands to retry.
364	 * It should tick at the least granular value of pkt_timeout
365	 * (which is one second)
366	 */
367	fp_retry_ticker = 1;
368
369	fp_retry_ticks = drv_usectohz(fp_retry_ticker * 1000 * 1000);
370	fp_offline_ticks = drv_usectohz(fp_offline_ticker * 1000 * 1000);
371
372	switch (cmd) {
373	case DDI_ATTACH:
374		rval = fp_attach_handler(dip);
375		break;
376
377	case DDI_RESUME:
378		rval = fp_resume_handler(dip);
379		break;
380
381	default:
382		rval = DDI_FAILURE;
383		break;
384	}
385	return (rval);
386}
387
388
389/*
390 * fp_detach:
391 *
392 * If a ULP fails to handle cmd request converse of
393 * cmd is invoked for ULPs that previously succeeded
394 * cmd request.
395 */
396static int
397fp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
398{
399	int 			rval = DDI_FAILURE;
400	fc_local_port_t 	*port;
401	fc_attach_cmd_t 	converse;
402	uint8_t			cnt;
403
404	if ((port = ddi_get_soft_state(fp_driver_softstate,
405	    ddi_get_instance(dip))) == NULL) {
406		return (DDI_FAILURE);
407	}
408
409	mutex_enter(&port->fp_mutex);
410
411	if (port->fp_ulp_attach) {
412		mutex_exit(&port->fp_mutex);
413		return (DDI_FAILURE);
414	}
415
416	switch (cmd) {
417	case DDI_DETACH:
418		if (port->fp_task != FP_TASK_IDLE) {
419			mutex_exit(&port->fp_mutex);
420			return (DDI_FAILURE);
421		}
422
423		/* Let's attempt to quit the job handler gracefully */
424		port->fp_soft_state |= FP_DETACH_INPROGRESS;
425
426		mutex_exit(&port->fp_mutex);
427		converse = FC_CMD_ATTACH;
428		if (fctl_detach_ulps(port, FC_CMD_DETACH,
429		    &modlinkage) != FC_SUCCESS) {
430			mutex_enter(&port->fp_mutex);
431			port->fp_soft_state &= ~FP_DETACH_INPROGRESS;
432			mutex_exit(&port->fp_mutex);
433			rval = DDI_FAILURE;
434			break;
435		}
436
437		mutex_enter(&port->fp_mutex);
438		for (cnt = 0; (port->fp_job_head) && (cnt < fp_cmd_wait_cnt);
439		    cnt++) {
440			mutex_exit(&port->fp_mutex);
441			delay(drv_usectohz(1000000));
442			mutex_enter(&port->fp_mutex);
443		}
444
445		if (port->fp_job_head) {
446			mutex_exit(&port->fp_mutex);
447			rval = DDI_FAILURE;
448			break;
449		}
450		mutex_exit(&port->fp_mutex);
451
452		rval = fp_detach_handler(port);
453		break;
454
455	case DDI_SUSPEND:
456		mutex_exit(&port->fp_mutex);
457		converse = FC_CMD_RESUME;
458		if (fctl_detach_ulps(port, FC_CMD_SUSPEND,
459		    &modlinkage) != FC_SUCCESS) {
460			rval = DDI_FAILURE;
461			break;
462		}
463		if ((rval = fp_suspend_handler(port)) != DDI_SUCCESS) {
464			(void) callb_generic_cpr(&port->fp_cpr_info,
465			    CB_CODE_CPR_RESUME);
466		}
467		break;
468
469	default:
470		mutex_exit(&port->fp_mutex);
471		break;
472	}
473
474	/*
475	 * Use softint to perform reattach.  Mark fp_ulp_attach so we
476	 * don't attempt to do this repeatedly on behalf of some persistent
477	 * caller.
478	 */
479	if (rval != DDI_SUCCESS) {
480		mutex_enter(&port->fp_mutex);
481		port->fp_ulp_attach = 1;
482
483		/*
484		 * If the port is in the low power mode then there is
485		 * possibility that fca too could be in low power mode.
486		 * Try to raise the power before calling attach ulps.
487		 */
488
489		if ((port->fp_soft_state & FP_SOFT_POWER_DOWN) &&
490		    (!(port->fp_soft_state & FP_SOFT_NO_PMCOMP))) {
491			mutex_exit(&port->fp_mutex);
492			(void) pm_raise_power(port->fp_port_dip,
493			    FP_PM_COMPONENT, FP_PM_PORT_UP);
494		} else {
495			mutex_exit(&port->fp_mutex);
496		}
497
498
499		fp_attach_ulps(port, converse);
500
501		mutex_enter(&port->fp_mutex);
502		while (port->fp_ulp_attach) {
503			cv_wait(&port->fp_attach_cv, &port->fp_mutex);
504		}
505
506		port->fp_soft_state &= ~FP_DETACH_INPROGRESS;
507
508		/*
509		 * Mark state as detach failed so asynchronous ULP attach
510		 * events (downstream, not the ones we're initiating with
511		 * the call to fp_attach_ulps) are not honored.  We're
512		 * really still in pending detach.
513		 */
514		port->fp_soft_state |= FP_DETACH_FAILED;
515
516		mutex_exit(&port->fp_mutex);
517	}
518
519	return (rval);
520}
521
522
523/*
524 * fp_getinfo:
525 *   Given the device number, return either the
526 *   dev_info_t pointer or the instance number.
527 */
528
529/* ARGSUSED */
530static int
531fp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
532{
533	int 		rval;
534	minor_t		instance;
535	fc_local_port_t *port;
536
537	rval = DDI_SUCCESS;
538	instance = getminor((dev_t)arg);
539
540	switch (cmd) {
541	case DDI_INFO_DEVT2DEVINFO:
542		if ((port = ddi_get_soft_state(fp_driver_softstate,
543		    instance)) == NULL) {
544			rval = DDI_FAILURE;
545			break;
546		}
547		*result = (void *)port->fp_port_dip;
548		break;
549
550	case DDI_INFO_DEVT2INSTANCE:
551		*result = (void *)(uintptr_t)instance;
552		break;
553
554	default:
555		rval = DDI_FAILURE;
556		break;
557	}
558
559	return (rval);
560}
561
562
563/*
564 * Entry point for power up and power down request from kernel
565 */
566static int
567fp_power(dev_info_t *dip, int comp, int level)
568{
569	int		rval = DDI_FAILURE;
570	fc_local_port_t	*port;
571
572	port = ddi_get_soft_state(fp_driver_softstate, ddi_get_instance(dip));
573	if (port == NULL || comp != FP_PM_COMPONENT) {
574		return (rval);
575	}
576
577	switch (level) {
578	case FP_PM_PORT_UP:
579		rval = DDI_SUCCESS;
580
581		/*
582		 * If the port is DDI_SUSPENDed, let the DDI_RESUME
583		 * code complete the rediscovery.
584		 */
585		mutex_enter(&port->fp_mutex);
586		if (port->fp_soft_state & FP_SOFT_SUSPEND) {
587			port->fp_soft_state &= ~FP_SOFT_POWER_DOWN;
588			port->fp_pm_level = FP_PM_PORT_UP;
589			mutex_exit(&port->fp_mutex);
590			fctl_attach_ulps(port, FC_CMD_POWER_UP, &modlinkage);
591			break;
592		}
593
594		if (port->fp_soft_state & FP_SOFT_POWER_DOWN) {
595			ASSERT(port->fp_pm_level == FP_PM_PORT_DOWN);
596
597			port->fp_pm_level = FP_PM_PORT_UP;
598			rval = fp_power_up(port);
599			if (rval != DDI_SUCCESS) {
600				port->fp_pm_level = FP_PM_PORT_DOWN;
601			}
602		} else {
603			port->fp_pm_level = FP_PM_PORT_UP;
604		}
605		mutex_exit(&port->fp_mutex);
606		break;
607
608	case FP_PM_PORT_DOWN:
609		mutex_enter(&port->fp_mutex);
610
611		ASSERT(!(port->fp_soft_state & FP_SOFT_NO_PMCOMP));
612		if (port->fp_soft_state & FP_SOFT_NO_PMCOMP) {
613			/*
614			 * PM framework goofed up. We have don't
615			 * have any PM components. Let's never go down.
616			 */
617			mutex_exit(&port->fp_mutex);
618			break;
619
620		}
621
622		if (port->fp_ulp_attach) {
623			/* We shouldn't let the power go down */
624			mutex_exit(&port->fp_mutex);
625			break;
626		}
627
628		/*
629		 * Not a whole lot to do if we are detaching
630		 */
631		if (port->fp_soft_state & FP_SOFT_IN_DETACH) {
632			port->fp_pm_level = FP_PM_PORT_DOWN;
633			mutex_exit(&port->fp_mutex);
634			rval = DDI_SUCCESS;
635			break;
636		}
637
638		if (!port->fp_pm_busy && !port->fp_pm_busy_nocomp) {
639			port->fp_pm_level = FP_PM_PORT_DOWN;
640
641			rval = fp_power_down(port);
642			if (rval != DDI_SUCCESS) {
643				port->fp_pm_level = FP_PM_PORT_UP;
644				ASSERT(!(port->fp_soft_state &
645				    FP_SOFT_POWER_DOWN));
646			} else {
647				ASSERT(port->fp_soft_state &
648				    FP_SOFT_POWER_DOWN);
649			}
650		}
651		mutex_exit(&port->fp_mutex);
652		break;
653
654	default:
655		break;
656	}
657
658	return (rval);
659}
660
661
662/*
663 * Open FC port devctl node
664 */
665static int
666fp_open(dev_t *devp, int flag, int otype, cred_t *credp)
667{
668	int 		instance;
669	fc_local_port_t *port;
670
671	if (otype != OTYP_CHR) {
672		return (EINVAL);
673	}
674
675	/*
676	 * This is not a toy to play with. Allow only powerful
677	 * users (hopefully knowledgeable) to access the port
678	 * (A hacker potentially could download a sick binary
679	 * file into FCA)
680	 */
681	if (drv_priv(credp)) {
682		return (EPERM);
683	}
684
685	instance = (int)getminor(*devp);
686
687	port = ddi_get_soft_state(fp_driver_softstate, instance);
688	if (port == NULL) {
689		return (ENXIO);
690	}
691
692	mutex_enter(&port->fp_mutex);
693	if (port->fp_flag & FP_EXCL) {
694		/*
695		 * It is already open for exclusive access.
696		 * So shut the door on this caller.
697		 */
698		mutex_exit(&port->fp_mutex);
699		return (EBUSY);
700	}
701
702	if (flag & FEXCL) {
703		if (port->fp_flag & FP_OPEN) {
704			/*
705			 * Exclusive operation not possible
706			 * as it is already opened
707			 */
708			mutex_exit(&port->fp_mutex);
709			return (EBUSY);
710		}
711		port->fp_flag |= FP_EXCL;
712	}
713	port->fp_flag |= FP_OPEN;
714	mutex_exit(&port->fp_mutex);
715
716	return (0);
717}
718
719
720/*
721 * The driver close entry point is called on the last close()
722 * of a device. So it is perfectly alright to just clobber the
723 * open flag and reset it to idle (instead of having to reset
724 * each flag bits). For any confusion, check out close(9E).
725 */
726
727/* ARGSUSED */
728static int
729fp_close(dev_t dev, int flag, int otype, cred_t *credp)
730{
731	int 		instance;
732	fc_local_port_t *port;
733
734	if (otype != OTYP_CHR) {
735		return (EINVAL);
736	}
737
738	instance = (int)getminor(dev);
739
740	port = ddi_get_soft_state(fp_driver_softstate, instance);
741	if (port == NULL) {
742		return (ENXIO);
743	}
744
745	mutex_enter(&port->fp_mutex);
746	if ((port->fp_flag & FP_OPEN) == 0) {
747		mutex_exit(&port->fp_mutex);
748		return (ENODEV);
749	}
750	port->fp_flag = FP_IDLE;
751	mutex_exit(&port->fp_mutex);
752
753	return (0);
754}
755
756/*
757 * Handle IOCTL requests
758 */
759
760/* ARGSUSED */
761static int
762fp_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval)
763{
764	int 		instance;
765	int		ret = 0;
766	fcio_t		fcio;
767	fc_local_port_t *port;
768
769	instance = (int)getminor(dev);
770
771	port = ddi_get_soft_state(fp_driver_softstate, instance);
772	if (port == NULL) {
773		return (ENXIO);
774	}
775
776	mutex_enter(&port->fp_mutex);
777	if ((port->fp_flag & FP_OPEN) == 0) {
778		mutex_exit(&port->fp_mutex);
779		return (ENXIO);
780	}
781
782	if (port->fp_soft_state & FP_SOFT_SUSPEND) {
783		mutex_exit(&port->fp_mutex);
784		return (ENXIO);
785	}
786
787	mutex_exit(&port->fp_mutex);
788
789	/* this will raise power if necessary */
790	ret = fctl_busy_port(port);
791	if (ret != 0) {
792		return (ret);
793	}
794
795	ASSERT(port->fp_pm_level == FP_PM_PORT_UP);
796
797
798	switch (cmd) {
799	case FCIO_CMD: {
800#ifdef	_MULTI_DATAMODEL
801		switch (ddi_model_convert_from(mode & FMODELS)) {
802		case DDI_MODEL_ILP32: {
803			struct fcio32 fcio32;
804
805			if (ddi_copyin((void *)data, (void *)&fcio32,
806			    sizeof (struct fcio32), mode)) {
807				ret = EFAULT;
808				break;
809			}
810			fcio.fcio_xfer = fcio32.fcio_xfer;
811			fcio.fcio_cmd = fcio32.fcio_cmd;
812			fcio.fcio_flags = fcio32.fcio_flags;
813			fcio.fcio_cmd_flags = fcio32.fcio_cmd_flags;
814			fcio.fcio_ilen = (size_t)fcio32.fcio_ilen;
815			fcio.fcio_ibuf =
816				(caddr_t)(uintptr_t)fcio32.fcio_ibuf;
817			fcio.fcio_olen = (size_t)fcio32.fcio_olen;
818			fcio.fcio_obuf =
819				(caddr_t)(uintptr_t)fcio32.fcio_obuf;
820			fcio.fcio_alen = (size_t)fcio32.fcio_alen;
821			fcio.fcio_abuf =
822				(caddr_t)(uintptr_t)fcio32.fcio_abuf;
823			fcio.fcio_errno = fcio32.fcio_errno;
824			break;
825		}
826
827		case DDI_MODEL_NONE:
828			if (ddi_copyin((void *)data, (void *)&fcio,
829			    sizeof (fcio_t), mode)) {
830				ret = EFAULT;
831			}
832			break;
833		}
834#else	/* _MULTI_DATAMODEL */
835		if (ddi_copyin((void *)data, (void *)&fcio,
836		sizeof (fcio_t), mode)) {
837			ret = EFAULT;
838			break;
839		}
840#endif	/* _MULTI_DATAMODEL */
841		if (!ret) {
842			ret = fp_fciocmd(port, data, mode, &fcio);
843		}
844		break;
845	}
846
847	default:
848		ret = fctl_ulp_port_ioctl(port, dev, cmd, data,
849		    mode, credp, rval);
850	}
851
852	fctl_idle_port(port);
853
854	return (ret);
855}
856
857
858/*
859 * Init Symbolic Port Name and Node Name
860 * LV will try to get symbolic names from FCA driver
861 * and register these to name server,
862 * if LV fails to get these,
863 * LV will register its default symbolic names to name server.
864 * The Default symbolic node name format is :
865 * 	<hostname>:<hba driver name>(instance)
866 * The Default symbolic port name format is :
867 * 	<fp path name>
868 */
869static void
870fp_init_symbolic_names(fc_local_port_t *port)
871{
872	const char *vendorname = ddi_driver_name(port->fp_fca_dip);
873	char *sym_name;
874	char fcaname[50] = {0};
875	int hostnlen, fcanlen;
876
877	if (port->fp_sym_node_namelen == 0) {
878		hostnlen = strlen(utsname.nodename);
879		(void) snprintf(fcaname, sizeof (fcaname),
880		    "%s%d", vendorname, ddi_get_instance(port->fp_fca_dip));
881		fcanlen = strlen(fcaname);
882
883		sym_name = kmem_zalloc(hostnlen + fcanlen + 2, KM_SLEEP);
884		(void) sprintf(sym_name, "%s:%s", utsname.nodename, fcaname);
885		port->fp_sym_node_namelen = strlen(sym_name);
886		if (port->fp_sym_node_namelen >= FCHBA_SYMB_NAME_LEN) {
887			port->fp_sym_node_namelen = FCHBA_SYMB_NAME_LEN;
888		}
889		(void) strncpy(port->fp_sym_node_name, sym_name,
890		    port->fp_sym_node_namelen);
891		kmem_free(sym_name, hostnlen + fcanlen + 2);
892	}
893
894	if (port->fp_sym_port_namelen == 0) {
895		char *pathname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
896
897		(void) ddi_pathname(port->fp_port_dip, pathname);
898		port->fp_sym_port_namelen = strlen(pathname);
899		if (port->fp_sym_port_namelen >= FCHBA_SYMB_NAME_LEN) {
900			port->fp_sym_port_namelen = FCHBA_SYMB_NAME_LEN;
901		}
902		(void) strncpy(port->fp_sym_port_name, pathname,
903		    port->fp_sym_port_namelen);
904		kmem_free(pathname, MAXPATHLEN);
905	}
906}
907
908
909/*
910 * Perform port attach
911 */
912static int
913fp_attach_handler(dev_info_t *dip)
914{
915	int			rval;
916	int			instance;
917	int 			port_num;
918	int			port_len;
919	char 			name[30];
920	char			i_pwwn[17];
921	fp_cmd_t		*pkt;
922	uint32_t		ub_count;
923	fc_local_port_t 	*port;
924	job_request_t 		*job;
925	fc_local_port_t *phyport = NULL;
926	int portpro1;
927	char pwwn[17], nwwn[17];
928
929	instance = ddi_get_instance(dip);
930
931	port_len = sizeof (port_num);
932
933	rval = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
934	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "port",
935	    (caddr_t)&port_num, &port_len);
936
937	if (rval != DDI_SUCCESS) {
938		cmn_err(CE_WARN, "fp(%d): No port property in devinfo",
939		    instance);
940		return (DDI_FAILURE);
941	}
942
943	if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
944	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
945		cmn_err(CE_WARN, "fp(%d): failed to create devctl minor node",
946		    instance);
947		return (DDI_FAILURE);
948	}
949
950	if (ddi_create_minor_node(dip, "fc", S_IFCHR, instance,
951	    DDI_NT_FC_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
952		cmn_err(CE_WARN, "fp(%d): failed to create fc attachment"
953		    " point minor node", instance);
954		ddi_remove_minor_node(dip, NULL);
955		return (DDI_FAILURE);
956	}
957
958	if (ddi_soft_state_zalloc(fp_driver_softstate, instance)
959	    != DDI_SUCCESS) {
960		cmn_err(CE_WARN, "fp(%d): failed to alloc soft state",
961		    instance);
962		ddi_remove_minor_node(dip, NULL);
963		return (DDI_FAILURE);
964	}
965	port = ddi_get_soft_state(fp_driver_softstate, instance);
966
967	(void) sprintf(port->fp_ibuf, "fp(%d)", instance);
968
969	port->fp_instance = instance;
970	port->fp_ulp_attach = 1;
971	port->fp_port_num = port_num;
972	port->fp_verbose = fp_verbosity;
973	port->fp_options = fp_options;
974
975	port->fp_fca_dip = ddi_get_parent(dip);
976	port->fp_port_dip = dip;
977	port->fp_fca_tran = (fc_fca_tran_t *)
978	    ddi_get_driver_private(port->fp_fca_dip);
979
980	port->fp_task = port->fp_last_task = FP_TASK_IDLE;
981
982	/*
983	 * Init the starting value of fp_rscn_count. Note that if
984	 * FC_INVALID_RSCN_COUNT is 0 (which is what it currently is), the
985	 * actual # of RSCNs will be (fp_rscn_count - 1)
986	 */
987	port->fp_rscn_count = FC_INVALID_RSCN_COUNT + 1;
988
989	mutex_init(&port->fp_mutex, NULL, MUTEX_DRIVER, NULL);
990	cv_init(&port->fp_cv, NULL, CV_DRIVER, NULL);
991	cv_init(&port->fp_attach_cv, NULL, CV_DRIVER, NULL);
992
993	(void) sprintf(name, "fp%d_cache", instance);
994
995	if ((portpro1 = ddi_prop_get_int(DDI_DEV_T_ANY,
996	    dip, DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
997	    "phyport-instance", -1)) != -1) {
998		phyport = ddi_get_soft_state(fp_driver_softstate, portpro1);
999		fc_wwn_to_str(&phyport->fp_service_params.nport_ww_name, pwwn);
1000		fc_wwn_to_str(&phyport->fp_service_params.node_ww_name, nwwn);
1001		port->fp_npiv_type = FC_NPIV_PORT;
1002	}
1003
1004	/*
1005	 * Allocate the pool of fc_packet_t structs to be used with
1006	 * this fp instance.
1007	 */
1008	port->fp_pkt_cache = kmem_cache_create(name,
1009	    (port->fp_fca_tran->fca_pkt_size) + sizeof (fp_cmd_t), 8,
1010	    fp_cache_constructor, fp_cache_destructor, NULL, (void *)port,
1011	    NULL, 0);
1012
1013	if (port->fp_pkt_cache == NULL) {
1014		goto cache_alloc_failed;
1015	}
1016
1017
1018	/*
1019	 * Allocate the d_id and pwwn hash tables for all remote ports
1020	 * connected to this local port.
1021	 */
1022	port->fp_did_table = kmem_zalloc(did_table_size *
1023	    sizeof (struct d_id_hash), KM_SLEEP);
1024
1025	port->fp_pwwn_table = kmem_zalloc(pwwn_table_size *
1026	    sizeof (struct pwwn_hash), KM_SLEEP);
1027
1028	port->fp_taskq = taskq_create("fp_ulp_callback", 1,
1029	    MINCLSYSPRI, 1, 16, 0);
1030
1031	/* Indicate that don't have the pm components yet */
1032	port->fp_soft_state |=  FP_SOFT_NO_PMCOMP;
1033
1034	/*
1035	 * Bind the callbacks with the FCA driver. This will open the gate
1036	 * for asynchronous callbacks, so after this call the fp_mutex
1037	 * must be held when updating the fc_local_port_t struct.
1038	 *
1039	 * This is done _before_ setting up the job thread so we can avoid
1040	 * cleaning up after the thread_create() in the error path. This
1041	 * also means fp will be operating with fp_els_resp_pkt set to NULL.
1042	 */
1043	if (fp_bind_callbacks(port) != DDI_SUCCESS) {
1044		goto bind_callbacks_failed;
1045	}
1046
1047	if (phyport) {
1048		mutex_enter(&phyport->fp_mutex);
1049		if (phyport->fp_port_next) {
1050			phyport->fp_port_next->fp_port_prev = port;
1051			port->fp_port_next =  phyport->fp_port_next;
1052			phyport->fp_port_next = port;
1053			port->fp_port_prev = phyport;
1054		} else {
1055			phyport->fp_port_next = port;
1056			phyport->fp_port_prev = port;
1057			port->fp_port_next =  phyport;
1058			port->fp_port_prev = phyport;
1059		}
1060		mutex_exit(&phyport->fp_mutex);
1061	}
1062
1063	/*
1064	 * Init Symbolic Names
1065	 */
1066	fp_init_symbolic_names(port);
1067
1068	pkt = fp_alloc_pkt(port, sizeof (la_els_logi_t), sizeof (la_els_logi_t),
1069	    KM_SLEEP, NULL);
1070
1071	if (pkt == NULL) {
1072		cmn_err(CE_WARN, "fp(%d): failed to allocate ELS packet",
1073		    instance);
1074		goto alloc_els_packet_failed;
1075	}
1076
1077	(void) thread_create(NULL, 0, fp_job_handler, port, 0, &p0, TS_RUN,
1078	    v.v_maxsyspri - 2);
1079
1080	fc_wwn_to_str(&port->fp_service_params.nport_ww_name, i_pwwn);
1081	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "initiator-port",
1082	    i_pwwn) != DDI_PROP_SUCCESS) {
1083		fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
1084		    "fp(%d): Updating 'initiator-port' property"
1085		    " on fp dev_info node failed", instance);
1086	}
1087
1088	fc_wwn_to_str(&port->fp_service_params.node_ww_name, i_pwwn);
1089	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "initiator-node",
1090	    i_pwwn) != DDI_PROP_SUCCESS) {
1091		fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
1092		    "fp(%d): Updating 'initiator-node' property"
1093		    " on fp dev_info node failed", instance);
1094	}
1095
1096	mutex_enter(&port->fp_mutex);
1097	port->fp_els_resp_pkt = pkt;
1098	mutex_exit(&port->fp_mutex);
1099
1100	/*
1101	 * Determine the count of unsolicited buffers this FCA can support
1102	 */
1103	fp_retrieve_caps(port);
1104
1105	/*
1106	 * Allocate unsolicited buffer tokens
1107	 */
1108	if (port->fp_ub_count) {
1109		ub_count = port->fp_ub_count;
1110		port->fp_ub_tokens = kmem_zalloc(ub_count *
1111		    sizeof (*port->fp_ub_tokens), KM_SLEEP);
1112		/*
1113		 * Do not fail the attach if unsolicited buffer allocation
1114		 * fails; Just try to get along with whatever the FCA can do.
1115		 */
1116		if (fc_ulp_uballoc(port, &ub_count, fp_unsol_buf_size,
1117		    FC_TYPE_EXTENDED_LS, port->fp_ub_tokens) !=
1118		    FC_SUCCESS || ub_count != port->fp_ub_count) {
1119			cmn_err(CE_WARN, "fp(%d): failed to allocate "
1120			    " Unsolicited buffers. proceeding with attach...",
1121			    instance);
1122			kmem_free(port->fp_ub_tokens,
1123			    sizeof (*port->fp_ub_tokens) * port->fp_ub_count);
1124			port->fp_ub_tokens = NULL;
1125		}
1126	}
1127
1128	fp_load_ulp_modules(dip, port);
1129
1130	/*
1131	 * Enable DDI_SUSPEND and DDI_RESUME for this instance.
1132	 */
1133	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
1134	    "pm-hardware-state", "needs-suspend-resume",
1135	    strlen("needs-suspend-resume") + 1);
1136
1137	/*
1138	 * fctl maintains a list of all port handles, so
1139	 * help fctl add this one to its list now.
1140	 */
1141	mutex_enter(&port->fp_mutex);
1142	fctl_add_port(port);
1143
1144	/*
1145	 * If a state change is already in progress, set the bind state t
1146	 * OFFLINE as well, so further state change callbacks into ULPs
1147	 * will pass the appropriate states
1148	 */
1149	if (FC_PORT_STATE_MASK(port->fp_bind_state) == FC_STATE_OFFLINE ||
1150	    port->fp_statec_busy) {
1151		port->fp_bind_state = FC_STATE_OFFLINE;
1152		mutex_exit(&port->fp_mutex);
1153
1154		fp_startup_done((opaque_t)port, FC_PKT_SUCCESS);
1155	} else {
1156		/*
1157		 * Without dropping the mutex, ensure that the port
1158		 * startup happens ahead of state change callback
1159		 * processing
1160		 */
1161		ASSERT(port->fp_job_tail == NULL && port->fp_job_head == NULL);
1162
1163		port->fp_last_task = port->fp_task;
1164		port->fp_task = FP_TASK_PORT_STARTUP;
1165
1166		job = fctl_alloc_job(JOB_PORT_STARTUP, JOB_TYPE_FCTL_ASYNC,
1167		    fp_startup_done, (opaque_t)port, KM_SLEEP);
1168
1169		port->fp_job_head = port->fp_job_tail = job;
1170
1171		cv_signal(&port->fp_cv);
1172
1173		mutex_exit(&port->fp_mutex);
1174	}
1175
1176	mutex_enter(&port->fp_mutex);
1177	while (port->fp_ulp_attach) {
1178		cv_wait(&port->fp_attach_cv, &port->fp_mutex);
1179	}
1180	mutex_exit(&port->fp_mutex);
1181
1182	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
1183	    "pm-components", fp_pm_comps,
1184	    sizeof (fp_pm_comps) / sizeof (fp_pm_comps[0])) !=
1185	    DDI_PROP_SUCCESS) {
1186		FP_TRACE(FP_NHEAD2(9, 0), "Failed to create PM"
1187		    " components property, PM disabled on this port.");
1188		mutex_enter(&port->fp_mutex);
1189		port->fp_pm_level = FP_PM_PORT_UP;
1190		mutex_exit(&port->fp_mutex);
1191	} else {
1192		if (pm_raise_power(dip, FP_PM_COMPONENT,
1193		    FP_PM_PORT_UP) != DDI_SUCCESS) {
1194			FP_TRACE(FP_NHEAD2(9, 0), "Failed to raise"
1195			    " power level");
1196			mutex_enter(&port->fp_mutex);
1197			port->fp_pm_level = FP_PM_PORT_UP;
1198			mutex_exit(&port->fp_mutex);
1199		}
1200
1201		/*
1202		 * Don't unset the FP_SOFT_NO_PMCOMP flag until after
1203		 * the call to pm_raise_power.  The PM framework can't
1204		 * handle multiple threads calling into it during attach.
1205		 */
1206
1207		mutex_enter(&port->fp_mutex);
1208		port->fp_soft_state &=  ~FP_SOFT_NO_PMCOMP;
1209		mutex_exit(&port->fp_mutex);
1210	}
1211
1212	ddi_report_dev(dip);
1213
1214	fp_log_port_event(port, ESC_SUNFC_PORT_ATTACH);
1215
1216	return (DDI_SUCCESS);
1217
1218	/*
1219	 * Unwind any/all preceeding allocations in the event of an error.
1220	 */
1221
1222alloc_els_packet_failed:
1223
1224	if (port->fp_fca_handle != NULL) {
1225		port->fp_fca_tran->fca_unbind_port(port->fp_fca_handle);
1226		port->fp_fca_handle = NULL;
1227	}
1228
1229	if (port->fp_ub_tokens != NULL) {
1230		(void) fc_ulp_ubfree(port, port->fp_ub_count,
1231		    port->fp_ub_tokens);
1232		kmem_free(port->fp_ub_tokens,
1233		    port->fp_ub_count * sizeof (*port->fp_ub_tokens));
1234		port->fp_ub_tokens = NULL;
1235	}
1236
1237	if (port->fp_els_resp_pkt != NULL) {
1238		fp_free_pkt(port->fp_els_resp_pkt);
1239		port->fp_els_resp_pkt = NULL;
1240	}
1241
1242bind_callbacks_failed:
1243
1244	if (port->fp_taskq != NULL) {
1245		taskq_destroy(port->fp_taskq);
1246	}
1247
1248	if (port->fp_pwwn_table != NULL) {
1249		kmem_free(port->fp_pwwn_table,
1250		    pwwn_table_size * sizeof (struct pwwn_hash));
1251		port->fp_pwwn_table = NULL;
1252	}
1253
1254	if (port->fp_did_table != NULL) {
1255		kmem_free(port->fp_did_table,
1256		    did_table_size * sizeof (struct d_id_hash));
1257		port->fp_did_table = NULL;
1258	}
1259
1260	if (port->fp_pkt_cache != NULL) {
1261		kmem_cache_destroy(port->fp_pkt_cache);
1262		port->fp_pkt_cache = NULL;
1263	}
1264
1265cache_alloc_failed:
1266
1267	cv_destroy(&port->fp_attach_cv);
1268	cv_destroy(&port->fp_cv);
1269	mutex_destroy(&port->fp_mutex);
1270	ddi_remove_minor_node(port->fp_port_dip, NULL);
1271	ddi_soft_state_free(fp_driver_softstate, instance);
1272	ddi_prop_remove_all(dip);
1273
1274	return (DDI_FAILURE);
1275}
1276
1277
1278/*
1279 * Handle DDI_RESUME request
1280 */
1281static int
1282fp_resume_handler(dev_info_t *dip)
1283{
1284	int		rval;
1285	fc_local_port_t *port;
1286
1287	port = ddi_get_soft_state(fp_driver_softstate, ddi_get_instance(dip));
1288
1289	ASSERT(port != NULL);
1290
1291#ifdef	DEBUG
1292	mutex_enter(&port->fp_mutex);
1293	ASSERT(port->fp_soft_state & FP_SOFT_SUSPEND);
1294	mutex_exit(&port->fp_mutex);
1295#endif
1296
1297	/*
1298	 * If the port was power suspended, raise the power level
1299	 */
1300	mutex_enter(&port->fp_mutex);
1301	if ((port->fp_soft_state & FP_SOFT_POWER_DOWN) &&
1302	    (!(port->fp_soft_state & FP_SOFT_NO_PMCOMP))) {
1303		ASSERT(port->fp_pm_level == FP_PM_PORT_DOWN);
1304
1305		mutex_exit(&port->fp_mutex);
1306		if (pm_raise_power(dip, FP_PM_COMPONENT,
1307		    FP_PM_PORT_UP) != DDI_SUCCESS) {
1308			FP_TRACE(FP_NHEAD2(9, 0),
1309			    "Failed to raise the power level");
1310			return (DDI_FAILURE);
1311		}
1312		mutex_enter(&port->fp_mutex);
1313	}
1314	port->fp_soft_state &= ~FP_SOFT_SUSPEND;
1315	mutex_exit(&port->fp_mutex);
1316
1317	/*
1318	 * All the discovery is initiated and handled by per-port thread.
1319	 * Further all the discovery is done in handled in callback mode
1320	 * (not polled mode); In a specific case such as this, the discovery
1321	 * is required to happen in polled mode. The easiest way out is
1322	 * to bail out port thread and get started. Come back and fix this
1323	 * to do on demand discovery initiated by ULPs. ULPs such as FCP
1324	 * will do on-demand discovery during pre-power-up busctl handling
1325	 * which will only be possible when SCSA provides a new HBA vector
1326	 * for sending down the PM busctl requests.
1327	 */
1328	(void) callb_generic_cpr(&port->fp_cpr_info, CB_CODE_CPR_RESUME);
1329
1330	rval = fp_resume_all(port, FC_CMD_RESUME);
1331	if (rval != DDI_SUCCESS) {
1332		mutex_enter(&port->fp_mutex);
1333		port->fp_soft_state |= FP_SOFT_SUSPEND;
1334		mutex_exit(&port->fp_mutex);
1335		(void) callb_generic_cpr(&port->fp_cpr_info,
1336		    CB_CODE_CPR_CHKPT);
1337	}
1338
1339	return (rval);
1340}
1341
1342/*
1343 * Perform FC Port power on initialization
1344 */
1345static int
1346fp_power_up(fc_local_port_t *port)
1347{
1348	int	rval;
1349
1350	ASSERT(MUTEX_HELD(&port->fp_mutex));
1351
1352	ASSERT((port->fp_soft_state & FP_SOFT_SUSPEND) == 0);
1353	ASSERT(port->fp_soft_state & FP_SOFT_POWER_DOWN);
1354
1355	port->fp_soft_state &= ~FP_SOFT_POWER_DOWN;
1356
1357	mutex_exit(&port->fp_mutex);
1358
1359	rval = fp_resume_all(port, FC_CMD_POWER_UP);
1360	if (rval != DDI_SUCCESS) {
1361		mutex_enter(&port->fp_mutex);
1362		port->fp_soft_state |= FP_SOFT_POWER_DOWN;
1363	} else {
1364		mutex_enter(&port->fp_mutex);
1365	}
1366
1367	return (rval);
1368}
1369
1370
1371/*
1372 * It is important to note that the power may possibly be removed between
1373 * SUSPEND and the ensuing RESUME operation. In such a context the underlying
1374 * FC port hardware would have gone through an OFFLINE to ONLINE transition
1375 * (hardware state). In this case, the port driver may need to rediscover the
1376 * topology, perform LOGINs, register with the name server again and perform
1377 * any such port initialization procedures. To perform LOGINs, the driver could
1378 * use the port device handle to see if a LOGIN needs to be performed and use
1379 * the D_ID and WWN in it. The LOGINs may fail (if the hardware is reconfigured
1380 * or removed) which will be reflected in the map the ULPs will see.
1381 */
1382static int
1383fp_resume_all(fc_local_port_t *port, fc_attach_cmd_t cmd)
1384{
1385
1386	ASSERT(!MUTEX_HELD(&port->fp_mutex));
1387
1388	if (fp_bind_callbacks(port) != DDI_SUCCESS) {
1389		return (DDI_FAILURE);
1390	}
1391
1392	mutex_enter(&port->fp_mutex);
1393
1394	/*
1395	 * If there are commands queued for delayed retry, instead of
1396	 * working the hard way to figure out which ones are good for
1397	 * restart and which ones not (ELSs are definitely not good
1398	 * as the port will have to go through a new spin of rediscovery
1399	 * now), so just flush them out.
1400	 */
1401	if (port->fp_restore & FP_RESTORE_WAIT_TIMEOUT) {
1402		fp_cmd_t	*cmd;
1403
1404		port->fp_restore &= ~FP_RESTORE_WAIT_TIMEOUT;
1405
1406		mutex_exit(&port->fp_mutex);
1407		while ((cmd = fp_deque_cmd(port)) != NULL) {
1408			cmd->cmd_pkt.pkt_state = FC_PKT_TRAN_ERROR;
1409			fp_iodone(cmd);
1410		}
1411		mutex_enter(&port->fp_mutex);
1412	}
1413
1414	if (FC_PORT_STATE_MASK(port->fp_bind_state) == FC_STATE_OFFLINE) {
1415		if ((port->fp_restore & FP_RESTORE_OFFLINE_TIMEOUT) ||
1416		    port->fp_dev_count) {
1417			port->fp_restore &= ~FP_RESTORE_OFFLINE_TIMEOUT;
1418			port->fp_offline_tid = timeout(fp_offline_timeout,
1419			    (caddr_t)port, fp_offline_ticks);
1420		}
1421		if (port->fp_job_head) {
1422			cv_signal(&port->fp_cv);
1423		}
1424		mutex_exit(&port->fp_mutex);
1425		fctl_attach_ulps(port, cmd, &modlinkage);
1426	} else {
1427		struct job_request *job;
1428
1429		/*
1430		 * If an OFFLINE timer was running at the time of
1431		 * suspending, there is no need to restart it as
1432		 * the port is ONLINE now.
1433		 */
1434		port->fp_restore &= ~FP_RESTORE_OFFLINE_TIMEOUT;
1435		if (port->fp_statec_busy == 0) {
1436			port->fp_soft_state |= FP_SOFT_IN_STATEC_CB;
1437		}
1438		port->fp_statec_busy++;
1439		mutex_exit(&port->fp_mutex);
1440
1441		job = fctl_alloc_job(JOB_PORT_ONLINE,
1442		    JOB_CANCEL_ULP_NOTIFICATION, NULL, NULL, KM_SLEEP);
1443		fctl_enque_job(port, job);
1444
1445		fctl_jobwait(job);
1446		fctl_remove_oldies(port);
1447
1448		fctl_attach_ulps(port, cmd, &modlinkage);
1449		fctl_dealloc_job(job);
1450	}
1451
1452	return (DDI_SUCCESS);
1453}
1454
1455
1456/*
1457 * At this time, there shouldn't be any I/O requests on this port.
1458 * But the unsolicited callbacks from the underlying FCA port need
1459 * to be handled very carefully. The steps followed to handle the
1460 * DDI_DETACH are:
1461 *	+ 	Grab the port driver mutex, check if the unsolicited
1462 *		callback is currently under processing. If true, fail
1463 *		the DDI_DETACH request by printing a message; If false
1464 *		mark the DDI_DETACH as under progress, so that any
1465 *		further unsolicited callbacks get bounced.
1466 *	+	Perform PRLO/LOGO if necessary, cleanup all the data
1467 *		structures.
1468 *	+	Get the job_handler thread to gracefully exit.
1469 *	+	Unregister callbacks with the FCA port.
1470 *	+	Now that some peace is found, notify all the ULPs of
1471 *		DDI_DETACH request (using ulp_port_detach entry point)
1472 *	+	Free all mutexes, semaphores, conditional variables.
1473 *	+	Free the soft state, return success.
1474 *
1475 * Important considerations:
1476 *		Port driver de-registers state change and unsolicited
1477 *		callbacks before taking up the task of notifying ULPs
1478 *		and performing PRLO and LOGOs.
1479 *
1480 *		A port may go offline at the time PRLO/LOGO is being
1481 *		requested. It is expected of all FCA drivers to fail
1482 *		such requests either immediately with a FC_OFFLINE
1483 *		return code to fc_fca_transport() or return the packet
1484 *		asynchronously with pkt state set to FC_PKT_PORT_OFFLINE
1485 */
1486static int
1487fp_detach_handler(fc_local_port_t *port)
1488{
1489	job_request_t 	*job;
1490	uint32_t	delay_count;
1491	fc_orphan_t	*orp, *tmporp;
1492
1493	/*
1494	 * In a Fabric topology with many host ports connected to
1495	 * a switch, another detaching instance of fp might have
1496	 * triggered a LOGO (which is an unsolicited request to
1497	 * this instance). So in order to be able to successfully
1498	 * detach by taking care of such cases a delay of about
1499	 * 30 seconds is introduced.
1500	 */
1501	delay_count = 0;
1502	mutex_enter(&port->fp_mutex);
1503	while ((port->fp_soft_state &
1504	    (FP_SOFT_IN_STATEC_CB | FP_SOFT_IN_UNSOL_CB)) &&
1505	    (delay_count < 30)) {
1506		mutex_exit(&port->fp_mutex);
1507		delay_count++;
1508		delay(drv_usectohz(1000000));
1509		mutex_enter(&port->fp_mutex);
1510	}
1511
1512	if (port->fp_soft_state &
1513	    (FP_SOFT_IN_STATEC_CB | FP_SOFT_IN_UNSOL_CB)) {
1514		port->fp_soft_state &= ~FP_DETACH_INPROGRESS;
1515		mutex_exit(&port->fp_mutex);
1516		cmn_err(CE_WARN, "fp(%d): FCA callback in progress: "
1517		    " Failing detach", port->fp_instance);
1518		return (DDI_FAILURE);
1519	}
1520
1521	port->fp_soft_state |= FP_SOFT_IN_DETACH;
1522	port->fp_soft_state &= ~FP_DETACH_INPROGRESS;
1523	mutex_exit(&port->fp_mutex);
1524
1525	/*
1526	 * If we're powered down, we need to raise power prior to submitting
1527	 * the JOB_PORT_SHUTDOWN job.  Otherwise, the job handler will never
1528	 * process the shutdown job.
1529	 */
1530	if (fctl_busy_port(port) != 0) {
1531		cmn_err(CE_WARN, "fp(%d): fctl_busy_port failed",
1532		    port->fp_instance);
1533		mutex_enter(&port->fp_mutex);
1534		port->fp_soft_state &= ~FP_SOFT_IN_DETACH;
1535		mutex_exit(&port->fp_mutex);
1536		return (DDI_FAILURE);
1537	}
1538
1539	/*
1540	 * This will deallocate data structs and cause the "job" thread
1541	 * to exit, in preparation for DDI_DETACH on the instance.
1542	 * This can sleep for an arbitrary duration, since it waits for
1543	 * commands over the wire, timeout(9F) callbacks, etc.
1544	 *
1545	 * CAUTION: There is still a race here, where the "job" thread
1546	 * can still be executing code even tho the fctl_jobwait() call
1547	 * below has returned to us.  In theory the fp driver could even be
1548	 * modunloaded even tho the job thread isn't done executing.
1549	 * without creating the race condition.
1550	 */
1551	job = fctl_alloc_job(JOB_PORT_SHUTDOWN, 0, NULL,
1552	    (opaque_t)port, KM_SLEEP);
1553	fctl_enque_job(port, job);
1554	fctl_jobwait(job);
1555	fctl_dealloc_job(job);
1556
1557
1558	(void) pm_lower_power(port->fp_port_dip, FP_PM_COMPONENT,
1559	    FP_PM_PORT_DOWN);
1560
1561	if (port->fp_taskq) {
1562		taskq_destroy(port->fp_taskq);
1563	}
1564
1565	ddi_prop_remove_all(port->fp_port_dip);
1566
1567	ddi_remove_minor_node(port->fp_port_dip, NULL);
1568
1569	fctl_remove_port(port);
1570
1571	fp_free_pkt(port->fp_els_resp_pkt);
1572
1573	if (port->fp_ub_tokens) {
1574		if (fc_ulp_ubfree(port, port->fp_ub_count,
1575		    port->fp_ub_tokens) != FC_SUCCESS) {
1576			cmn_err(CE_WARN, "fp(%d): couldn't free "
1577			    " unsolicited buffers", port->fp_instance);
1578		}
1579		kmem_free(port->fp_ub_tokens,
1580		    sizeof (*port->fp_ub_tokens) * port->fp_ub_count);
1581		port->fp_ub_tokens = NULL;
1582	}
1583
1584	if (port->fp_pkt_cache != NULL) {
1585		kmem_cache_destroy(port->fp_pkt_cache);
1586	}
1587
1588	port->fp_fca_tran->fca_unbind_port(port->fp_fca_handle);
1589
1590	mutex_enter(&port->fp_mutex);
1591	if (port->fp_did_table) {
1592		kmem_free(port->fp_did_table, did_table_size *
1593			sizeof (struct d_id_hash));
1594	}
1595
1596	if (port->fp_pwwn_table) {
1597		kmem_free(port->fp_pwwn_table, pwwn_table_size *
1598		    sizeof (struct pwwn_hash));
1599	}
1600	orp = port->fp_orphan_list;
1601	while (orp) {
1602		tmporp = orp;
1603		orp = orp->orp_next;
1604		kmem_free(tmporp, sizeof (*orp));
1605	}
1606
1607	mutex_exit(&port->fp_mutex);
1608
1609	fp_log_port_event(port, ESC_SUNFC_PORT_DETACH);
1610
1611	mutex_destroy(&port->fp_mutex);
1612	cv_destroy(&port->fp_attach_cv);
1613	cv_destroy(&port->fp_cv);
1614	ddi_soft_state_free(fp_driver_softstate, port->fp_instance);
1615
1616	return (DDI_SUCCESS);
1617}
1618
1619
1620/*
1621 * Steps to perform DDI_SUSPEND operation on a FC port
1622 *
1623 *	- If already suspended return DDI_FAILURE
1624 *	- If already power-suspended return DDI_SUCCESS
1625 *	- If an unsolicited callback or state change handling is in
1626 *	    in progress, throw a warning message, return DDI_FAILURE
1627 *	- Cancel timeouts
1628 *	- SUSPEND the job_handler thread (means do nothing as it is
1629 *          taken care of by the CPR frame work)
1630 */
1631static int
1632fp_suspend_handler(fc_local_port_t *port)
1633{
1634	uint32_t	delay_count;
1635
1636	mutex_enter(&port->fp_mutex);
1637
1638	/*
1639	 * The following should never happen, but
1640	 * let the driver be more defensive here
1641	 */
1642	if (port->fp_soft_state & FP_SOFT_SUSPEND) {
1643		mutex_exit(&port->fp_mutex);
1644		return (DDI_FAILURE);
1645	}
1646
1647	/*
1648	 * If the port is already power suspended, there
1649	 * is nothing else to do, So return DDI_SUCCESS,
1650	 * but mark the SUSPEND bit in the soft state
1651	 * before leaving.
1652	 */
1653	if (port->fp_soft_state & FP_SOFT_POWER_DOWN) {
1654		port->fp_soft_state |= FP_SOFT_SUSPEND;
1655		mutex_exit(&port->fp_mutex);
1656		return (DDI_SUCCESS);
1657	}
1658
1659	/*
1660	 * Check if an unsolicited callback or state change handling is
1661	 * in progress. If true, fail the suspend operation; also throw
1662	 * a warning message notifying the failure. Note that Sun PCI
1663	 * hotplug spec recommends messages in cases of failure (but
1664	 * not flooding the console)
1665	 *
1666	 * Busy waiting for a short interval (500 millisecond ?) to see
1667	 * if the callback processing completes may be another idea. Since
1668	 * most of the callback processing involves a lot of work, it
1669	 * is safe to just fail the SUSPEND operation. It is definitely
1670	 * not bad to fail the SUSPEND operation if the driver is busy.
1671	 */
1672	delay_count = 0;
1673	while ((port->fp_soft_state & (FP_SOFT_IN_STATEC_CB |
1674	    FP_SOFT_IN_UNSOL_CB)) && (delay_count < 30)) {
1675		mutex_exit(&port->fp_mutex);
1676		delay_count++;
1677		delay(drv_usectohz(1000000));
1678		mutex_enter(&port->fp_mutex);
1679	}
1680
1681	if (port->fp_soft_state & (FP_SOFT_IN_STATEC_CB |
1682	    FP_SOFT_IN_UNSOL_CB)) {
1683		mutex_exit(&port->fp_mutex);
1684		cmn_err(CE_WARN, "fp(%d): FCA callback in progress: "
1685		    " Failing suspend", port->fp_instance);
1686		return (DDI_FAILURE);
1687	}
1688
1689	/*
1690	 * Check of FC port thread is busy
1691	 */
1692	if (port->fp_job_head) {
1693		mutex_exit(&port->fp_mutex);
1694		FP_TRACE(FP_NHEAD2(9, 0),
1695		    "FC port thread is busy: Failing suspend");
1696		return (DDI_FAILURE);
1697	}
1698	port->fp_soft_state |= FP_SOFT_SUSPEND;
1699
1700	fp_suspend_all(port);
1701	mutex_exit(&port->fp_mutex);
1702
1703	return (DDI_SUCCESS);
1704}
1705
1706
1707/*
1708 * Prepare for graceful power down of a FC port
1709 */
1710static int
1711fp_power_down(fc_local_port_t *port)
1712{
1713	ASSERT(MUTEX_HELD(&port->fp_mutex));
1714
1715	/*
1716	 * Power down request followed by a DDI_SUSPEND should
1717	 * never happen; If it does return DDI_SUCCESS
1718	 */
1719	if (port->fp_soft_state & FP_SOFT_SUSPEND) {
1720		port->fp_soft_state |= FP_SOFT_POWER_DOWN;
1721		return (DDI_SUCCESS);
1722	}
1723
1724	/*
1725	 * If the port is already power suspended, there
1726	 * is nothing else to do, So return DDI_SUCCESS,
1727	 */
1728	if (port->fp_soft_state & FP_SOFT_POWER_DOWN) {
1729		return (DDI_SUCCESS);
1730	}
1731
1732	/*
1733	 * Check if an unsolicited callback or state change handling
1734	 * is in progress. If true, fail the PM suspend operation.
1735	 * But don't print a message unless the verbosity of the
1736	 * driver desires otherwise.
1737	 */
1738	if ((port->fp_soft_state & FP_SOFT_IN_STATEC_CB) ||
1739	    (port->fp_soft_state & FP_SOFT_IN_UNSOL_CB)) {
1740		FP_TRACE(FP_NHEAD2(9, 0),
1741		    "Unsolicited callback in progress: Failing power down");
1742		return (DDI_FAILURE);
1743	}
1744
1745	/*
1746	 * Check of FC port thread is busy
1747	 */
1748	if (port->fp_job_head) {
1749		FP_TRACE(FP_NHEAD2(9, 0),
1750		    "FC port thread is busy: Failing power down");
1751		return (DDI_FAILURE);
1752	}
1753	port->fp_soft_state |= FP_SOFT_POWER_DOWN;
1754
1755	/*
1756	 * check if the ULPs are ready for power down
1757	 */
1758	mutex_exit(&port->fp_mutex);
1759	if (fctl_detach_ulps(port, FC_CMD_POWER_DOWN,
1760	    &modlinkage) != FC_SUCCESS) {
1761		mutex_enter(&port->fp_mutex);
1762		port->fp_soft_state &= ~FP_SOFT_POWER_DOWN;
1763		mutex_exit(&port->fp_mutex);
1764
1765		/*
1766		 * Power back up the obedient ULPs that went down
1767		 */
1768		fp_attach_ulps(port, FC_CMD_POWER_UP);
1769
1770		FP_TRACE(FP_NHEAD2(9, 0),
1771		    "ULP(s) busy, detach_ulps failed. Failing power down");
1772		mutex_enter(&port->fp_mutex);
1773		return (DDI_FAILURE);
1774	}
1775	mutex_enter(&port->fp_mutex);
1776
1777	fp_suspend_all(port);
1778
1779	return (DDI_SUCCESS);
1780}
1781
1782
1783/*
1784 * Suspend the entire FC port
1785 */
1786static void
1787fp_suspend_all(fc_local_port_t *port)
1788{
1789	int			index;
1790	struct pwwn_hash 	*head;
1791	fc_remote_port_t 	*pd;
1792
1793	ASSERT(MUTEX_HELD(&port->fp_mutex));
1794
1795	if (port->fp_wait_tid != 0) {
1796		timeout_id_t 	tid;
1797
1798		tid = port->fp_wait_tid;
1799		port->fp_wait_tid = (timeout_id_t)NULL;
1800		mutex_exit(&port->fp_mutex);
1801		(void) untimeout(tid);
1802		mutex_enter(&port->fp_mutex);
1803		port->fp_restore |= FP_RESTORE_WAIT_TIMEOUT;
1804	}
1805
1806	if (port->fp_offline_tid) {
1807		timeout_id_t 	tid;
1808
1809		tid = port->fp_offline_tid;
1810		port->fp_offline_tid = (timeout_id_t)NULL;
1811		mutex_exit(&port->fp_mutex);
1812		(void) untimeout(tid);
1813		mutex_enter(&port->fp_mutex);
1814		port->fp_restore |= FP_RESTORE_OFFLINE_TIMEOUT;
1815	}
1816	mutex_exit(&port->fp_mutex);
1817	port->fp_fca_tran->fca_unbind_port(port->fp_fca_handle);
1818	mutex_enter(&port->fp_mutex);
1819
1820	/*
1821	 * Mark all devices as OLD, and reset the LOGIN state as well
1822	 * (this will force the ULPs to perform a LOGIN after calling
1823	 * fc_portgetmap() during RESUME/PM_RESUME)
1824	 */
1825	for (index = 0; index < pwwn_table_size; index++) {
1826		head = &port->fp_pwwn_table[index];
1827		pd = head->pwwn_head;
1828		while (pd != NULL) {
1829			mutex_enter(&pd->pd_mutex);
1830			fp_remote_port_offline(pd);
1831			fctl_delist_did_table(port, pd);
1832			pd->pd_state = PORT_DEVICE_VALID;
1833			pd->pd_login_count = 0;
1834			mutex_exit(&pd->pd_mutex);
1835			pd = pd->pd_wwn_hnext;
1836		}
1837	}
1838}
1839
1840
1841/*
1842 * fp_cache_constructor: Constructor function for kmem_cache_create(9F).
1843 * Performs intializations for fc_packet_t structs.
1844 * Returns 0 for success or -1 for failure.
1845 *
1846 * This function allocates DMA handles for both command and responses.
1847 * Most of the ELSs used have both command and responses so it is strongly
1848 * desired to move them to cache constructor routine.
1849 *
1850 * Context: Can sleep iff called with KM_SLEEP flag.
1851 */
1852static int
1853fp_cache_constructor(void *buf, void *cdarg, int kmflags)
1854{
1855	int 		(*cb) (caddr_t);
1856	fc_packet_t 	*pkt;
1857	fp_cmd_t 	*cmd = (fp_cmd_t *)buf;
1858	fc_local_port_t *port = (fc_local_port_t *)cdarg;
1859
1860	cb = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
1861
1862	cmd->cmd_next = NULL;
1863	cmd->cmd_flags = 0;
1864	cmd->cmd_dflags = 0;
1865	cmd->cmd_job = NULL;
1866	cmd->cmd_port = port;
1867	pkt = &cmd->cmd_pkt;
1868
1869	if (ddi_dma_alloc_handle(port->fp_fca_dip,
1870	    port->fp_fca_tran->fca_dma_attr, cb, NULL,
1871	    &pkt->pkt_cmd_dma) != DDI_SUCCESS) {
1872		return (-1);
1873	}
1874
1875	if (ddi_dma_alloc_handle(port->fp_fca_dip,
1876	    port->fp_fca_tran->fca_dma_attr, cb, NULL,
1877	    &pkt->pkt_resp_dma) != DDI_SUCCESS) {
1878		ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1879		return (-1);
1880	}
1881
1882	pkt->pkt_cmd_acc = pkt->pkt_resp_acc = NULL;
1883	pkt->pkt_cmd_cookie_cnt = pkt->pkt_resp_cookie_cnt =
1884	    pkt->pkt_data_cookie_cnt = 0;
1885	pkt->pkt_cmd_cookie = pkt->pkt_resp_cookie =
1886	    pkt->pkt_data_cookie = NULL;
1887	pkt->pkt_fca_private = (caddr_t)buf + sizeof (fp_cmd_t);
1888
1889	return (0);
1890}
1891
1892
1893/*
1894 * fp_cache_destructor: Destructor function for kmem_cache_create().
1895 * Performs un-intializations for fc_packet_t structs.
1896 */
1897/* ARGSUSED */
1898static void
1899fp_cache_destructor(void *buf, void *cdarg)
1900{
1901	fp_cmd_t 	*cmd = (fp_cmd_t *)buf;
1902	fc_packet_t	*pkt;
1903
1904	pkt = &cmd->cmd_pkt;
1905	if (pkt->pkt_cmd_dma) {
1906		ddi_dma_free_handle(&pkt->pkt_cmd_dma);
1907	}
1908
1909	if (pkt->pkt_resp_dma) {
1910		ddi_dma_free_handle(&pkt->pkt_resp_dma);
1911	}
1912}
1913
1914
1915/*
1916 * Packet allocation for ELS and any other port driver commands
1917 *
1918 * Some ELSs like FLOGI and PLOGI are critical for topology and
1919 * device discovery and a system's inability to allocate memory
1920 * or DVMA resources while performing some of these critical ELSs
1921 * cause a lot of problem. While memory allocation failures are
1922 * rare, DVMA resource failures are common as the applications
1923 * are becoming more and more powerful on huge servers.  So it
1924 * is desirable to have a framework support to reserve a fragment
1925 * of DVMA. So until this is fixed the correct way, the suffering
1926 * is huge whenever a LIP happens at a time DVMA resources are
1927 * drained out completely - So an attempt needs to be made to
1928 * KM_SLEEP while requesting for these resources, hoping that
1929 * the requests won't hang forever.
1930 *
1931 * The fc_remote_port_t argument is stored into the pkt_pd field in the
1932 * fc_packet_t struct prior to the fc_ulp_init_packet() call.  This
1933 * ensures that the pd_ref_count for the fc_remote_port_t is valid.
1934 * If there is no fc_remote_port_t associated with the fc_packet_t, then
1935 * fp_alloc_pkt() must be called with pd set to NULL.
1936 */
1937
1938static fp_cmd_t *
1939fp_alloc_pkt(fc_local_port_t *port, int cmd_len, int resp_len, int kmflags,
1940    fc_remote_port_t *pd)
1941{
1942	int		rval;
1943	ulong_t		real_len;
1944	fp_cmd_t 	*cmd;
1945	fc_packet_t	*pkt;
1946	int 		(*cb) (caddr_t);
1947	ddi_dma_cookie_t	pkt_cookie;
1948	ddi_dma_cookie_t	*cp;
1949	uint32_t		cnt;
1950
1951	ASSERT(!MUTEX_HELD(&port->fp_mutex));
1952
1953	cb = (kmflags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
1954
1955	cmd = (fp_cmd_t *)kmem_cache_alloc(port->fp_pkt_cache, kmflags);
1956	if (cmd == NULL) {
1957		return (cmd);
1958	}
1959
1960	cmd->cmd_ulp_pkt = NULL;
1961	cmd->cmd_flags = 0;
1962	pkt = &cmd->cmd_pkt;
1963	ASSERT(cmd->cmd_dflags == 0);
1964
1965	pkt->pkt_datalen = 0;
1966	pkt->pkt_data = NULL;
1967	pkt->pkt_state = 0;
1968	pkt->pkt_action = 0;
1969	pkt->pkt_reason = 0;
1970	pkt->pkt_expln = 0;
1971
1972	/*
1973	 * Init pkt_pd with the given pointer; this must be done _before_
1974	 * the call to fc_ulp_init_packet().
1975	 */
1976	pkt->pkt_pd = pd;
1977
1978	/* Now call the FCA driver to init its private, per-packet fields */
1979	if (fc_ulp_init_packet((opaque_t)port, pkt, kmflags) != FC_SUCCESS) {
1980		goto alloc_pkt_failed;
1981	}
1982
1983	if (cmd_len) {
1984		ASSERT(pkt->pkt_cmd_dma != NULL);
1985
1986		rval = ddi_dma_mem_alloc(pkt->pkt_cmd_dma, cmd_len,
1987		    port->fp_fca_tran->fca_acc_attr, DDI_DMA_CONSISTENT,
1988		    cb, NULL, (caddr_t *)&pkt->pkt_cmd, &real_len,
1989		    &pkt->pkt_cmd_acc);
1990
1991		if (rval != DDI_SUCCESS) {
1992			goto alloc_pkt_failed;
1993		}
1994		cmd->cmd_dflags |= FP_CMD_VALID_DMA_MEM;
1995
1996		if (real_len < cmd_len) {
1997			goto alloc_pkt_failed;
1998		}
1999
2000		rval = ddi_dma_addr_bind_handle(pkt->pkt_cmd_dma, NULL,
2001		    pkt->pkt_cmd, real_len, DDI_DMA_WRITE |
2002		    DDI_DMA_CONSISTENT, cb, NULL,
2003		    &pkt_cookie, &pkt->pkt_cmd_cookie_cnt);
2004
2005		if (rval != DDI_DMA_MAPPED) {
2006			goto alloc_pkt_failed;
2007		}
2008
2009		cmd->cmd_dflags |= FP_CMD_VALID_DMA_BIND;
2010
2011		if (pkt->pkt_cmd_cookie_cnt >
2012		    port->fp_fca_tran->fca_dma_attr->dma_attr_sgllen) {
2013			goto alloc_pkt_failed;
2014		}
2015
2016		ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2017
2018		cp = pkt->pkt_cmd_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2019		    pkt->pkt_cmd_cookie_cnt * sizeof (pkt_cookie),
2020		    KM_NOSLEEP);
2021
2022		if (cp == NULL) {
2023			goto alloc_pkt_failed;
2024		}
2025
2026		*cp = pkt_cookie;
2027		cp++;
2028		for (cnt = 1; cnt < pkt->pkt_cmd_cookie_cnt; cnt++, cp++) {
2029			ddi_dma_nextcookie(pkt->pkt_cmd_dma, &pkt_cookie);
2030			*cp = pkt_cookie;
2031		}
2032	}
2033
2034	if (resp_len) {
2035		ASSERT(pkt->pkt_resp_dma != NULL);
2036
2037		rval = ddi_dma_mem_alloc(pkt->pkt_resp_dma, resp_len,
2038		    port->fp_fca_tran->fca_acc_attr,
2039		    DDI_DMA_CONSISTENT, cb, NULL,
2040		    (caddr_t *)&pkt->pkt_resp, &real_len,
2041		    &pkt->pkt_resp_acc);
2042
2043		if (rval != DDI_SUCCESS) {
2044			goto alloc_pkt_failed;
2045		}
2046		cmd->cmd_dflags |= FP_RESP_VALID_DMA_MEM;
2047
2048		if (real_len < resp_len) {
2049			goto alloc_pkt_failed;
2050		}
2051
2052		rval = ddi_dma_addr_bind_handle(pkt->pkt_resp_dma, NULL,
2053		    pkt->pkt_resp, real_len, DDI_DMA_READ |
2054		    DDI_DMA_CONSISTENT, cb, NULL,
2055		    &pkt_cookie, &pkt->pkt_resp_cookie_cnt);
2056
2057		if (rval != DDI_DMA_MAPPED) {
2058			goto alloc_pkt_failed;
2059		}
2060
2061		cmd->cmd_dflags |= FP_RESP_VALID_DMA_BIND;
2062
2063		if (pkt->pkt_resp_cookie_cnt >
2064		    port->fp_fca_tran->fca_dma_attr->dma_attr_sgllen) {
2065			goto alloc_pkt_failed;
2066		}
2067
2068		ASSERT(pkt->pkt_cmd_cookie_cnt != 0);
2069
2070		cp = pkt->pkt_resp_cookie = (ddi_dma_cookie_t *)kmem_alloc(
2071		    pkt->pkt_resp_cookie_cnt * sizeof (pkt_cookie),
2072		    KM_NOSLEEP);
2073
2074		if (cp == NULL) {
2075			goto alloc_pkt_failed;
2076		}
2077
2078		*cp = pkt_cookie;
2079		cp++;
2080		for (cnt = 1; cnt < pkt->pkt_resp_cookie_cnt; cnt++, cp++) {
2081			ddi_dma_nextcookie(pkt->pkt_resp_dma, &pkt_cookie);
2082			*cp = pkt_cookie;
2083		}
2084	}
2085
2086	pkt->pkt_cmdlen = cmd_len;
2087	pkt->pkt_rsplen = resp_len;
2088	pkt->pkt_ulp_private = cmd;
2089
2090	return (cmd);
2091
2092alloc_pkt_failed:
2093
2094	fp_free_dma(cmd);
2095
2096	if (pkt->pkt_cmd_cookie != NULL) {
2097		kmem_free(pkt->pkt_cmd_cookie,
2098		    pkt->pkt_cmd_cookie_cnt * sizeof (ddi_dma_cookie_t));
2099		pkt->pkt_cmd_cookie = NULL;
2100	}
2101
2102	if (pkt->pkt_resp_cookie != NULL) {
2103		kmem_free(pkt->pkt_resp_cookie,
2104		    pkt->pkt_resp_cookie_cnt * sizeof (ddi_dma_cookie_t));
2105		pkt->pkt_resp_cookie = NULL;
2106	}
2107
2108	kmem_cache_free(port->fp_pkt_cache, cmd);
2109
2110	return (NULL);
2111}
2112
2113
2114/*
2115 * Free FC packet
2116 */
2117static void
2118fp_free_pkt(fp_cmd_t *cmd)
2119{
2120	fc_local_port_t *port;
2121	fc_packet_t 	*pkt;
2122
2123	ASSERT(!MUTEX_HELD(&cmd->cmd_port->fp_mutex));
2124
2125	cmd->cmd_next = NULL;
2126	cmd->cmd_job = NULL;
2127	pkt = &cmd->cmd_pkt;
2128	pkt->pkt_ulp_private = 0;
2129	pkt->pkt_tran_flags = 0;
2130	pkt->pkt_tran_type = 0;
2131	port = cmd->cmd_port;
2132
2133	if (pkt->pkt_cmd_cookie != NULL) {
2134		kmem_free(pkt->pkt_cmd_cookie, pkt->pkt_cmd_cookie_cnt *
2135		    sizeof (ddi_dma_cookie_t));
2136		pkt->pkt_cmd_cookie = NULL;
2137	}
2138
2139	if (pkt->pkt_resp_cookie != NULL) {
2140		kmem_free(pkt->pkt_resp_cookie, pkt->pkt_resp_cookie_cnt *
2141		    sizeof (ddi_dma_cookie_t));
2142		pkt->pkt_resp_cookie = NULL;
2143	}
2144
2145	fp_free_dma(cmd);
2146	(void) fc_ulp_uninit_packet((opaque_t)port, pkt);
2147	kmem_cache_free(port->fp_pkt_cache, (void *)cmd);
2148}
2149
2150
2151/*
2152 * Release DVMA resources
2153 */
2154static void
2155fp_free_dma(fp_cmd_t *cmd)
2156{
2157	fc_packet_t *pkt = &cmd->cmd_pkt;
2158
2159	pkt->pkt_cmdlen = 0;
2160	pkt->pkt_rsplen = 0;
2161	pkt->pkt_tran_type = 0;
2162	pkt->pkt_tran_flags = 0;
2163
2164	if (cmd->cmd_dflags & FP_CMD_VALID_DMA_BIND) {
2165		(void) ddi_dma_unbind_handle(pkt->pkt_cmd_dma);
2166	}
2167
2168	if (cmd->cmd_dflags & FP_CMD_VALID_DMA_MEM) {
2169		if (pkt->pkt_cmd_acc) {
2170			ddi_dma_mem_free(&pkt->pkt_cmd_acc);
2171		}
2172	}
2173
2174	if (cmd->cmd_dflags & FP_RESP_VALID_DMA_BIND) {
2175		(void) ddi_dma_unbind_handle(pkt->pkt_resp_dma);
2176	}
2177
2178	if (cmd->cmd_dflags & FP_RESP_VALID_DMA_MEM) {
2179		if (pkt->pkt_resp_acc) {
2180			ddi_dma_mem_free(&pkt->pkt_resp_acc);
2181		}
2182	}
2183	cmd->cmd_dflags = 0;
2184}
2185
2186
2187/*
2188 * Dedicated thread to perform various activities.  One thread for
2189 * each fc_local_port_t (driver soft state) instance.
2190 * Note, this effectively works out to one thread for each local
2191 * port, but there are also some Solaris taskq threads in use on a per-local
2192 * port basis; these also need to be taken into consideration.
2193 */
2194static void
2195fp_job_handler(fc_local_port_t *port)
2196{
2197	int			rval;
2198	uint32_t		*d_id;
2199	fc_remote_port_t 	*pd;
2200	job_request_t 		*job;
2201
2202#ifndef	__lock_lint
2203	/*
2204	 * Solaris-internal stuff for proper operation of kernel threads
2205	 * with Solaris CPR.
2206	 */
2207	CALLB_CPR_INIT(&port->fp_cpr_info, &port->fp_mutex,
2208	    callb_generic_cpr, "fp_job_handler");
2209#endif
2210
2211
2212	/* Loop forever waiting for work to do */
2213	for (;;) {
2214
2215		mutex_enter(&port->fp_mutex);
2216
2217		/*
2218		 * Sleep if no work to do right now, or if we want
2219		 * to suspend or power-down.
2220		 */
2221		while (port->fp_job_head == NULL ||
2222		    (port->fp_soft_state & (FP_SOFT_POWER_DOWN |
2223		    FP_SOFT_SUSPEND))) {
2224			CALLB_CPR_SAFE_BEGIN(&port->fp_cpr_info);
2225			cv_wait(&port->fp_cv, &port->fp_mutex);
2226			CALLB_CPR_SAFE_END(&port->fp_cpr_info, &port->fp_mutex);
2227		}
2228
2229		/*
2230		 * OK, we've just been woken up, so retrieve the next entry
2231		 * from the head of the job queue for this local port.
2232		 */
2233		job = fctl_deque_job(port);
2234
2235		/*
2236		 * Handle all the fp driver's supported job codes here
2237		 * in this big honkin' switch.
2238		 */
2239		switch (job->job_code) {
2240		case JOB_PORT_SHUTDOWN:
2241			/*
2242			 * fp_port_shutdown() is only called from here. This
2243			 * will prepare the local port instance (softstate)
2244			 * for detaching.  This cancels timeout callbacks,
2245			 * executes LOGOs with remote ports, cleans up tables,
2246			 * and deallocates data structs.
2247			 */
2248			fp_port_shutdown(port, job);
2249
2250			/*
2251			 * This will exit the job thread.
2252			 */
2253#ifndef __lock_lint
2254			CALLB_CPR_EXIT(&(port->fp_cpr_info));
2255#else
2256			mutex_exit(&port->fp_mutex);
2257#endif
2258			fctl_jobdone(job);
2259			thread_exit();
2260
2261			/* NOTREACHED */
2262
2263		case JOB_ATTACH_ULP: {
2264			/*
2265			 * This job is spawned in response to a ULP calling
2266			 * fc_ulp_add().
2267			 */
2268
2269			boolean_t do_attach_ulps = B_TRUE;
2270
2271			/*
2272			 * If fp is detaching, we don't want to call
2273			 * fp_startup_done as this asynchronous
2274			 * notification may interfere with the re-attach.
2275			 */
2276
2277			if (port->fp_soft_state & (FP_DETACH_INPROGRESS |
2278			    FP_SOFT_IN_DETACH | FP_DETACH_FAILED)) {
2279				do_attach_ulps = B_FALSE;
2280			} else {
2281				/*
2282				 * We are going to force the transport
2283				 * to attach to the ULPs, so set
2284				 * fp_ulp_attach.  This will keep any
2285				 * potential detach from occurring until
2286				 * we are done.
2287				 */
2288				port->fp_ulp_attach = 1;
2289			}
2290
2291			mutex_exit(&port->fp_mutex);
2292
2293			/*
2294			 * NOTE: Since we just dropped the mutex, there is now
2295			 * a race window where the fp_soft_state check above
2296			 * could change here.  This race is covered because an
2297			 * additional check was added in the functions hidden
2298			 * under fp_startup_done().
2299			 */
2300			if (do_attach_ulps == B_TRUE) {
2301				/*
2302				 * This goes thru a bit of a convoluted call
2303				 * chain before spawning off a DDI taskq
2304				 * request to perform the actual attach
2305				 * operations. Blocking can occur at a number
2306				 * of points.
2307				 */
2308				fp_startup_done((opaque_t)port, FC_PKT_SUCCESS);
2309			}
2310			job->job_result = FC_SUCCESS;
2311			fctl_jobdone(job);
2312			break;
2313		}
2314
2315		case JOB_ULP_NOTIFY: {
2316			/*
2317			 * Pass state change notifications up to any/all
2318			 * registered ULPs.
2319			 */
2320			uint32_t statec;
2321
2322			statec = job->job_ulp_listlen;
2323			if (statec == FC_STATE_RESET_REQUESTED) {
2324				port->fp_last_task = port->fp_task;
2325				port->fp_task = FP_TASK_OFFLINE;
2326				fp_port_offline(port, 0);
2327				port->fp_task = port->fp_last_task;
2328				port->fp_last_task = FP_TASK_IDLE;
2329			}
2330
2331			if (--port->fp_statec_busy == 0) {
2332				port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
2333			}
2334
2335			mutex_exit(&port->fp_mutex);
2336
2337			job->job_result = fp_ulp_notify(port, statec, KM_SLEEP);
2338			fctl_jobdone(job);
2339			break;
2340		}
2341
2342		case JOB_PLOGI_ONE:
2343			/*
2344			 * Issue a PLOGI to a single remote port. Multiple
2345			 * PLOGIs to different remote ports may occur in
2346			 * parallel.
2347			 * This can create the fc_remote_port_t if it does not
2348			 * already exist.
2349			 */
2350
2351			mutex_exit(&port->fp_mutex);
2352			d_id = (uint32_t *)job->job_private;
2353			pd = fctl_get_remote_port_by_did(port, *d_id);
2354
2355			if (pd) {
2356				mutex_enter(&pd->pd_mutex);
2357				if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
2358					pd->pd_login_count++;
2359					mutex_exit(&pd->pd_mutex);
2360					job->job_result = FC_SUCCESS;
2361					fctl_jobdone(job);
2362					break;
2363				}
2364				mutex_exit(&pd->pd_mutex);
2365			} else {
2366				mutex_enter(&port->fp_mutex);
2367				if (FC_IS_TOP_SWITCH(port->fp_topology)) {
2368					mutex_exit(&port->fp_mutex);
2369					pd = fp_create_remote_port_by_ns(port,
2370					    *d_id, KM_SLEEP);
2371					if (pd == NULL) {
2372						job->job_result = FC_FAILURE;
2373						fctl_jobdone(job);
2374						break;
2375					}
2376				} else {
2377					mutex_exit(&port->fp_mutex);
2378				}
2379			}
2380
2381			job->job_flags |= JOB_TYPE_FP_ASYNC;
2382			job->job_counter = 1;
2383
2384			rval = fp_port_login(port, *d_id, job,
2385			    FP_CMD_PLOGI_RETAIN, KM_SLEEP, pd, NULL);
2386
2387			if (rval != FC_SUCCESS) {
2388				job->job_result = rval;
2389				fctl_jobdone(job);
2390			}
2391			break;
2392
2393		case JOB_LOGO_ONE: {
2394			/*
2395			 * Issue a PLOGO to a single remote port. Multiple
2396			 * PLOGOs to different remote ports may occur in
2397			 * parallel.
2398			 */
2399			fc_remote_port_t *pd;
2400
2401#ifndef	__lock_lint
2402			ASSERT(job->job_counter > 0);
2403#endif
2404
2405			pd = (fc_remote_port_t *)job->job_ulp_pkts;
2406
2407			mutex_enter(&pd->pd_mutex);
2408			if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
2409				mutex_exit(&pd->pd_mutex);
2410				job->job_result = FC_LOGINREQ;
2411				mutex_exit(&port->fp_mutex);
2412				fctl_jobdone(job);
2413				break;
2414			}
2415			if (pd->pd_login_count > 1) {
2416				pd->pd_login_count--;
2417				mutex_exit(&pd->pd_mutex);
2418				job->job_result = FC_SUCCESS;
2419				mutex_exit(&port->fp_mutex);
2420				fctl_jobdone(job);
2421				break;
2422			}
2423			mutex_exit(&pd->pd_mutex);
2424			mutex_exit(&port->fp_mutex);
2425			job->job_flags |= JOB_TYPE_FP_ASYNC;
2426			(void) fp_logout(port, pd, job);
2427			break;
2428		}
2429
2430		case JOB_FCIO_LOGIN:
2431			/*
2432			 * PLOGI initiated at ioctl request.
2433			 */
2434			mutex_exit(&port->fp_mutex);
2435			job->job_result =
2436			    fp_fcio_login(port, job->job_private, job);
2437			fctl_jobdone(job);
2438			break;
2439
2440		case JOB_FCIO_LOGOUT:
2441			/*
2442			 * PLOGO initiated at ioctl request.
2443			 */
2444			mutex_exit(&port->fp_mutex);
2445			job->job_result =
2446			    fp_fcio_logout(port, job->job_private, job);
2447			fctl_jobdone(job);
2448			break;
2449
2450		case JOB_PORT_GETMAP:
2451		case JOB_PORT_GETMAP_PLOGI_ALL: {
2452			port->fp_last_task = port->fp_task;
2453			port->fp_task = FP_TASK_GETMAP;
2454
2455			switch (port->fp_topology) {
2456			case FC_TOP_PRIVATE_LOOP:
2457				job->job_counter = 1;
2458
2459				fp_get_loopmap(port, job);
2460				mutex_exit(&port->fp_mutex);
2461				fp_jobwait(job);
2462				fctl_fillout_map(port,
2463				    (fc_portmap_t **)job->job_private,
2464				    (uint32_t *)job->job_arg, 1, 0, 0);
2465				fctl_jobdone(job);
2466				mutex_enter(&port->fp_mutex);
2467				break;
2468
2469			case FC_TOP_PUBLIC_LOOP:
2470			case FC_TOP_FABRIC:
2471				mutex_exit(&port->fp_mutex);
2472				job->job_counter = 1;
2473
2474				job->job_result = fp_ns_getmap(port,
2475				    job, (fc_portmap_t **)job->job_private,
2476				    (uint32_t *)job->job_arg,
2477				    FCTL_GAN_START_ID);
2478				fctl_jobdone(job);
2479				mutex_enter(&port->fp_mutex);
2480				break;
2481
2482			case FC_TOP_PT_PT:
2483				mutex_exit(&port->fp_mutex);
2484				fctl_fillout_map(port,
2485				    (fc_portmap_t **)job->job_private,
2486				    (uint32_t *)job->job_arg, 1, 0, 0);
2487				fctl_jobdone(job);
2488				mutex_enter(&port->fp_mutex);
2489				break;
2490
2491			default:
2492				mutex_exit(&port->fp_mutex);
2493				fctl_jobdone(job);
2494				mutex_enter(&port->fp_mutex);
2495				break;
2496			}
2497			port->fp_task = port->fp_last_task;
2498			port->fp_last_task = FP_TASK_IDLE;
2499			mutex_exit(&port->fp_mutex);
2500			break;
2501		}
2502
2503		case JOB_PORT_OFFLINE: {
2504			fp_log_port_event(port, ESC_SUNFC_PORT_OFFLINE);
2505
2506			port->fp_last_task = port->fp_task;
2507			port->fp_task = FP_TASK_OFFLINE;
2508
2509			if (port->fp_statec_busy > 2) {
2510				job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
2511				fp_port_offline(port, 0);
2512				if (--port->fp_statec_busy == 0) {
2513					port->fp_soft_state &=
2514					    ~FP_SOFT_IN_STATEC_CB;
2515				}
2516			} else {
2517				fp_port_offline(port, 1);
2518			}
2519
2520			port->fp_task = port->fp_last_task;
2521			port->fp_last_task = FP_TASK_IDLE;
2522
2523			mutex_exit(&port->fp_mutex);
2524
2525			fctl_jobdone(job);
2526			break;
2527		}
2528
2529		case JOB_PORT_STARTUP: {
2530			if ((rval = fp_port_startup(port, job)) != FC_SUCCESS) {
2531				if (port->fp_statec_busy > 1) {
2532					mutex_exit(&port->fp_mutex);
2533					break;
2534				}
2535				mutex_exit(&port->fp_mutex);
2536
2537				FP_TRACE(FP_NHEAD2(9, rval),
2538				    "Topology discovery failed");
2539				break;
2540			}
2541
2542			/*
2543			 * Attempt building device handles in case
2544			 * of private Loop.
2545			 */
2546			if (port->fp_topology == FC_TOP_PRIVATE_LOOP) {
2547				job->job_counter = 1;
2548
2549				fp_get_loopmap(port, job);
2550				mutex_exit(&port->fp_mutex);
2551				fp_jobwait(job);
2552				mutex_enter(&port->fp_mutex);
2553				if (port->fp_lilp_map.lilp_magic < MAGIC_LIRP) {
2554					ASSERT(port->fp_total_devices == 0);
2555					port->fp_total_devices =
2556					    port->fp_dev_count;
2557				}
2558			} else if (FC_IS_TOP_SWITCH(port->fp_topology)) {
2559				/*
2560				 * Hack to avoid state changes going up early
2561				 */
2562				port->fp_statec_busy++;
2563				port->fp_soft_state |= FP_SOFT_IN_STATEC_CB;
2564
2565				job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
2566				fp_fabric_online(port, job);
2567				job->job_flags &= ~JOB_CANCEL_ULP_NOTIFICATION;
2568			}
2569			mutex_exit(&port->fp_mutex);
2570			fctl_jobdone(job);
2571			break;
2572		}
2573
2574		case JOB_PORT_ONLINE: {
2575			char 		*newtop;
2576			char 		*oldtop;
2577			uint32_t	old_top;
2578
2579			fp_log_port_event(port, ESC_SUNFC_PORT_ONLINE);
2580
2581			/*
2582			 * Bail out early if there are a lot of
2583			 * state changes in the pipeline
2584			 */
2585			if (port->fp_statec_busy > 1) {
2586				--port->fp_statec_busy;
2587				mutex_exit(&port->fp_mutex);
2588				fctl_jobdone(job);
2589				break;
2590			}
2591
2592			switch (old_top = port->fp_topology) {
2593			case FC_TOP_PRIVATE_LOOP:
2594				oldtop = "Private Loop";
2595				break;
2596
2597			case FC_TOP_PUBLIC_LOOP:
2598				oldtop = "Public Loop";
2599				break;
2600
2601			case FC_TOP_PT_PT:
2602				oldtop = "Point to Point";
2603				break;
2604
2605			case FC_TOP_FABRIC:
2606				oldtop = "Fabric";
2607				break;
2608
2609			default:
2610				oldtop = NULL;
2611				break;
2612			}
2613
2614			port->fp_last_task = port->fp_task;
2615			port->fp_task = FP_TASK_ONLINE;
2616
2617			if ((rval = fp_port_startup(port, job)) != FC_SUCCESS) {
2618
2619				port->fp_task = port->fp_last_task;
2620				port->fp_last_task = FP_TASK_IDLE;
2621
2622				if (port->fp_statec_busy > 1) {
2623					--port->fp_statec_busy;
2624					mutex_exit(&port->fp_mutex);
2625					break;
2626				}
2627
2628				port->fp_state = FC_STATE_OFFLINE;
2629
2630				FP_TRACE(FP_NHEAD2(9, rval),
2631				    "Topology discovery failed");
2632
2633				if (--port->fp_statec_busy == 0) {
2634					port->fp_soft_state &=
2635					    ~FP_SOFT_IN_STATEC_CB;
2636				}
2637
2638				if (port->fp_offline_tid == NULL) {
2639					port->fp_offline_tid =
2640					    timeout(fp_offline_timeout,
2641					    (caddr_t)port, fp_offline_ticks);
2642				}
2643
2644				mutex_exit(&port->fp_mutex);
2645				break;
2646			}
2647
2648			switch (port->fp_topology) {
2649			case FC_TOP_PRIVATE_LOOP:
2650				newtop = "Private Loop";
2651				break;
2652
2653			case FC_TOP_PUBLIC_LOOP:
2654				newtop = "Public Loop";
2655				break;
2656
2657			case FC_TOP_PT_PT:
2658				newtop = "Point to Point";
2659				break;
2660
2661			case FC_TOP_FABRIC:
2662				newtop = "Fabric";
2663				break;
2664
2665			default:
2666				newtop = NULL;
2667				break;
2668			}
2669
2670			if (oldtop && newtop && strcmp(oldtop, newtop)) {
2671				fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
2672				    "Change in FC Topology old = %s new = %s",
2673				    oldtop, newtop);
2674			}
2675
2676			switch (port->fp_topology) {
2677			case FC_TOP_PRIVATE_LOOP: {
2678				int orphan = (old_top == FC_TOP_FABRIC ||
2679				    old_top == FC_TOP_PUBLIC_LOOP) ? 1 : 0;
2680
2681				mutex_exit(&port->fp_mutex);
2682				fp_loop_online(port, job, orphan);
2683				break;
2684			}
2685
2686			case FC_TOP_PUBLIC_LOOP:
2687				/* FALLTHROUGH */
2688			case FC_TOP_FABRIC:
2689				fp_fabric_online(port, job);
2690				mutex_exit(&port->fp_mutex);
2691				break;
2692
2693			case FC_TOP_PT_PT:
2694				fp_p2p_online(port, job);
2695				mutex_exit(&port->fp_mutex);
2696				break;
2697
2698			default:
2699				if (--port->fp_statec_busy != 0) {
2700					/*
2701					 * Watch curiously at what the next
2702					 * state transition can do.
2703					 */
2704					mutex_exit(&port->fp_mutex);
2705					break;
2706				}
2707
2708				FP_TRACE(FP_NHEAD2(9, 0),
2709				    "Topology Unknown, Offlining the port..");
2710
2711				port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
2712				port->fp_state = FC_STATE_OFFLINE;
2713
2714				if (port->fp_offline_tid == NULL) {
2715					port->fp_offline_tid =
2716					    timeout(fp_offline_timeout,
2717					    (caddr_t)port, fp_offline_ticks);
2718				}
2719				mutex_exit(&port->fp_mutex);
2720				break;
2721			}
2722
2723			mutex_enter(&port->fp_mutex);
2724
2725			port->fp_task = port->fp_last_task;
2726			port->fp_last_task = FP_TASK_IDLE;
2727
2728			mutex_exit(&port->fp_mutex);
2729
2730			fctl_jobdone(job);
2731			break;
2732		}
2733
2734		case JOB_PLOGI_GROUP: {
2735			mutex_exit(&port->fp_mutex);
2736			fp_plogi_group(port, job);
2737			break;
2738		}
2739
2740		case JOB_UNSOL_REQUEST: {
2741			mutex_exit(&port->fp_mutex);
2742			fp_handle_unsol_buf(port,
2743			    (fc_unsol_buf_t *)job->job_private, job);
2744			fctl_dealloc_job(job);
2745			break;
2746		}
2747
2748		case JOB_NS_CMD: {
2749			fctl_ns_req_t *ns_cmd;
2750
2751			mutex_exit(&port->fp_mutex);
2752
2753			job->job_flags |= JOB_TYPE_FP_ASYNC;
2754			ns_cmd = (fctl_ns_req_t *)job->job_private;
2755			if (ns_cmd->ns_cmd_code < NS_GA_NXT ||
2756			    ns_cmd->ns_cmd_code > NS_DA_ID) {
2757				job->job_result = FC_BADCMD;
2758				fctl_jobdone(job);
2759				break;
2760			}
2761
2762			if (FC_IS_CMD_A_REG(ns_cmd->ns_cmd_code)) {
2763				if (ns_cmd->ns_pd != NULL) {
2764					job->job_result = FC_BADOBJECT;
2765					fctl_jobdone(job);
2766					break;
2767				}
2768
2769				job->job_counter = 1;
2770
2771				rval = fp_ns_reg(port, ns_cmd->ns_pd,
2772				    ns_cmd->ns_cmd_code, job, 0, KM_SLEEP);
2773
2774				if (rval != FC_SUCCESS) {
2775					job->job_result = rval;
2776					fctl_jobdone(job);
2777				}
2778				break;
2779			}
2780			job->job_result = FC_SUCCESS;
2781			job->job_counter = 1;
2782
2783			rval = fp_ns_query(port, ns_cmd, job, 0, KM_SLEEP);
2784			if (rval != FC_SUCCESS) {
2785				fctl_jobdone(job);
2786			}
2787			break;
2788		}
2789
2790		case JOB_LINK_RESET: {
2791			la_wwn_t *pwwn;
2792			uint32_t topology;
2793
2794			pwwn = (la_wwn_t *)job->job_private;
2795			ASSERT(pwwn != NULL);
2796
2797			topology = port->fp_topology;
2798			mutex_exit(&port->fp_mutex);
2799
2800			if (fctl_is_wwn_zero(pwwn) == FC_SUCCESS ||
2801			    topology == FC_TOP_PRIVATE_LOOP) {
2802				job->job_flags |= JOB_TYPE_FP_ASYNC;
2803				rval = port->fp_fca_tran->fca_reset(
2804				    port->fp_fca_handle, FC_FCA_LINK_RESET);
2805				job->job_result = rval;
2806				fp_jobdone(job);
2807			} else {
2808				ASSERT((job->job_flags &
2809				    JOB_TYPE_FP_ASYNC) == 0);
2810
2811				if (FC_IS_TOP_SWITCH(topology)) {
2812					rval = fp_remote_lip(port, pwwn,
2813					    KM_SLEEP, job);
2814				} else {
2815					rval = FC_FAILURE;
2816				}
2817				if (rval != FC_SUCCESS) {
2818					job->job_result = rval;
2819				}
2820				fctl_jobdone(job);
2821			}
2822			break;
2823		}
2824
2825		default:
2826			mutex_exit(&port->fp_mutex);
2827			job->job_result = FC_BADCMD;
2828			fctl_jobdone(job);
2829			break;
2830		}
2831	}
2832	/* NOTREACHED */
2833}
2834
2835
2836/*
2837 * Perform FC port bring up initialization
2838 */
2839static int
2840fp_port_startup(fc_local_port_t *port, job_request_t *job)
2841{
2842	int		rval;
2843	uint32_t	state;
2844	uint32_t	src_id;
2845	fc_lilpmap_t 	*lilp_map;
2846
2847	ASSERT(MUTEX_HELD(&port->fp_mutex));
2848	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
2849
2850	FP_DTRACE(FP_NHEAD1(2, 0), "Entering fp_port_startup;"
2851	    " port=%p, job=%p", port, job);
2852
2853	port->fp_topology = FC_TOP_UNKNOWN;
2854	port->fp_port_id.port_id = 0;
2855	state = FC_PORT_STATE_MASK(port->fp_state);
2856
2857	if (state == FC_STATE_OFFLINE) {
2858		port->fp_port_type.port_type = FC_NS_PORT_UNKNOWN;
2859		job->job_result = FC_OFFLINE;
2860		mutex_exit(&port->fp_mutex);
2861		fctl_jobdone(job);
2862		mutex_enter(&port->fp_mutex);
2863		return (FC_OFFLINE);
2864	}
2865
2866	if (state == FC_STATE_LOOP) {
2867		port->fp_port_type.port_type = FC_NS_PORT_NL;
2868		mutex_exit(&port->fp_mutex);
2869
2870		lilp_map = &port->fp_lilp_map;
2871		if ((rval = fp_get_lilpmap(port, lilp_map)) != FC_SUCCESS) {
2872			job->job_result = FC_FAILURE;
2873			fctl_jobdone(job);
2874
2875			FP_TRACE(FP_NHEAD1(9, rval),
2876			    "LILP map Invalid or not present");
2877			mutex_enter(&port->fp_mutex);
2878			return (FC_FAILURE);
2879		}
2880
2881		if (lilp_map->lilp_length == 0) {
2882			job->job_result = FC_NO_MAP;
2883			fctl_jobdone(job);
2884			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
2885			    "LILP map length zero");
2886			mutex_enter(&port->fp_mutex);
2887			return (FC_NO_MAP);
2888		}
2889		src_id = lilp_map->lilp_myalpa & 0xFF;
2890	} else {
2891		fc_remote_port_t	*pd;
2892		fc_fca_pm_t		pm;
2893		fc_fca_p2p_info_t	p2p_info;
2894		int			pd_recepient;
2895
2896		/*
2897		 * Get P2P remote port info if possible
2898		 */
2899		bzero((caddr_t)&pm, sizeof (pm));
2900
2901		pm.pm_cmd_flags = FC_FCA_PM_READ;
2902		pm.pm_cmd_code = FC_PORT_GET_P2P_INFO;
2903		pm.pm_data_len = sizeof (fc_fca_p2p_info_t);
2904		pm.pm_data_buf = (caddr_t)&p2p_info;
2905
2906		rval = port->fp_fca_tran->fca_port_manage(
2907		    port->fp_fca_handle, &pm);
2908
2909		if (rval == FC_SUCCESS) {
2910			port->fp_port_id.port_id = p2p_info.fca_d_id;
2911			port->fp_port_type.port_type = FC_NS_PORT_N;
2912			port->fp_topology = FC_TOP_PT_PT;
2913			port->fp_total_devices = 1;
2914			pd_recepient = fctl_wwn_cmp(
2915			    &port->fp_service_params.nport_ww_name,
2916			    &p2p_info.pwwn) < 0 ?
2917			    PD_PLOGI_RECEPIENT : PD_PLOGI_INITIATOR;
2918			mutex_exit(&port->fp_mutex);
2919			pd = fctl_create_remote_port(port,
2920			    &p2p_info.nwwn,
2921			    &p2p_info.pwwn,
2922			    p2p_info.d_id,
2923			    pd_recepient, KM_NOSLEEP);
2924			FP_DTRACE(FP_NHEAD1(2, 0), "Exiting fp_port_startup;"
2925			    " P2P port=%p pd=%p", port, pd);
2926			mutex_enter(&port->fp_mutex);
2927			return (FC_SUCCESS);
2928		}
2929		port->fp_port_type.port_type = FC_NS_PORT_N;
2930		mutex_exit(&port->fp_mutex);
2931		src_id = 0;
2932	}
2933
2934	job->job_counter = 1;
2935	job->job_result = FC_SUCCESS;
2936
2937	if ((rval = fp_fabric_login(port, src_id, job, FP_CMD_PLOGI_DONT_CARE,
2938	    KM_SLEEP)) != FC_SUCCESS) {
2939		port->fp_port_type.port_type = FC_NS_PORT_UNKNOWN;
2940		job->job_result = FC_FAILURE;
2941		fctl_jobdone(job);
2942
2943		mutex_enter(&port->fp_mutex);
2944		if (port->fp_statec_busy <= 1) {
2945			mutex_exit(&port->fp_mutex);
2946			fp_printf(port, CE_NOTE, FP_LOG_ONLY, rval, NULL,
2947			    "Couldn't transport FLOGI");
2948			mutex_enter(&port->fp_mutex);
2949		}
2950		return (FC_FAILURE);
2951	}
2952
2953	fp_jobwait(job);
2954
2955	mutex_enter(&port->fp_mutex);
2956	if (job->job_result == FC_SUCCESS) {
2957		if (FC_IS_TOP_SWITCH(port->fp_topology)) {
2958			mutex_exit(&port->fp_mutex);
2959			fp_ns_init(port, job, KM_SLEEP);
2960			mutex_enter(&port->fp_mutex);
2961		}
2962	} else {
2963		if (state == FC_STATE_LOOP) {
2964			port->fp_topology = FC_TOP_PRIVATE_LOOP;
2965			port->fp_port_id.port_id =
2966			    port->fp_lilp_map.lilp_myalpa & 0xFF;
2967		}
2968	}
2969
2970	FP_DTRACE(FP_NHEAD1(2, 0), "Exiting fp_port_startup; port=%p, job=%p",
2971	    port, job);
2972
2973	return (FC_SUCCESS);
2974}
2975
2976
2977/*
2978 * Perform ULP invocations following FC port startup
2979 */
2980/* ARGSUSED */
2981static void
2982fp_startup_done(opaque_t arg, uchar_t result)
2983{
2984	fc_local_port_t *port = arg;
2985
2986	fp_attach_ulps(port, FC_CMD_ATTACH);
2987
2988	FP_DTRACE(FP_NHEAD1(2, 0), "fp_startup almost complete; port=%p", port);
2989}
2990
2991
2992/*
2993 * Perform ULP port attach
2994 */
2995static void
2996fp_ulp_port_attach(void *arg)
2997{
2998	fp_soft_attach_t *att = (fp_soft_attach_t *)arg;
2999	fc_local_port_t	 *port = att->att_port;
3000
3001	FP_DTRACE(FP_NHEAD1(1, 0), "port attach of"
3002	    " ULPs begin; port=%p, cmd=%x", port, att->att_cmd);
3003
3004	fctl_attach_ulps(att->att_port, att->att_cmd, &modlinkage);
3005
3006	if (att->att_need_pm_idle == B_TRUE) {
3007		fctl_idle_port(port);
3008	}
3009
3010	FP_DTRACE(FP_NHEAD1(1, 0), "port attach of"
3011	    " ULPs end; port=%p, cmd=%x", port, att->att_cmd);
3012
3013	mutex_enter(&att->att_port->fp_mutex);
3014	att->att_port->fp_ulp_attach = 0;
3015
3016	port->fp_task = port->fp_last_task;
3017	port->fp_last_task = FP_TASK_IDLE;
3018
3019	cv_signal(&att->att_port->fp_attach_cv);
3020
3021	mutex_exit(&att->att_port->fp_mutex);
3022
3023	kmem_free(att, sizeof (fp_soft_attach_t));
3024}
3025
3026/*
3027 * Entry point to funnel all requests down to FCAs
3028 */
3029static int
3030fp_sendcmd(fc_local_port_t *port, fp_cmd_t *cmd, opaque_t fca_handle)
3031{
3032	int rval;
3033
3034	mutex_enter(&port->fp_mutex);
3035	if (port->fp_statec_busy > 1 || (cmd->cmd_ulp_pkt != NULL &&
3036	    (port->fp_statec_busy || FC_PORT_STATE_MASK(port->fp_state) ==
3037	    FC_STATE_OFFLINE))) {
3038		/*
3039		 * This means there is more than one state change
3040		 * at this point of time - Since they are processed
3041		 * serially, any processing of the current one should
3042		 * be failed, failed and move up in processing the next
3043		 */
3044		cmd->cmd_pkt.pkt_state = FC_PKT_ELS_IN_PROGRESS;
3045		cmd->cmd_pkt.pkt_reason = FC_REASON_OFFLINE;
3046		if (cmd->cmd_job) {
3047			/*
3048			 * A state change that is going to be invalidated
3049			 * by another one already in the port driver's queue
3050			 * need not go up to all ULPs. This will minimize
3051			 * needless processing and ripples in ULP modules
3052			 */
3053			cmd->cmd_job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
3054		}
3055		mutex_exit(&port->fp_mutex);
3056		return (FC_STATEC_BUSY);
3057	}
3058
3059	if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
3060		cmd->cmd_pkt.pkt_state = FC_PKT_PORT_OFFLINE;
3061		cmd->cmd_pkt.pkt_reason = FC_REASON_OFFLINE;
3062		mutex_exit(&port->fp_mutex);
3063
3064		return (FC_OFFLINE);
3065	}
3066	mutex_exit(&port->fp_mutex);
3067
3068	rval = cmd->cmd_transport(fca_handle, &cmd->cmd_pkt);
3069	if (rval != FC_SUCCESS) {
3070		if (rval == FC_TRAN_BUSY) {
3071			cmd->cmd_retry_interval = fp_retry_delay;
3072			rval = fp_retry_cmd(&cmd->cmd_pkt);
3073			if (rval == FC_FAILURE) {
3074				cmd->cmd_pkt.pkt_state = FC_PKT_TRAN_BSY;
3075			}
3076		}
3077	}
3078
3079	return (rval);
3080}
3081
3082
3083/*
3084 * Each time a timeout kicks in, walk the wait queue, decrement the
3085 * the retry_interval, when the retry_interval becomes less than
3086 * or equal to zero, re-transport the command: If the re-transport
3087 * fails with BUSY, enqueue the command in the wait queue.
3088 *
3089 * In order to prevent looping forever because of commands enqueued
3090 * from within this function itself, save the current tail pointer
3091 * (in cur_tail) and exit the loop after serving this command.
3092 */
3093static void
3094fp_resendcmd(void *port_handle)
3095{
3096	int		rval;
3097	fc_local_port_t	*port;
3098	fp_cmd_t 	*cmd;
3099	fp_cmd_t	*cur_tail;
3100
3101	port = port_handle;
3102	mutex_enter(&port->fp_mutex);
3103	cur_tail = port->fp_wait_tail;
3104	mutex_exit(&port->fp_mutex);
3105
3106	while ((cmd = fp_deque_cmd(port)) != NULL) {
3107		cmd->cmd_retry_interval -= fp_retry_ticker;
3108		/* Check if we are detaching */
3109		if (port->fp_soft_state &
3110		    (FP_SOFT_IN_DETACH | FP_DETACH_INPROGRESS)) {
3111			cmd->cmd_pkt.pkt_state = FC_PKT_TRAN_ERROR;
3112			cmd->cmd_pkt.pkt_reason = 0;
3113			fp_iodone(cmd);
3114		} else if (cmd->cmd_retry_interval <= 0) {
3115			rval = cmd->cmd_transport(port->fp_fca_handle,
3116			    &cmd->cmd_pkt);
3117
3118			if (rval != FC_SUCCESS) {
3119				if (cmd->cmd_pkt.pkt_state == FC_PKT_TRAN_BSY) {
3120					if (--cmd->cmd_retry_count) {
3121						fp_enque_cmd(port, cmd);
3122						if (cmd == cur_tail) {
3123							break;
3124						}
3125						continue;
3126					}
3127					cmd->cmd_pkt.pkt_state =
3128					    FC_PKT_TRAN_BSY;
3129				} else {
3130					cmd->cmd_pkt.pkt_state =
3131					    FC_PKT_TRAN_ERROR;
3132				}
3133				cmd->cmd_pkt.pkt_reason = 0;
3134				fp_iodone(cmd);
3135			}
3136		} else {
3137			fp_enque_cmd(port, cmd);
3138		}
3139
3140		if (cmd == cur_tail) {
3141			break;
3142		}
3143	}
3144
3145	mutex_enter(&port->fp_mutex);
3146	if (port->fp_wait_head) {
3147		timeout_id_t tid;
3148
3149		mutex_exit(&port->fp_mutex);
3150		tid = timeout(fp_resendcmd, (caddr_t)port,
3151		    fp_retry_ticks);
3152		mutex_enter(&port->fp_mutex);
3153		port->fp_wait_tid = tid;
3154	} else {
3155		port->fp_wait_tid = NULL;
3156	}
3157	mutex_exit(&port->fp_mutex);
3158}
3159
3160
3161/*
3162 * Handle Local, Fabric, N_Port, Transport (whatever that means) BUSY here.
3163 *
3164 * Yes, as you can see below, cmd_retry_count is used here too.  That means
3165 * the retries for BUSY are less if there were transport failures (transport
3166 * failure means fca_transport failure). The goal is not to exceed overall
3167 * retries set in the cmd_retry_count (whatever may be the reason for retry)
3168 *
3169 * Return Values:
3170 *	FC_SUCCESS
3171 *	FC_FAILURE
3172 */
3173static int
3174fp_retry_cmd(fc_packet_t *pkt)
3175{
3176	fp_cmd_t *cmd;
3177
3178	cmd = pkt->pkt_ulp_private;
3179
3180	if (--cmd->cmd_retry_count) {
3181		fp_enque_cmd(cmd->cmd_port, cmd);
3182		return (FC_SUCCESS);
3183	} else {
3184		return (FC_FAILURE);
3185	}
3186}
3187
3188
3189/*
3190 * Queue up FC packet for deferred retry
3191 */
3192static void
3193fp_enque_cmd(fc_local_port_t *port, fp_cmd_t *cmd)
3194{
3195	timeout_id_t tid;
3196
3197	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3198
3199#ifdef	DEBUG
3200	fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, &cmd->cmd_pkt,
3201	    "Retrying ELS for %x", cmd->cmd_pkt.pkt_cmd_fhdr.d_id);
3202#endif
3203
3204	mutex_enter(&port->fp_mutex);
3205	if (port->fp_wait_tail) {
3206		port->fp_wait_tail->cmd_next = cmd;
3207		port->fp_wait_tail = cmd;
3208	} else {
3209		ASSERT(port->fp_wait_head == NULL);
3210		port->fp_wait_head = port->fp_wait_tail = cmd;
3211		if (port->fp_wait_tid == NULL) {
3212			mutex_exit(&port->fp_mutex);
3213			tid = timeout(fp_resendcmd, (caddr_t)port,
3214			    fp_retry_ticks);
3215			mutex_enter(&port->fp_mutex);
3216			port->fp_wait_tid = tid;
3217		}
3218	}
3219	mutex_exit(&port->fp_mutex);
3220}
3221
3222
3223/*
3224 * Handle all RJT codes
3225 */
3226static int
3227fp_handle_reject(fc_packet_t *pkt)
3228{
3229	int 		rval = FC_FAILURE;
3230	uchar_t		next_class;
3231	fp_cmd_t 	*cmd;
3232	fc_local_port_t *port;
3233
3234	cmd = pkt->pkt_ulp_private;
3235	port = cmd->cmd_port;
3236
3237	switch (pkt->pkt_state) {
3238	case FC_PKT_FABRIC_RJT:
3239	case FC_PKT_NPORT_RJT:
3240		if (pkt->pkt_reason == FC_REASON_CLASS_NOT_SUPP) {
3241			next_class = fp_get_nextclass(cmd->cmd_port,
3242			    FC_TRAN_CLASS(pkt->pkt_tran_flags));
3243
3244			if (next_class == FC_TRAN_CLASS_INVALID) {
3245				return (rval);
3246			}
3247			pkt->pkt_tran_flags = FC_TRAN_INTR | next_class;
3248			pkt->pkt_tran_type = FC_PKT_EXCHANGE;
3249
3250			rval = fp_sendcmd(cmd->cmd_port, cmd,
3251			    cmd->cmd_port->fp_fca_handle);
3252
3253			if (rval != FC_SUCCESS) {
3254				pkt->pkt_state = FC_PKT_TRAN_ERROR;
3255			}
3256		}
3257			break;
3258
3259	case FC_PKT_LS_RJT:
3260	case FC_PKT_BA_RJT:
3261		if ((pkt->pkt_reason == FC_REASON_LOGICAL_ERROR) ||
3262		    (pkt->pkt_reason == FC_REASON_LOGICAL_BSY)) {
3263			cmd->cmd_retry_interval = fp_retry_delay;
3264			rval = fp_retry_cmd(pkt);
3265		}
3266		break;
3267
3268	case FC_PKT_FS_RJT:
3269		if (pkt->pkt_reason == FC_REASON_FS_LOGICAL_BUSY) {
3270			cmd->cmd_retry_interval = fp_retry_delay;
3271			rval = fp_retry_cmd(pkt);
3272		}
3273		break;
3274
3275	case FC_PKT_LOCAL_RJT:
3276		if (pkt->pkt_reason == FC_REASON_QFULL) {
3277			cmd->cmd_retry_interval = fp_retry_delay;
3278			rval = fp_retry_cmd(pkt);
3279		}
3280		break;
3281
3282	default:
3283		FP_TRACE(FP_NHEAD1(1, 0),
3284		    "fp_handle_reject(): Invalid pkt_state");
3285		break;
3286	}
3287
3288	return (rval);
3289}
3290
3291
3292/*
3293 * Return the next class of service supported by the FCA
3294 */
3295static uchar_t
3296fp_get_nextclass(fc_local_port_t *port, uchar_t cur_class)
3297{
3298	uchar_t next_class;
3299
3300	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3301
3302	switch (cur_class) {
3303	case FC_TRAN_CLASS_INVALID:
3304		if (port->fp_cos & FC_NS_CLASS1) {
3305			next_class = FC_TRAN_CLASS1;
3306			break;
3307		}
3308		/* FALLTHROUGH */
3309
3310	case FC_TRAN_CLASS1:
3311		if (port->fp_cos & FC_NS_CLASS2) {
3312			next_class = FC_TRAN_CLASS2;
3313			break;
3314		}
3315		/* FALLTHROUGH */
3316
3317	case FC_TRAN_CLASS2:
3318		if (port->fp_cos & FC_NS_CLASS3) {
3319			next_class = FC_TRAN_CLASS3;
3320			break;
3321		}
3322		/* FALLTHROUGH */
3323
3324	case FC_TRAN_CLASS3:
3325	default:
3326		next_class = FC_TRAN_CLASS_INVALID;
3327		break;
3328	}
3329
3330	return (next_class);
3331}
3332
3333
3334/*
3335 * Determine if a class of service is supported by the FCA
3336 */
3337static int
3338fp_is_class_supported(uint32_t cos, uchar_t tran_class)
3339{
3340	int rval;
3341
3342	switch (tran_class) {
3343	case FC_TRAN_CLASS1:
3344		if (cos & FC_NS_CLASS1) {
3345			rval = FC_SUCCESS;
3346		} else {
3347			rval = FC_FAILURE;
3348		}
3349		break;
3350
3351	case FC_TRAN_CLASS2:
3352		if (cos & FC_NS_CLASS2) {
3353			rval = FC_SUCCESS;
3354		} else {
3355			rval = FC_FAILURE;
3356		}
3357		break;
3358
3359	case FC_TRAN_CLASS3:
3360		if (cos & FC_NS_CLASS3) {
3361			rval = FC_SUCCESS;
3362		} else {
3363			rval = FC_FAILURE;
3364		}
3365		break;
3366
3367	default:
3368		rval = FC_FAILURE;
3369		break;
3370	}
3371
3372	return (rval);
3373}
3374
3375
3376/*
3377 * Dequeue FC packet for retry
3378 */
3379static fp_cmd_t *
3380fp_deque_cmd(fc_local_port_t *port)
3381{
3382	fp_cmd_t *cmd;
3383
3384	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3385
3386	mutex_enter(&port->fp_mutex);
3387
3388	if (port->fp_wait_head == NULL) {
3389		/*
3390		 * To avoid races, NULL the fp_wait_tid as
3391		 * we are about to exit the timeout thread.
3392		 */
3393		port->fp_wait_tid = NULL;
3394		mutex_exit(&port->fp_mutex);
3395		return (NULL);
3396	}
3397
3398	cmd = port->fp_wait_head;
3399	port->fp_wait_head = cmd->cmd_next;
3400	cmd->cmd_next = NULL;
3401
3402	if (port->fp_wait_head == NULL) {
3403		port->fp_wait_tail = NULL;
3404	}
3405	mutex_exit(&port->fp_mutex);
3406
3407	return (cmd);
3408}
3409
3410
3411/*
3412 * Wait for job completion
3413 */
3414static void
3415fp_jobwait(job_request_t *job)
3416{
3417	sema_p(&job->job_port_sema);
3418}
3419
3420
3421/*
3422 * Convert FC packet state to FC errno
3423 */
3424int
3425fp_state_to_rval(uchar_t state)
3426{
3427	int count;
3428
3429	for (count = 0; count < sizeof (fp_xlat) /
3430	sizeof (fp_xlat[0]); count++) {
3431		if (fp_xlat[count].xlat_state == state) {
3432			return (fp_xlat[count].xlat_rval);
3433		}
3434	}
3435
3436	return (FC_FAILURE);
3437}
3438
3439
3440/*
3441 * For Synchronous I/O requests, the caller is
3442 * expected to do fctl_jobdone(if necessary)
3443 *
3444 * We want to preserve at least one failure in the
3445 * job_result if it happens.
3446 *
3447 */
3448static void
3449fp_iodone(fp_cmd_t *cmd)
3450{
3451	fc_packet_t		*ulp_pkt = cmd->cmd_ulp_pkt;
3452	job_request_t 		*job = cmd->cmd_job;
3453	fc_remote_port_t 	*pd = cmd->cmd_pkt.pkt_pd;
3454
3455	ASSERT(job != NULL);
3456	ASSERT(cmd->cmd_port != NULL);
3457	ASSERT(&cmd->cmd_pkt != NULL);
3458
3459	mutex_enter(&job->job_mutex);
3460	if (job->job_result == FC_SUCCESS) {
3461		job->job_result = fp_state_to_rval(cmd->cmd_pkt.pkt_state);
3462	}
3463	mutex_exit(&job->job_mutex);
3464
3465	if (pd) {
3466		mutex_enter(&pd->pd_mutex);
3467		pd->pd_flags = PD_IDLE;
3468		mutex_exit(&pd->pd_mutex);
3469	}
3470
3471	if (ulp_pkt) {
3472		if (pd && cmd->cmd_flags & FP_CMD_DELDEV_ON_ERROR &&
3473		    FP_IS_PKT_ERROR(ulp_pkt)) {
3474			fc_local_port_t 	*port;
3475			fc_remote_node_t	*node;
3476
3477			port = cmd->cmd_port;
3478
3479			mutex_enter(&pd->pd_mutex);
3480			pd->pd_state = PORT_DEVICE_INVALID;
3481			pd->pd_ref_count--;
3482			node = pd->pd_remote_nodep;
3483			mutex_exit(&pd->pd_mutex);
3484
3485			ASSERT(node != NULL);
3486			ASSERT(port != NULL);
3487
3488			if (fctl_destroy_remote_port(port, pd) == 0) {
3489				fctl_destroy_remote_node(node);
3490			}
3491
3492			ulp_pkt->pkt_pd = NULL;
3493		}
3494
3495		ulp_pkt->pkt_comp(ulp_pkt);
3496	}
3497
3498	fp_free_pkt(cmd);
3499	fp_jobdone(job);
3500}
3501
3502
3503/*
3504 * Job completion handler
3505 */
3506static void
3507fp_jobdone(job_request_t *job)
3508{
3509	mutex_enter(&job->job_mutex);
3510	ASSERT(job->job_counter > 0);
3511
3512	if (--job->job_counter != 0) {
3513		mutex_exit(&job->job_mutex);
3514		return;
3515	}
3516
3517	if (job->job_ulp_pkts) {
3518		ASSERT(job->job_ulp_listlen > 0);
3519		kmem_free(job->job_ulp_pkts,
3520		    sizeof (fc_packet_t *) * job->job_ulp_listlen);
3521	}
3522
3523	if (job->job_flags & JOB_TYPE_FP_ASYNC) {
3524		mutex_exit(&job->job_mutex);
3525		fctl_jobdone(job);
3526	} else {
3527		mutex_exit(&job->job_mutex);
3528		sema_v(&job->job_port_sema);
3529	}
3530}
3531
3532
3533/*
3534 * Try to perform shutdown of a port during a detach. No return
3535 * value since the detach should not fail because the port shutdown
3536 * failed.
3537 */
3538static void
3539fp_port_shutdown(fc_local_port_t *port, job_request_t *job)
3540{
3541	int			index;
3542	int			count;
3543	int			flags;
3544	fp_cmd_t 		*cmd;
3545	struct pwwn_hash 	*head;
3546	fc_remote_port_t 	*pd;
3547
3548	ASSERT(MUTEX_HELD(&port->fp_mutex));
3549
3550	job->job_result = FC_SUCCESS;
3551
3552	if (port->fp_taskq) {
3553		/*
3554		 * We must release the mutex here to ensure that other
3555		 * potential jobs can complete their processing.  Many
3556		 * also need this mutex.
3557		 */
3558		mutex_exit(&port->fp_mutex);
3559		taskq_wait(port->fp_taskq);
3560		mutex_enter(&port->fp_mutex);
3561	}
3562
3563	if (port->fp_offline_tid) {
3564		timeout_id_t tid;
3565
3566		tid = port->fp_offline_tid;
3567		port->fp_offline_tid = NULL;
3568		mutex_exit(&port->fp_mutex);
3569		(void) untimeout(tid);
3570		mutex_enter(&port->fp_mutex);
3571	}
3572
3573	if (port->fp_wait_tid) {
3574		timeout_id_t tid;
3575
3576		tid = port->fp_wait_tid;
3577		port->fp_wait_tid = NULL;
3578		mutex_exit(&port->fp_mutex);
3579		(void) untimeout(tid);
3580	} else {
3581		mutex_exit(&port->fp_mutex);
3582	}
3583
3584	/*
3585	 * While we cancel the timeout, let's also return the
3586	 * the outstanding requests back to the callers.
3587	 */
3588	while ((cmd = fp_deque_cmd(port)) != NULL) {
3589		ASSERT(cmd->cmd_job != NULL);
3590		cmd->cmd_job->job_result = FC_OFFLINE;
3591		fp_iodone(cmd);
3592	}
3593
3594	/*
3595	 * Gracefully LOGO with all the devices logged in.
3596	 */
3597	mutex_enter(&port->fp_mutex);
3598
3599	for (count = index = 0; index < pwwn_table_size; index++) {
3600		head = &port->fp_pwwn_table[index];
3601		pd = head->pwwn_head;
3602		while (pd != NULL) {
3603			mutex_enter(&pd->pd_mutex);
3604			if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
3605				count++;
3606			}
3607			mutex_exit(&pd->pd_mutex);
3608			pd = pd->pd_wwn_hnext;
3609		}
3610	}
3611
3612	if (job->job_flags & JOB_TYPE_FP_ASYNC) {
3613		flags = job->job_flags;
3614		job->job_flags &= ~JOB_TYPE_FP_ASYNC;
3615	} else {
3616		flags = 0;
3617	}
3618	if (count) {
3619		job->job_counter = count;
3620
3621		for (index = 0; index < pwwn_table_size; index++) {
3622			head = &port->fp_pwwn_table[index];
3623			pd = head->pwwn_head;
3624			while (pd != NULL) {
3625				mutex_enter(&pd->pd_mutex);
3626				if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
3627					ASSERT(pd->pd_login_count > 0);
3628					/*
3629					 * Force the counter to ONE in order
3630					 * for us to really send LOGO els.
3631					 */
3632					pd->pd_login_count = 1;
3633					mutex_exit(&pd->pd_mutex);
3634					mutex_exit(&port->fp_mutex);
3635					(void) fp_logout(port, pd, job);
3636					mutex_enter(&port->fp_mutex);
3637				} else {
3638					mutex_exit(&pd->pd_mutex);
3639				}
3640				pd = pd->pd_wwn_hnext;
3641			}
3642		}
3643		mutex_exit(&port->fp_mutex);
3644		fp_jobwait(job);
3645	} else {
3646		mutex_exit(&port->fp_mutex);
3647	}
3648
3649	if (job->job_result != FC_SUCCESS) {
3650		FP_TRACE(FP_NHEAD1(9, 0),
3651		    "Can't logout all devices. Proceeding with"
3652		    " port shutdown");
3653		job->job_result = FC_SUCCESS;
3654	}
3655
3656	fctl_destroy_all_remote_ports(port);
3657
3658	mutex_enter(&port->fp_mutex);
3659	if (FC_IS_TOP_SWITCH(port->fp_topology)) {
3660		mutex_exit(&port->fp_mutex);
3661		fp_ns_fini(port, job);
3662	} else {
3663		mutex_exit(&port->fp_mutex);
3664	}
3665
3666	if (flags) {
3667		job->job_flags = flags;
3668	}
3669
3670	mutex_enter(&port->fp_mutex);
3671
3672}
3673
3674
3675/*
3676 * Build the port driver's data structures based on the AL_PA list
3677 */
3678static void
3679fp_get_loopmap(fc_local_port_t *port, job_request_t *job)
3680{
3681	int 			rval;
3682	int			flag;
3683	int 			count;
3684	uint32_t		d_id;
3685	fc_remote_port_t 	*pd;
3686	fc_lilpmap_t 		*lilp_map;
3687
3688	ASSERT(MUTEX_HELD(&port->fp_mutex));
3689
3690	if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
3691		job->job_result = FC_OFFLINE;
3692		mutex_exit(&port->fp_mutex);
3693		fp_jobdone(job);
3694		mutex_enter(&port->fp_mutex);
3695		return;
3696	}
3697
3698	if (port->fp_lilp_map.lilp_length == 0) {
3699		mutex_exit(&port->fp_mutex);
3700		job->job_result = FC_NO_MAP;
3701		fp_jobdone(job);
3702		mutex_enter(&port->fp_mutex);
3703		return;
3704	}
3705	mutex_exit(&port->fp_mutex);
3706
3707	lilp_map = &port->fp_lilp_map;
3708	job->job_counter = lilp_map->lilp_length;
3709
3710	if (job->job_code == JOB_PORT_GETMAP_PLOGI_ALL) {
3711		flag = FP_CMD_PLOGI_RETAIN;
3712	} else {
3713		flag = FP_CMD_PLOGI_DONT_CARE;
3714	}
3715
3716	for (count = 0; count < lilp_map->lilp_length; count++) {
3717		d_id = lilp_map->lilp_alpalist[count];
3718
3719		if (d_id == (lilp_map->lilp_myalpa & 0xFF)) {
3720			fp_jobdone(job);
3721			continue;
3722		}
3723
3724		pd = fctl_get_remote_port_by_did(port, d_id);
3725		if (pd) {
3726			mutex_enter(&pd->pd_mutex);
3727			if (flag == FP_CMD_PLOGI_DONT_CARE ||
3728			    pd->pd_state == PORT_DEVICE_LOGGED_IN) {
3729				mutex_exit(&pd->pd_mutex);
3730				fp_jobdone(job);
3731				continue;
3732			}
3733			mutex_exit(&pd->pd_mutex);
3734		}
3735
3736		rval = fp_port_login(port, d_id, job, flag,
3737		    KM_SLEEP, pd, NULL);
3738		if (rval != FC_SUCCESS) {
3739			fp_jobdone(job);
3740		}
3741	}
3742
3743	mutex_enter(&port->fp_mutex);
3744}
3745
3746
3747/*
3748 * Perform loop ONLINE processing
3749 */
3750static void
3751fp_loop_online(fc_local_port_t *port, job_request_t *job, int orphan)
3752{
3753	int			count;
3754	int			rval;
3755	uint32_t		d_id;
3756	uint32_t		listlen;
3757	fc_lilpmap_t		*lilp_map;
3758	fc_remote_port_t	*pd;
3759	fc_portmap_t		*changelist;
3760
3761	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3762
3763	FP_TRACE(FP_NHEAD1(1, 0), "fp_loop_online begin; port=%p, job=%p",
3764	    port, job);
3765
3766	lilp_map = &port->fp_lilp_map;
3767
3768	if (lilp_map->lilp_length) {
3769		mutex_enter(&port->fp_mutex);
3770		if (port->fp_soft_state & FP_SOFT_IN_FCA_RESET) {
3771			port->fp_soft_state &= ~FP_SOFT_IN_FCA_RESET;
3772			mutex_exit(&port->fp_mutex);
3773			delay(drv_usectohz(PLDA_RR_TOV * 1000 * 1000));
3774		} else {
3775			mutex_exit(&port->fp_mutex);
3776		}
3777
3778		job->job_counter = lilp_map->lilp_length;
3779
3780		for (count = 0; count < lilp_map->lilp_length; count++) {
3781			d_id = lilp_map->lilp_alpalist[count];
3782
3783			if (d_id == (lilp_map->lilp_myalpa & 0xFF)) {
3784				fp_jobdone(job);
3785				continue;
3786			}
3787
3788			pd = fctl_get_remote_port_by_did(port, d_id);
3789			if (pd != NULL) {
3790#ifdef	DEBUG
3791				mutex_enter(&pd->pd_mutex);
3792				if (pd->pd_recepient == PD_PLOGI_INITIATOR) {
3793					ASSERT(pd->pd_type != PORT_DEVICE_OLD);
3794				}
3795				mutex_exit(&pd->pd_mutex);
3796#endif
3797				fp_jobdone(job);
3798				continue;
3799			}
3800
3801			rval = fp_port_login(port, d_id, job,
3802			    FP_CMD_PLOGI_DONT_CARE, KM_SLEEP, pd, NULL);
3803
3804			if (rval != FC_SUCCESS) {
3805				fp_jobdone(job);
3806			}
3807		}
3808		fp_jobwait(job);
3809	}
3810	listlen = 0;
3811	changelist = NULL;
3812
3813	if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
3814		mutex_enter(&port->fp_mutex);
3815		ASSERT(port->fp_statec_busy > 0);
3816		if (port->fp_statec_busy == 1) {
3817			mutex_exit(&port->fp_mutex);
3818			fctl_fillout_map(port, &changelist, &listlen,
3819			    1, 0, orphan);
3820
3821			mutex_enter(&port->fp_mutex);
3822			if (port->fp_lilp_map.lilp_magic < MAGIC_LIRP) {
3823				ASSERT(port->fp_total_devices == 0);
3824				port->fp_total_devices = port->fp_dev_count;
3825			}
3826		} else {
3827			job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
3828		}
3829		mutex_exit(&port->fp_mutex);
3830	}
3831
3832	if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
3833		(void) fp_ulp_statec_cb(port, FC_STATE_ONLINE, changelist,
3834		    listlen, listlen, KM_SLEEP);
3835	} else {
3836		mutex_enter(&port->fp_mutex);
3837		if (--port->fp_statec_busy == 0) {
3838			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
3839		}
3840		ASSERT(changelist == NULL && listlen == 0);
3841		mutex_exit(&port->fp_mutex);
3842	}
3843
3844	FP_TRACE(FP_NHEAD1(1, 0), "fp_loop_online end; port=%p, job=%p",
3845	    port, job);
3846}
3847
3848
3849/*
3850 * Get an Arbitrated Loop map from the underlying FCA
3851 */
3852static int
3853fp_get_lilpmap(fc_local_port_t *port, fc_lilpmap_t *lilp_map)
3854{
3855	int rval;
3856
3857	FP_TRACE(FP_NHEAD1(1, 0), "fp_get_lilpmap Begin; port=%p, map=%p",
3858	    port, lilp_map);
3859
3860	bzero((caddr_t)lilp_map, sizeof (fc_lilpmap_t));
3861	rval = port->fp_fca_tran->fca_getmap(port->fp_fca_handle, lilp_map);
3862	lilp_map->lilp_magic &= 0xFF;	/* Ignore upper byte */
3863
3864	if (rval != FC_SUCCESS) {
3865		rval = FC_NO_MAP;
3866	} else if (lilp_map->lilp_length == 0 &&
3867	    (lilp_map->lilp_magic >= MAGIC_LISM &&
3868	    lilp_map->lilp_magic < MAGIC_LIRP)) {
3869		uchar_t lilp_length;
3870
3871		/*
3872		 * Since the map length is zero, provide all
3873		 * the valid AL_PAs for NL_ports discovery.
3874		 */
3875		lilp_length = sizeof (fp_valid_alpas) /
3876		    sizeof (fp_valid_alpas[0]);
3877		lilp_map->lilp_length = lilp_length;
3878		bcopy(fp_valid_alpas, lilp_map->lilp_alpalist,
3879		    lilp_length);
3880	} else {
3881		rval = fp_validate_lilp_map(lilp_map);
3882
3883		if (rval == FC_SUCCESS) {
3884			mutex_enter(&port->fp_mutex);
3885			port->fp_total_devices = lilp_map->lilp_length - 1;
3886			mutex_exit(&port->fp_mutex);
3887		}
3888	}
3889
3890	mutex_enter(&port->fp_mutex);
3891	if (rval != FC_SUCCESS && !(port->fp_soft_state & FP_SOFT_BAD_LINK)) {
3892		port->fp_soft_state |= FP_SOFT_BAD_LINK;
3893		mutex_exit(&port->fp_mutex);
3894
3895		if (port->fp_fca_tran->fca_reset(port->fp_fca_handle,
3896		    FC_FCA_RESET_CORE) != FC_SUCCESS) {
3897			FP_TRACE(FP_NHEAD1(9, 0),
3898			    "FCA reset failed after LILP map was found"
3899			    " to be invalid");
3900		}
3901	} else if (rval == FC_SUCCESS) {
3902		port->fp_soft_state &= ~FP_SOFT_BAD_LINK;
3903		mutex_exit(&port->fp_mutex);
3904	} else {
3905		mutex_exit(&port->fp_mutex);
3906	}
3907
3908	FP_TRACE(FP_NHEAD1(1, 0), "fp_get_lilpmap End; port=%p, map=%p", port,
3909	    lilp_map);
3910
3911	return (rval);
3912}
3913
3914
3915/*
3916 * Perform Fabric Login:
3917 *
3918 * Return Values:
3919 *		FC_SUCCESS
3920 *		FC_FAILURE
3921 *		FC_NOMEM
3922 *		FC_TRANSPORT_ERROR
3923 *		and a lot others defined in fc_error.h
3924 */
3925static int
3926fp_fabric_login(fc_local_port_t *port, uint32_t s_id, job_request_t *job,
3927    int flag, int sleep)
3928{
3929	int		rval;
3930	fp_cmd_t 	*cmd;
3931	uchar_t 	class;
3932
3933	ASSERT(!MUTEX_HELD(&port->fp_mutex));
3934
3935	FP_TRACE(FP_NHEAD1(1, 0), "fp_fabric_login Begin; port=%p, job=%p",
3936	    port, job);
3937
3938	class = fp_get_nextclass(port, FC_TRAN_CLASS_INVALID);
3939	if (class == FC_TRAN_CLASS_INVALID) {
3940		return (FC_ELS_BAD);
3941	}
3942
3943	cmd = fp_alloc_pkt(port, sizeof (la_els_logi_t),
3944	    sizeof (la_els_logi_t), sleep, NULL);
3945	if (cmd == NULL) {
3946		return (FC_NOMEM);
3947	}
3948
3949	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
3950	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
3951	cmd->cmd_flags = flag;
3952	cmd->cmd_retry_count = fp_retry_count;
3953	cmd->cmd_ulp_pkt = NULL;
3954
3955	fp_xlogi_init(port, cmd, s_id, 0xFFFFFE, fp_flogi_intr,
3956	    job, LA_ELS_FLOGI);
3957
3958	rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
3959	if (rval != FC_SUCCESS) {
3960		fp_free_pkt(cmd);
3961	}
3962
3963	FP_TRACE(FP_NHEAD1(1, 0), "fp_fabric_login End; port=%p, job=%p",
3964	    port, job);
3965
3966	return (rval);
3967}
3968
3969
3970/*
3971 * In some scenarios such as private loop device discovery period
3972 * the fc_remote_port_t data structure isn't allocated. The allocation
3973 * is done when the PLOGI is successful. In some other scenarios
3974 * such as Fabric topology, the fc_remote_port_t is already created
3975 * and initialized with appropriate values (as the NS provides
3976 * them)
3977 */
3978static int
3979fp_port_login(fc_local_port_t *port, uint32_t d_id, job_request_t *job,
3980    int cmd_flag, int sleep, fc_remote_port_t *pd, fc_packet_t *ulp_pkt)
3981{
3982	uchar_t class;
3983	fp_cmd_t *cmd;
3984	uint32_t src_id;
3985	fc_remote_port_t *tmp_pd;
3986	int relogin;
3987	int found = 0;
3988
3989#ifdef	DEBUG
3990	if (pd == NULL) {
3991		ASSERT(fctl_get_remote_port_by_did(port, d_id) == NULL);
3992	}
3993#endif
3994	ASSERT(job->job_counter > 0);
3995
3996	class = fp_get_nextclass(port, FC_TRAN_CLASS_INVALID);
3997	if (class == FC_TRAN_CLASS_INVALID) {
3998		return (FC_ELS_BAD);
3999	}
4000
4001	mutex_enter(&port->fp_mutex);
4002	tmp_pd = fctl_lookup_pd_by_did(port, d_id);
4003	mutex_exit(&port->fp_mutex);
4004
4005	relogin = 1;
4006	if (tmp_pd) {
4007		mutex_enter(&tmp_pd->pd_mutex);
4008		if ((tmp_pd->pd_aux_flags & PD_DISABLE_RELOGIN) &&
4009		    !(tmp_pd->pd_aux_flags & PD_LOGGED_OUT)) {
4010			tmp_pd->pd_state = PORT_DEVICE_LOGGED_IN;
4011			relogin = 0;
4012		}
4013		mutex_exit(&tmp_pd->pd_mutex);
4014	}
4015
4016	if (!relogin) {
4017		mutex_enter(&tmp_pd->pd_mutex);
4018		if (tmp_pd->pd_state == PORT_DEVICE_LOGGED_IN) {
4019			cmd_flag |= FP_CMD_PLOGI_RETAIN;
4020		}
4021		mutex_exit(&tmp_pd->pd_mutex);
4022
4023		cmd = fp_alloc_pkt(port, sizeof (la_els_adisc_t),
4024		    sizeof (la_els_adisc_t), sleep, tmp_pd);
4025		if (cmd == NULL) {
4026			return (FC_NOMEM);
4027		}
4028
4029		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
4030		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
4031		cmd->cmd_flags = cmd_flag;
4032		cmd->cmd_retry_count = fp_retry_count;
4033		cmd->cmd_ulp_pkt = ulp_pkt;
4034
4035		mutex_enter(&port->fp_mutex);
4036		mutex_enter(&tmp_pd->pd_mutex);
4037		fp_adisc_init(cmd, job);
4038		mutex_exit(&tmp_pd->pd_mutex);
4039		mutex_exit(&port->fp_mutex);
4040
4041		cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_adisc_t);
4042		cmd->cmd_pkt.pkt_rsplen = sizeof (la_els_adisc_t);
4043
4044	} else {
4045		cmd = fp_alloc_pkt(port, sizeof (la_els_logi_t),
4046		    sizeof (la_els_logi_t), sleep, pd);
4047		if (cmd == NULL) {
4048			return (FC_NOMEM);
4049		}
4050
4051		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
4052		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
4053		cmd->cmd_flags = cmd_flag;
4054		cmd->cmd_retry_count = fp_retry_count;
4055		cmd->cmd_ulp_pkt = ulp_pkt;
4056
4057		mutex_enter(&port->fp_mutex);
4058		src_id = port->fp_port_id.port_id;
4059		mutex_exit(&port->fp_mutex);
4060
4061		fp_xlogi_init(port, cmd, src_id, d_id, fp_plogi_intr,
4062		    job, LA_ELS_PLOGI);
4063	}
4064
4065	if (pd) {
4066		mutex_enter(&pd->pd_mutex);
4067		pd->pd_flags = PD_ELS_IN_PROGRESS;
4068		mutex_exit(&pd->pd_mutex);
4069	}
4070
4071	/* npiv check to make sure we don't log into ourself */
4072	if (relogin && (port->fp_topology == FC_TOP_FABRIC)) {
4073		if ((d_id & 0xffff00) ==
4074		    (port->fp_port_id.port_id & 0xffff00)) {
4075			found = 1;
4076		}
4077	}
4078
4079	if (found ||
4080	    (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS)) {
4081		if (found) {
4082			fc_packet_t *pkt = &cmd->cmd_pkt;
4083			pkt->pkt_state = FC_PKT_NPORT_RJT;
4084		}
4085		if (pd) {
4086			mutex_enter(&pd->pd_mutex);
4087			pd->pd_flags = PD_IDLE;
4088			mutex_exit(&pd->pd_mutex);
4089		}
4090
4091		if (ulp_pkt) {
4092			fc_packet_t *pkt = &cmd->cmd_pkt;
4093
4094			ulp_pkt->pkt_state = pkt->pkt_state;
4095			ulp_pkt->pkt_reason = pkt->pkt_reason;
4096			ulp_pkt->pkt_action = pkt->pkt_action;
4097			ulp_pkt->pkt_expln = pkt->pkt_expln;
4098		}
4099
4100		fp_iodone(cmd);
4101	}
4102
4103	return (FC_SUCCESS);
4104}
4105
4106
4107/*
4108 * Register the LOGIN parameters with a port device
4109 */
4110static void
4111fp_register_login(ddi_acc_handle_t *handle, fc_remote_port_t *pd,
4112    la_els_logi_t *acc, uchar_t class)
4113{
4114	fc_remote_node_t 	*node;
4115
4116	ASSERT(pd != NULL);
4117
4118	mutex_enter(&pd->pd_mutex);
4119	node = pd->pd_remote_nodep;
4120	if (pd->pd_login_count == 0) {
4121		pd->pd_login_count++;
4122	}
4123
4124	if (handle) {
4125		ddi_rep_get8(*handle, (uint8_t *)&pd->pd_csp,
4126		    (uint8_t *)&acc->common_service,
4127		    sizeof (acc->common_service), DDI_DEV_AUTOINCR);
4128		ddi_rep_get8(*handle, (uint8_t *)&pd->pd_clsp1,
4129		    (uint8_t *)&acc->class_1, sizeof (acc->class_1),
4130		    DDI_DEV_AUTOINCR);
4131		ddi_rep_get8(*handle, (uint8_t *)&pd->pd_clsp2,
4132		    (uint8_t *)&acc->class_2, sizeof (acc->class_2),
4133		    DDI_DEV_AUTOINCR);
4134		ddi_rep_get8(*handle, (uint8_t *)&pd->pd_clsp3,
4135		    (uint8_t *)&acc->class_3, sizeof (acc->class_3),
4136		    DDI_DEV_AUTOINCR);
4137	} else {
4138		pd->pd_csp = acc->common_service;
4139		pd->pd_clsp1 = acc->class_1;
4140		pd->pd_clsp2 = acc->class_2;
4141		pd->pd_clsp3 = acc->class_3;
4142	}
4143
4144	pd->pd_state = PORT_DEVICE_LOGGED_IN;
4145	pd->pd_login_class = class;
4146	mutex_exit(&pd->pd_mutex);
4147
4148#ifndef	__lock_lint
4149	ASSERT(fctl_get_remote_port_by_did(pd->pd_port,
4150	    pd->pd_port_id.port_id) == pd);
4151#endif
4152
4153	mutex_enter(&node->fd_mutex);
4154	if (handle) {
4155		ddi_rep_get8(*handle, (uint8_t *)node->fd_vv,
4156		    (uint8_t *)acc->vendor_version, sizeof (node->fd_vv),
4157		    DDI_DEV_AUTOINCR);
4158	} else {
4159		bcopy(acc->vendor_version, node->fd_vv, sizeof (node->fd_vv));
4160	}
4161	mutex_exit(&node->fd_mutex);
4162}
4163
4164
4165/*
4166 * Mark the remote port as OFFLINE
4167 */
4168static void
4169fp_remote_port_offline(fc_remote_port_t *pd)
4170{
4171	ASSERT(MUTEX_HELD(&pd->pd_mutex));
4172	if (pd->pd_login_count &&
4173	    ((pd->pd_aux_flags & PD_DISABLE_RELOGIN) == 0)) {
4174		bzero((caddr_t)&pd->pd_csp, sizeof (struct common_service));
4175		bzero((caddr_t)&pd->pd_clsp1, sizeof (struct service_param));
4176		bzero((caddr_t)&pd->pd_clsp2, sizeof (struct service_param));
4177		bzero((caddr_t)&pd->pd_clsp3, sizeof (struct service_param));
4178		pd->pd_login_class = 0;
4179	}
4180	pd->pd_type = PORT_DEVICE_OLD;
4181	pd->pd_flags = PD_IDLE;
4182	fctl_tc_reset(&pd->pd_logo_tc);
4183}
4184
4185
4186/*
4187 * Deregistration of a port device
4188 */
4189static void
4190fp_unregister_login(fc_remote_port_t *pd)
4191{
4192	fc_remote_node_t *node;
4193
4194	ASSERT(pd != NULL);
4195
4196	mutex_enter(&pd->pd_mutex);
4197	pd->pd_login_count = 0;
4198	bzero((caddr_t)&pd->pd_csp, sizeof (struct common_service));
4199	bzero((caddr_t)&pd->pd_clsp1, sizeof (struct service_param));
4200	bzero((caddr_t)&pd->pd_clsp2, sizeof (struct service_param));
4201	bzero((caddr_t)&pd->pd_clsp3, sizeof (struct service_param));
4202
4203	pd->pd_state = PORT_DEVICE_VALID;
4204	pd->pd_login_class = 0;
4205	node = pd->pd_remote_nodep;
4206	mutex_exit(&pd->pd_mutex);
4207
4208	mutex_enter(&node->fd_mutex);
4209	bzero(node->fd_vv, sizeof (node->fd_vv));
4210	mutex_exit(&node->fd_mutex);
4211}
4212
4213
4214/*
4215 * Handle OFFLINE state of an FCA port
4216 */
4217static void
4218fp_port_offline(fc_local_port_t *port, int notify)
4219{
4220	int			index;
4221	int			statec;
4222	timeout_id_t		tid;
4223	struct pwwn_hash 	*head;
4224	fc_remote_port_t 	*pd;
4225
4226	ASSERT(MUTEX_HELD(&port->fp_mutex));
4227
4228	for (index = 0; index < pwwn_table_size; index++) {
4229		head = &port->fp_pwwn_table[index];
4230		pd = head->pwwn_head;
4231		while (pd != NULL) {
4232			mutex_enter(&pd->pd_mutex);
4233			fp_remote_port_offline(pd);
4234			fctl_delist_did_table(port, pd);
4235			mutex_exit(&pd->pd_mutex);
4236			pd = pd->pd_wwn_hnext;
4237		}
4238	}
4239	port->fp_total_devices = 0;
4240
4241	statec = 0;
4242	if (notify) {
4243		/*
4244		 * Decrement the statec busy counter as we
4245		 * are almost done with handling the state
4246		 * change
4247		 */
4248		ASSERT(port->fp_statec_busy > 0);
4249		if (--port->fp_statec_busy == 0) {
4250			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
4251		}
4252		mutex_exit(&port->fp_mutex);
4253		(void) fp_ulp_statec_cb(port, FC_STATE_OFFLINE, NULL,
4254		    0, 0, KM_SLEEP);
4255		mutex_enter(&port->fp_mutex);
4256
4257		if (port->fp_statec_busy) {
4258			statec++;
4259		}
4260	} else if (port->fp_statec_busy > 1) {
4261		statec++;
4262	}
4263
4264	if ((tid = port->fp_offline_tid) != NULL) {
4265		mutex_exit(&port->fp_mutex);
4266		(void) untimeout(tid);
4267		mutex_enter(&port->fp_mutex);
4268	}
4269
4270	if (!statec) {
4271		port->fp_offline_tid = timeout(fp_offline_timeout,
4272		    (caddr_t)port, fp_offline_ticks);
4273	}
4274}
4275
4276
4277/*
4278 * Offline devices and send up a state change notification to ULPs
4279 */
4280static void
4281fp_offline_timeout(void *port_handle)
4282{
4283	int		ret;
4284	fc_local_port_t *port = port_handle;
4285	uint32_t	listlen = 0;
4286	fc_portmap_t 	*changelist = NULL;
4287
4288	mutex_enter(&port->fp_mutex);
4289
4290	if ((FC_PORT_STATE_MASK(port->fp_state) != FC_STATE_OFFLINE) ||
4291	    (port->fp_soft_state &
4292	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN)) ||
4293	    port->fp_dev_count == 0 || port->fp_statec_busy) {
4294		port->fp_offline_tid = NULL;
4295		mutex_exit(&port->fp_mutex);
4296		return;
4297	}
4298
4299	mutex_exit(&port->fp_mutex);
4300
4301	FP_TRACE(FP_NHEAD2(9, 0), "OFFLINE timeout");
4302
4303	if (port->fp_options & FP_CORE_ON_OFFLINE_TIMEOUT) {
4304		if ((ret = port->fp_fca_tran->fca_reset(port->fp_fca_handle,
4305		    FC_FCA_CORE)) != FC_SUCCESS) {
4306			FP_TRACE(FP_NHEAD1(9, ret),
4307			    "Failed to force adapter dump");
4308		} else {
4309			FP_TRACE(FP_NHEAD1(9, 0),
4310			    "Forced adapter dump successfully");
4311		}
4312	} else if (port->fp_options & FP_RESET_CORE_ON_OFFLINE_TIMEOUT) {
4313		if ((ret = port->fp_fca_tran->fca_reset(port->fp_fca_handle,
4314		    FC_FCA_RESET_CORE)) != FC_SUCCESS) {
4315			FP_TRACE(FP_NHEAD1(9, ret),
4316			    "Failed to force adapter dump and reset");
4317		} else {
4318			FP_TRACE(FP_NHEAD1(9, 0),
4319			    "Forced adapter dump and reset successfully");
4320		}
4321	}
4322
4323	fctl_fillout_map(port, &changelist, &listlen, 1, 0, 0);
4324	(void) fp_ulp_statec_cb(port, FC_STATE_OFFLINE, changelist,
4325	    listlen, listlen, KM_SLEEP);
4326
4327	mutex_enter(&port->fp_mutex);
4328	port->fp_offline_tid = NULL;
4329	mutex_exit(&port->fp_mutex);
4330}
4331
4332
4333/*
4334 * Perform general purpose ELS request initialization
4335 */
4336static void
4337fp_els_init(fp_cmd_t *cmd, uint32_t s_id, uint32_t d_id,
4338    void (*comp) (), job_request_t *job)
4339{
4340	fc_packet_t *pkt;
4341
4342	pkt = &cmd->cmd_pkt;
4343	cmd->cmd_job = job;
4344
4345	pkt->pkt_cmd_fhdr.r_ctl = R_CTL_ELS_REQ;
4346	pkt->pkt_cmd_fhdr.d_id = d_id;
4347	pkt->pkt_cmd_fhdr.s_id = s_id;
4348	pkt->pkt_cmd_fhdr.type = FC_TYPE_EXTENDED_LS;
4349	pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
4350	pkt->pkt_cmd_fhdr.seq_id = 0;
4351	pkt->pkt_cmd_fhdr.df_ctl  = 0;
4352	pkt->pkt_cmd_fhdr.seq_cnt = 0;
4353	pkt->pkt_cmd_fhdr.ox_id = 0xffff;
4354	pkt->pkt_cmd_fhdr.rx_id = 0xffff;
4355	pkt->pkt_cmd_fhdr.ro = 0;
4356	pkt->pkt_cmd_fhdr.rsvd = 0;
4357	pkt->pkt_comp = comp;
4358	pkt->pkt_timeout = FP_ELS_TIMEOUT;
4359}
4360
4361
4362/*
4363 * Initialize PLOGI/FLOGI ELS request
4364 */
4365static void
4366fp_xlogi_init(fc_local_port_t *port, fp_cmd_t *cmd, uint32_t s_id,
4367    uint32_t d_id, void (*intr) (), job_request_t *job, uchar_t ls_code)
4368{
4369	ls_code_t	payload;
4370
4371	fp_els_init(cmd, s_id, d_id, intr, job);
4372	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
4373
4374	payload.ls_code = ls_code;
4375	payload.mbz = 0;
4376
4377	ddi_rep_put8(cmd->cmd_pkt.pkt_cmd_acc,
4378	    (uint8_t *)&port->fp_service_params,
4379	    (uint8_t *)cmd->cmd_pkt.pkt_cmd, sizeof (port->fp_service_params),
4380	    DDI_DEV_AUTOINCR);
4381
4382	ddi_rep_put8(cmd->cmd_pkt.pkt_cmd_acc, (uint8_t *)&payload,
4383	    (uint8_t *)cmd->cmd_pkt.pkt_cmd, sizeof (payload),
4384	    DDI_DEV_AUTOINCR);
4385}
4386
4387
4388/*
4389 * Initialize LOGO ELS request
4390 */
4391static void
4392fp_logo_init(fc_remote_port_t *pd, fp_cmd_t *cmd, job_request_t *job)
4393{
4394	fc_local_port_t	*port;
4395	fc_packet_t	*pkt;
4396	la_els_logo_t 	payload;
4397
4398	port = pd->pd_port;
4399	pkt = &cmd->cmd_pkt;
4400	ASSERT(MUTEX_HELD(&port->fp_mutex));
4401	ASSERT(MUTEX_HELD(&pd->pd_mutex));
4402
4403	fp_els_init(cmd, port->fp_port_id.port_id, pd->pd_port_id.port_id,
4404	    fp_logo_intr, job);
4405
4406	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
4407
4408	pkt->pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
4409	pkt->pkt_tran_type = FC_PKT_EXCHANGE;
4410
4411	payload.ls_code.ls_code = LA_ELS_LOGO;
4412	payload.ls_code.mbz = 0;
4413	payload.nport_ww_name = port->fp_service_params.nport_ww_name;
4414	payload.nport_id = port->fp_port_id;
4415
4416	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
4417	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
4418}
4419
4420/*
4421 * Initialize RNID ELS request
4422 */
4423static void
4424fp_rnid_init(fp_cmd_t *cmd, uint16_t flag, job_request_t *job)
4425{
4426	fc_local_port_t	*port;
4427	fc_packet_t	*pkt;
4428	la_els_rnid_t 	payload;
4429	fc_remote_port_t	*pd;
4430
4431	pkt = &cmd->cmd_pkt;
4432	pd = pkt->pkt_pd;
4433	port = pd->pd_port;
4434
4435	ASSERT(MUTEX_HELD(&port->fp_mutex));
4436	ASSERT(MUTEX_HELD(&pd->pd_mutex));
4437
4438	fp_els_init(cmd, port->fp_port_id.port_id, pd->pd_port_id.port_id,
4439	    fp_rnid_intr, job);
4440
4441	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
4442	pkt->pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
4443	pkt->pkt_tran_type = FC_PKT_EXCHANGE;
4444
4445	payload.ls_code.ls_code = LA_ELS_RNID;
4446	payload.ls_code.mbz = 0;
4447	payload.data_format = flag;
4448
4449	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
4450	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
4451}
4452
4453/*
4454 * Initialize RLS ELS request
4455 */
4456static void
4457fp_rls_init(fp_cmd_t *cmd, job_request_t *job)
4458{
4459	fc_local_port_t	*port;
4460	fc_packet_t	*pkt;
4461	la_els_rls_t 	payload;
4462	fc_remote_port_t	*pd;
4463
4464	pkt = &cmd->cmd_pkt;
4465	pd = pkt->pkt_pd;
4466	port = pd->pd_port;
4467
4468	ASSERT(MUTEX_HELD(&port->fp_mutex));
4469	ASSERT(MUTEX_HELD(&pd->pd_mutex));
4470
4471	fp_els_init(cmd, port->fp_port_id.port_id, pd->pd_port_id.port_id,
4472	    fp_rls_intr, job);
4473
4474	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
4475	pkt->pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
4476	pkt->pkt_tran_type = FC_PKT_EXCHANGE;
4477
4478	payload.ls_code.ls_code = LA_ELS_RLS;
4479	payload.ls_code.mbz = 0;
4480	payload.rls_portid = port->fp_port_id;
4481
4482	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
4483	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
4484}
4485
4486
4487/*
4488 * Initialize an ADISC ELS request
4489 */
4490static void
4491fp_adisc_init(fp_cmd_t *cmd, job_request_t *job)
4492{
4493	fc_local_port_t *port;
4494	fc_packet_t	*pkt;
4495	la_els_adisc_t 	payload;
4496	fc_remote_port_t	*pd;
4497
4498	pkt = &cmd->cmd_pkt;
4499	pd = pkt->pkt_pd;
4500	port = pd->pd_port;
4501
4502	ASSERT(MUTEX_HELD(&pd->pd_mutex));
4503	ASSERT(MUTEX_HELD(&pd->pd_port->fp_mutex));
4504
4505	fp_els_init(cmd, port->fp_port_id.port_id, pd->pd_port_id.port_id,
4506	    fp_adisc_intr, job);
4507
4508	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
4509	pkt->pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
4510	pkt->pkt_tran_type = FC_PKT_EXCHANGE;
4511
4512	payload.ls_code.ls_code = LA_ELS_ADISC;
4513	payload.ls_code.mbz = 0;
4514	payload.nport_id = port->fp_port_id;
4515	payload.port_wwn = port->fp_service_params.nport_ww_name;
4516	payload.node_wwn = port->fp_service_params.node_ww_name;
4517	payload.hard_addr = port->fp_hard_addr;
4518
4519	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
4520	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
4521}
4522
4523
4524/*
4525 * Send up a state change notification to ULPs.
4526 * Spawns a call to fctl_ulp_statec_cb in a taskq thread.
4527 */
4528static int
4529fp_ulp_statec_cb(fc_local_port_t *port, uint32_t state,
4530    fc_portmap_t *changelist, uint32_t listlen, uint32_t alloc_len, int sleep)
4531{
4532	fc_port_clist_t		*clist;
4533	fc_remote_port_t	*pd;
4534	int 			count;
4535
4536	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4537
4538	clist = kmem_zalloc(sizeof (*clist), sleep);
4539	if (clist == NULL) {
4540		kmem_free(changelist, alloc_len * sizeof (*changelist));
4541		return (FC_NOMEM);
4542	}
4543
4544	clist->clist_state = state;
4545
4546	mutex_enter(&port->fp_mutex);
4547	clist->clist_flags = port->fp_topology;
4548	mutex_exit(&port->fp_mutex);
4549
4550	clist->clist_port = (opaque_t)port;
4551	clist->clist_len = listlen;
4552	clist->clist_size = alloc_len;
4553	clist->clist_map = changelist;
4554
4555	/*
4556	 * Bump the reference count of each fc_remote_port_t in this changelist.
4557	 * This is necessary since these devices will be sitting in a taskq
4558	 * and referenced later.  When the state change notification is
4559	 * complete, the reference counts will be decremented.
4560	 */
4561	for (count = 0; count < clist->clist_len; count++) {
4562		pd = clist->clist_map[count].map_pd;
4563
4564		if (pd != NULL) {
4565			mutex_enter(&pd->pd_mutex);
4566			ASSERT((pd->pd_ref_count >= 0) ||
4567			    (pd->pd_aux_flags & PD_GIVEN_TO_ULPS));
4568			pd->pd_ref_count++;
4569
4570			if (clist->clist_map[count].map_state !=
4571			    PORT_DEVICE_INVALID) {
4572				pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
4573			}
4574
4575			mutex_exit(&pd->pd_mutex);
4576		}
4577	}
4578
4579#ifdef	DEBUG
4580	/*
4581	 * Sanity check for presence of OLD devices in the hash lists
4582	 */
4583	if (clist->clist_size) {
4584		ASSERT(clist->clist_map != NULL);
4585		for (count = 0; count < clist->clist_len; count++) {
4586			if (clist->clist_map[count].map_state ==
4587			    PORT_DEVICE_INVALID) {
4588				la_wwn_t 	pwwn;
4589				fc_portid_t 	d_id;
4590
4591				pd = clist->clist_map[count].map_pd;
4592				ASSERT(pd != NULL);
4593
4594				mutex_enter(&pd->pd_mutex);
4595				pwwn = pd->pd_port_name;
4596				d_id = pd->pd_port_id;
4597				mutex_exit(&pd->pd_mutex);
4598
4599				pd = fctl_get_remote_port_by_pwwn(port, &pwwn);
4600				ASSERT(pd != clist->clist_map[count].map_pd);
4601
4602				pd = fctl_get_remote_port_by_did(port,
4603				    d_id.port_id);
4604				ASSERT(pd != clist->clist_map[count].map_pd);
4605			}
4606		}
4607	}
4608#endif
4609
4610	mutex_enter(&port->fp_mutex);
4611
4612	if (state == FC_STATE_ONLINE) {
4613		if (--port->fp_statec_busy == 0) {
4614			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
4615		}
4616	}
4617	mutex_exit(&port->fp_mutex);
4618
4619	(void) taskq_dispatch(port->fp_taskq, fctl_ulp_statec_cb,
4620	    clist, KM_SLEEP);
4621
4622	FP_TRACE(FP_NHEAD1(4, 0), "fp_ulp_statec fired; Port=%p,"
4623	    "state=%x, len=%d", port, state, listlen);
4624
4625	return (FC_SUCCESS);
4626}
4627
4628
4629/*
4630 * Send up a FC_STATE_DEVICE_CHANGE state notification to ULPs
4631 */
4632static int
4633fp_ulp_devc_cb(fc_local_port_t *port, fc_portmap_t *changelist,
4634    uint32_t listlen, uint32_t alloc_len, int sleep, int sync)
4635{
4636	int		ret;
4637	fc_port_clist_t *clist;
4638
4639	ASSERT(!MUTEX_HELD(&port->fp_mutex));
4640
4641	clist = kmem_zalloc(sizeof (*clist), sleep);
4642	if (clist == NULL) {
4643		kmem_free(changelist, alloc_len * sizeof (*changelist));
4644		return (FC_NOMEM);
4645	}
4646
4647	clist->clist_state = FC_STATE_DEVICE_CHANGE;
4648
4649	mutex_enter(&port->fp_mutex);
4650	clist->clist_flags = port->fp_topology;
4651	mutex_exit(&port->fp_mutex);
4652
4653	clist->clist_port = (opaque_t)port;
4654	clist->clist_len = listlen;
4655	clist->clist_size = alloc_len;
4656	clist->clist_map = changelist;
4657
4658	/* Send sysevents for target state changes */
4659
4660	if (clist->clist_size) {
4661		int 			count;
4662		fc_remote_port_t	*pd;
4663
4664		ASSERT(clist->clist_map != NULL);
4665		for (count = 0; count < clist->clist_len; count++) {
4666			pd = clist->clist_map[count].map_pd;
4667
4668			/*
4669			 * Bump reference counts on all fc_remote_port_t
4670			 * structs in this list.  We don't know when the task
4671			 * will fire, and we don't need these fc_remote_port_t
4672			 * structs going away behind our back.
4673			 */
4674			if (pd) {
4675				mutex_enter(&pd->pd_mutex);
4676				ASSERT((pd->pd_ref_count >= 0) ||
4677				    (pd->pd_aux_flags & PD_GIVEN_TO_ULPS));
4678				pd->pd_ref_count++;
4679				mutex_exit(&pd->pd_mutex);
4680			}
4681
4682			if (clist->clist_map[count].map_state ==
4683			    PORT_DEVICE_VALID) {
4684				if (clist->clist_map[count].map_type ==
4685				    PORT_DEVICE_NEW) {
4686					/* Update our state change counter */
4687					mutex_enter(&port->fp_mutex);
4688					port->fp_last_change++;
4689					mutex_exit(&port->fp_mutex);
4690
4691					/* Additions */
4692					fp_log_target_event(port,
4693					    ESC_SUNFC_TARGET_ADD,
4694					    clist->clist_map[count].map_pwwn,
4695					    clist->clist_map[count].map_did.
4696					    port_id);
4697				}
4698
4699			} else if ((clist->clist_map[count].map_type ==
4700			    PORT_DEVICE_OLD) &&
4701			    (clist->clist_map[count].map_state ==
4702			    PORT_DEVICE_INVALID)) {
4703				/* Update our state change counter */
4704				mutex_enter(&port->fp_mutex);
4705				port->fp_last_change++;
4706				mutex_exit(&port->fp_mutex);
4707
4708				/*
4709				 * For removals, we don't decrement
4710				 * pd_ref_count until after the ULP's
4711				 * state change callback function has
4712				 * completed.
4713				 */
4714
4715				/* Removals */
4716				fp_log_target_event(port,
4717				    ESC_SUNFC_TARGET_REMOVE,
4718				    clist->clist_map[count].map_pwwn,
4719				    clist->clist_map[count].map_did.port_id);
4720			}
4721
4722			if (clist->clist_map[count].map_state !=
4723			    PORT_DEVICE_INVALID) {
4724				/*
4725				 * Indicate that the ULPs are now aware of
4726				 * this device.
4727				 */
4728
4729				mutex_enter(&pd->pd_mutex);
4730				pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
4731				mutex_exit(&pd->pd_mutex);
4732			}
4733
4734#ifdef	DEBUG
4735			/*
4736			 * Sanity check for OLD devices in the hash lists
4737			 */
4738			if (pd && clist->clist_map[count].map_state ==
4739			    PORT_DEVICE_INVALID) {
4740				la_wwn_t 	pwwn;
4741				fc_portid_t 	d_id;
4742
4743				mutex_enter(&pd->pd_mutex);
4744				pwwn = pd->pd_port_name;
4745				d_id = pd->pd_port_id;
4746				mutex_exit(&pd->pd_mutex);
4747
4748				/*
4749				 * This overwrites the 'pd' local variable.
4750				 * Beware of this if 'pd' ever gets
4751				 * referenced below this block.
4752				 */
4753				pd = fctl_get_remote_port_by_pwwn(port, &pwwn);
4754				ASSERT(pd != clist->clist_map[count].map_pd);
4755
4756				pd = fctl_get_remote_port_by_did(port,
4757				    d_id.port_id);
4758				ASSERT(pd != clist->clist_map[count].map_pd);
4759			}
4760#endif
4761		}
4762	}
4763
4764	if (sync) {
4765		clist->clist_wait = 1;
4766		mutex_init(&clist->clist_mutex, NULL, MUTEX_DRIVER, NULL);
4767		cv_init(&clist->clist_cv, NULL, CV_DRIVER, NULL);
4768	}
4769
4770	ret = taskq_dispatch(port->fp_taskq, fctl_ulp_statec_cb, clist, sleep);
4771	if (sync && ret) {
4772		mutex_enter(&clist->clist_mutex);
4773		while (clist->clist_wait) {
4774			cv_wait(&clist->clist_cv, &clist->clist_mutex);
4775		}
4776		mutex_exit(&clist->clist_mutex);
4777
4778		mutex_destroy(&clist->clist_mutex);
4779		cv_destroy(&clist->clist_cv);
4780		kmem_free(clist, sizeof (*clist));
4781	}
4782
4783	if (!ret) {
4784		FP_TRACE(FP_NHEAD1(4, 0), "fp_ulp_devc dispatch failed; "
4785		    "port=%p", port);
4786		kmem_free(clist->clist_map,
4787		    sizeof (*(clist->clist_map)) * clist->clist_size);
4788		kmem_free(clist, sizeof (*clist));
4789	} else {
4790		FP_TRACE(FP_NHEAD1(4, 0), "fp_ulp_devc fired; port=%p, len=%d",
4791		    port, listlen);
4792	}
4793
4794	return (FC_SUCCESS);
4795}
4796
4797
4798/*
4799 * Perform PLOGI to the group of devices for ULPs
4800 */
4801static void
4802fp_plogi_group(fc_local_port_t *port, job_request_t *job)
4803{
4804	int			offline;
4805	int 			count;
4806	int			rval;
4807	uint32_t		listlen;
4808	uint32_t		done;
4809	uint32_t		d_id;
4810	fc_remote_node_t	*node;
4811	fc_remote_port_t 	*pd;
4812	fc_remote_port_t	*tmp_pd;
4813	fc_packet_t		*ulp_pkt;
4814	la_els_logi_t		*els_data;
4815	ls_code_t		ls_code;
4816
4817	FP_TRACE(FP_NHEAD1(1, 0), "fp_plogi_group begin; port=%p, job=%p",
4818	    port, job);
4819
4820	done = 0;
4821	listlen = job->job_ulp_listlen;
4822	job->job_counter = job->job_ulp_listlen;
4823
4824	mutex_enter(&port->fp_mutex);
4825	offline = (port->fp_statec_busy ||
4826	    FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ? 1 : 0;
4827	mutex_exit(&port->fp_mutex);
4828
4829	for (count = 0; count < listlen; count++) {
4830		ASSERT(job->job_ulp_pkts[count]->pkt_rsplen >=
4831		    sizeof (la_els_logi_t));
4832
4833		ulp_pkt = job->job_ulp_pkts[count];
4834		pd = ulp_pkt->pkt_pd;
4835		d_id = ulp_pkt->pkt_cmd_fhdr.d_id;
4836
4837		if (offline) {
4838			done++;
4839
4840			ulp_pkt->pkt_state = FC_PKT_PORT_OFFLINE;
4841			ulp_pkt->pkt_reason = FC_REASON_OFFLINE;
4842			ulp_pkt->pkt_pd = NULL;
4843			ulp_pkt->pkt_comp(ulp_pkt);
4844
4845			job->job_ulp_pkts[count] = NULL;
4846
4847			fp_jobdone(job);
4848			continue;
4849		}
4850
4851		if (pd == NULL) {
4852			pd = fctl_get_remote_port_by_did(port, d_id);
4853			if (pd == NULL) { /* reset later */
4854				ulp_pkt->pkt_state = FC_PKT_FAILURE;
4855				continue;
4856			}
4857			mutex_enter(&pd->pd_mutex);
4858			if (pd->pd_flags == PD_ELS_IN_PROGRESS) {
4859				mutex_exit(&pd->pd_mutex);
4860				ulp_pkt->pkt_state = FC_PKT_ELS_IN_PROGRESS;
4861				done++;
4862				ulp_pkt->pkt_comp(ulp_pkt);
4863				job->job_ulp_pkts[count] = NULL;
4864				fp_jobdone(job);
4865			} else {
4866				ulp_pkt->pkt_state = FC_PKT_FAILURE;
4867				mutex_exit(&pd->pd_mutex);
4868			}
4869			continue;
4870		}
4871
4872		switch (ulp_pkt->pkt_state) {
4873		case FC_PKT_ELS_IN_PROGRESS:
4874			ulp_pkt->pkt_reason = FC_REASON_OFFLINE;
4875			/* FALLTHRU */
4876		case FC_PKT_LOCAL_RJT:
4877			done++;
4878			ulp_pkt->pkt_comp(ulp_pkt);
4879			job->job_ulp_pkts[count] = NULL;
4880			fp_jobdone(job);
4881			continue;
4882		default:
4883			break;
4884		}
4885
4886		/*
4887		 * Validate the pd corresponding to the d_id passed
4888		 * by the ULPs
4889		 */
4890		tmp_pd = fctl_get_remote_port_by_did(port, d_id);
4891		if ((tmp_pd == NULL) || (pd != tmp_pd)) {
4892			done++;
4893			ulp_pkt->pkt_state = FC_PKT_FAILURE;
4894			ulp_pkt->pkt_reason = FC_REASON_NO_CONNECTION;
4895			ulp_pkt->pkt_pd = NULL;
4896			ulp_pkt->pkt_comp(ulp_pkt);
4897			job->job_ulp_pkts[count] = NULL;
4898			fp_jobdone(job);
4899			continue;
4900		}
4901
4902		FP_TRACE(FP_NHEAD1(3, 0), "fp_plogi_group contd; "
4903		    "port=%p, pd=%p", port, pd);
4904
4905		mutex_enter(&pd->pd_mutex);
4906
4907		if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
4908			done++;
4909			els_data = (la_els_logi_t *)ulp_pkt->pkt_resp;
4910
4911			ls_code.ls_code = LA_ELS_ACC;
4912			ls_code.mbz = 0;
4913
4914			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4915			    (uint8_t *)&ls_code, (uint8_t *)&els_data->ls_code,
4916			    sizeof (ls_code_t), DDI_DEV_AUTOINCR);
4917
4918			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4919			    (uint8_t *)&pd->pd_csp,
4920			    (uint8_t *)&els_data->common_service,
4921			    sizeof (pd->pd_csp), DDI_DEV_AUTOINCR);
4922
4923			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4924			    (uint8_t *)&pd->pd_port_name,
4925			    (uint8_t *)&els_data->nport_ww_name,
4926			    sizeof (pd->pd_port_name), DDI_DEV_AUTOINCR);
4927
4928			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4929			    (uint8_t *)&pd->pd_clsp1,
4930			    (uint8_t *)&els_data->class_1,
4931			    sizeof (pd->pd_clsp1), DDI_DEV_AUTOINCR);
4932
4933			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4934			    (uint8_t *)&pd->pd_clsp2,
4935			    (uint8_t *)&els_data->class_2,
4936			    sizeof (pd->pd_clsp2), DDI_DEV_AUTOINCR);
4937
4938			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4939			    (uint8_t *)&pd->pd_clsp3,
4940			    (uint8_t *)&els_data->class_3,
4941			    sizeof (pd->pd_clsp3), DDI_DEV_AUTOINCR);
4942
4943			node = pd->pd_remote_nodep;
4944			pd->pd_login_count++;
4945			pd->pd_flags = PD_IDLE;
4946			ulp_pkt->pkt_pd = pd;
4947			mutex_exit(&pd->pd_mutex);
4948
4949			mutex_enter(&node->fd_mutex);
4950			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4951			    (uint8_t *)&node->fd_node_name,
4952			    (uint8_t *)(&els_data->node_ww_name),
4953			    sizeof (node->fd_node_name), DDI_DEV_AUTOINCR);
4954
4955
4956			ddi_rep_put8(ulp_pkt->pkt_resp_acc,
4957			    (uint8_t *)&node->fd_vv,
4958			    (uint8_t *)(&els_data->vendor_version),
4959			    sizeof (node->fd_vv), DDI_DEV_AUTOINCR);
4960
4961			mutex_exit(&node->fd_mutex);
4962			ulp_pkt->pkt_state = FC_PKT_SUCCESS;
4963		} else {
4964
4965			ulp_pkt->pkt_state = FC_PKT_FAILURE; /* reset later */
4966			mutex_exit(&pd->pd_mutex);
4967		}
4968
4969		if (ulp_pkt->pkt_state != FC_PKT_FAILURE) {
4970			ulp_pkt->pkt_comp(ulp_pkt);
4971			job->job_ulp_pkts[count] = NULL;
4972			fp_jobdone(job);
4973		}
4974	}
4975
4976	if (done == listlen) {
4977		fp_jobwait(job);
4978		fctl_jobdone(job);
4979		return;
4980	}
4981
4982	job->job_counter = listlen - done;
4983
4984	for (count = 0; count < listlen; count++) {
4985		int cmd_flags;
4986
4987		if ((ulp_pkt = job->job_ulp_pkts[count]) == NULL) {
4988			continue;
4989		}
4990
4991		ASSERT(ulp_pkt->pkt_state == FC_PKT_FAILURE);
4992
4993		cmd_flags = FP_CMD_PLOGI_RETAIN;
4994
4995		d_id = ulp_pkt->pkt_cmd_fhdr.d_id;
4996		ASSERT(d_id != 0);
4997
4998		pd = fctl_get_remote_port_by_did(port, d_id);
4999
5000		/*
5001		 * We need to properly adjust the port device
5002		 * reference counter before we assign the pd
5003		 * to the ULP packets port device pointer.
5004		 */
5005		if (pd != NULL && ulp_pkt->pkt_pd == NULL) {
5006			mutex_enter(&pd->pd_mutex);
5007			pd->pd_ref_count++;
5008			mutex_exit(&pd->pd_mutex);
5009			FP_TRACE(FP_NHEAD1(3, 0),
5010			    "fp_plogi_group: DID = 0x%x using new pd %p \
5011			    old pd NULL\n", d_id, pd);
5012		} else if (pd != NULL && ulp_pkt->pkt_pd != NULL &&
5013		    ulp_pkt->pkt_pd != pd) {
5014			mutex_enter(&pd->pd_mutex);
5015			pd->pd_ref_count++;
5016			mutex_exit(&pd->pd_mutex);
5017			mutex_enter(&ulp_pkt->pkt_pd->pd_mutex);
5018			ulp_pkt->pkt_pd->pd_ref_count--;
5019			mutex_exit(&ulp_pkt->pkt_pd->pd_mutex);
5020			FP_TRACE(FP_NHEAD1(3, 0),
5021			    "fp_plogi_group: DID = 0x%x pkt_pd %p != pd %p\n",
5022			    d_id, ulp_pkt->pkt_pd, pd);
5023		} else if (pd == NULL && ulp_pkt->pkt_pd != NULL) {
5024			mutex_enter(&ulp_pkt->pkt_pd->pd_mutex);
5025			ulp_pkt->pkt_pd->pd_ref_count--;
5026			mutex_exit(&ulp_pkt->pkt_pd->pd_mutex);
5027			FP_TRACE(FP_NHEAD1(3, 0),
5028			    "fp_plogi_group: DID = 0x%x pd is NULL and \
5029			    pkt_pd = %p\n", d_id, ulp_pkt->pkt_pd);
5030		}
5031
5032		ulp_pkt->pkt_pd = pd;
5033
5034		if (pd != NULL) {
5035			mutex_enter(&pd->pd_mutex);
5036			d_id = pd->pd_port_id.port_id;
5037			pd->pd_flags = PD_ELS_IN_PROGRESS;
5038			mutex_exit(&pd->pd_mutex);
5039		} else {
5040			d_id = ulp_pkt->pkt_cmd_fhdr.d_id;
5041#ifdef	DEBUG
5042			pd = fctl_get_remote_port_by_did(port, d_id);
5043			ASSERT(pd == NULL);
5044#endif
5045			/*
5046			 * In the Fabric topology, use NS to create
5047			 * port device, and if that fails still try
5048			 * with PLOGI - which will make yet another
5049			 * attempt to create after successful PLOGI
5050			 */
5051			mutex_enter(&port->fp_mutex);
5052			if (FC_IS_TOP_SWITCH(port->fp_topology)) {
5053				mutex_exit(&port->fp_mutex);
5054				pd = fp_create_remote_port_by_ns(port,
5055				    d_id, KM_SLEEP);
5056				if (pd) {
5057					cmd_flags |= FP_CMD_DELDEV_ON_ERROR;
5058
5059					mutex_enter(&pd->pd_mutex);
5060					pd->pd_flags = PD_ELS_IN_PROGRESS;
5061					mutex_exit(&pd->pd_mutex);
5062
5063					FP_TRACE(FP_NHEAD1(3, 0),
5064					    "fp_plogi_group;"
5065					    " NS created PD port=%p, job=%p,"
5066					    " pd=%p", port, job, pd);
5067				}
5068			} else {
5069				mutex_exit(&port->fp_mutex);
5070			}
5071			if ((ulp_pkt->pkt_pd == NULL) && (pd != NULL)) {
5072				FP_TRACE(FP_NHEAD1(3, 0),
5073				    "fp_plogi_group;"
5074				    "ulp_pkt's pd is NULL, get a pd %p",
5075				    pd);
5076				mutex_enter(&pd->pd_mutex);
5077				pd->pd_ref_count++;
5078				mutex_exit(&pd->pd_mutex);
5079			}
5080			ulp_pkt->pkt_pd = pd;
5081		}
5082
5083		rval = fp_port_login(port, d_id, job, cmd_flags,
5084		    KM_SLEEP, pd, ulp_pkt);
5085
5086		if (rval == FC_SUCCESS) {
5087			continue;
5088		}
5089
5090		if (rval == FC_STATEC_BUSY) {
5091			ulp_pkt->pkt_state = FC_PKT_PORT_OFFLINE;
5092			ulp_pkt->pkt_reason = FC_REASON_OFFLINE;
5093		} else {
5094			ulp_pkt->pkt_state = FC_PKT_FAILURE;
5095		}
5096
5097		if (pd) {
5098			mutex_enter(&pd->pd_mutex);
5099			pd->pd_flags = PD_IDLE;
5100			mutex_exit(&pd->pd_mutex);
5101		}
5102
5103		if (cmd_flags & FP_CMD_DELDEV_ON_ERROR) {
5104			ASSERT(pd != NULL);
5105
5106			FP_TRACE(FP_NHEAD1(3, 0), "fp_plogi_group: NS created,"
5107			    " PD removed; port=%p, job=%p", port, job);
5108
5109			mutex_enter(&pd->pd_mutex);
5110			pd->pd_ref_count--;
5111			node = pd->pd_remote_nodep;
5112			mutex_exit(&pd->pd_mutex);
5113
5114			ASSERT(node != NULL);
5115
5116			if (fctl_destroy_remote_port(port, pd) == 0) {
5117				fctl_destroy_remote_node(node);
5118			}
5119			ulp_pkt->pkt_pd = NULL;
5120		}
5121		ulp_pkt->pkt_comp(ulp_pkt);
5122		fp_jobdone(job);
5123	}
5124
5125	fp_jobwait(job);
5126	fctl_jobdone(job);
5127
5128	FP_TRACE(FP_NHEAD1(1, 0), "fp_plogi_group end: port=%p, job=%p",
5129	    port, job);
5130}
5131
5132
5133/*
5134 * Name server request initialization
5135 */
5136static void
5137fp_ns_init(fc_local_port_t *port, job_request_t *job, int sleep)
5138{
5139	int rval;
5140	int count;
5141	int size;
5142
5143	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
5144
5145	job->job_counter = 1;
5146	job->job_result = FC_SUCCESS;
5147
5148	rval = fp_port_login(port, 0xFFFFFC, job, FP_CMD_PLOGI_RETAIN,
5149	    KM_SLEEP, NULL, NULL);
5150
5151	if (rval != FC_SUCCESS) {
5152		mutex_enter(&port->fp_mutex);
5153		port->fp_topology = FC_TOP_NO_NS;
5154		mutex_exit(&port->fp_mutex);
5155		return;
5156	}
5157
5158	fp_jobwait(job);
5159
5160	if (job->job_result != FC_SUCCESS) {
5161		mutex_enter(&port->fp_mutex);
5162		port->fp_topology = FC_TOP_NO_NS;
5163		mutex_exit(&port->fp_mutex);
5164		return;
5165	}
5166
5167	/*
5168	 * At this time, we'll do NS registration for objects in the
5169	 * ns_reg_cmds (see top of this file) array.
5170	 *
5171	 * Each time a ULP module registers with the transport, the
5172	 * appropriate fc4 bit is set fc4 types and registered with
5173	 * the NS for this support. Also, ULPs and FC admin utilities
5174	 * may do registration for objects like IP address, symbolic
5175	 * port/node name, Initial process associator at run time.
5176	 */
5177	size = sizeof (ns_reg_cmds) / sizeof (ns_reg_cmds[0]);
5178	job->job_counter = size;
5179	job->job_result = FC_SUCCESS;
5180
5181	for (count = 0; count < size; count++) {
5182		if (fp_ns_reg(port, NULL, ns_reg_cmds[count],
5183		    job, 0, sleep) != FC_SUCCESS) {
5184			fp_jobdone(job);
5185		}
5186	}
5187	if (size) {
5188		fp_jobwait(job);
5189	}
5190
5191	job->job_result = FC_SUCCESS;
5192
5193	(void) fp_ns_get_devcount(port, job, 0, KM_SLEEP);
5194
5195	if (port->fp_dev_count < FP_MAX_DEVICES) {
5196		(void) fp_ns_get_devcount(port, job, 1, KM_SLEEP);
5197	}
5198
5199	job->job_counter = 1;
5200
5201	if (fp_ns_scr(port, job, FC_SCR_FULL_REGISTRATION,
5202	    sleep) == FC_SUCCESS) {
5203		fp_jobwait(job);
5204	}
5205}
5206
5207
5208/*
5209 * Name server finish:
5210 *	Unregister for RSCNs
5211 * 	Unregister all the host port objects in the Name Server
5212 * 	Perform LOGO with the NS;
5213 */
5214static void
5215fp_ns_fini(fc_local_port_t *port, job_request_t *job)
5216{
5217	fp_cmd_t 	*cmd;
5218	uchar_t		class;
5219	uint32_t	s_id;
5220	fc_packet_t	*pkt;
5221	la_els_logo_t 	payload;
5222
5223	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
5224
5225	job->job_counter = 1;
5226
5227	if (fp_ns_scr(port, job, FC_SCR_CLEAR_REGISTRATION, KM_SLEEP) !=
5228	    FC_SUCCESS) {
5229		fp_jobdone(job);
5230	}
5231	fp_jobwait(job);
5232
5233	job->job_counter = 1;
5234
5235	if (fp_ns_reg(port, NULL, NS_DA_ID, job, 0, KM_SLEEP) != FC_SUCCESS) {
5236		fp_jobdone(job);
5237	}
5238	fp_jobwait(job);
5239
5240	job->job_counter = 1;
5241
5242	cmd = fp_alloc_pkt(port, sizeof (la_els_logo_t),
5243		FP_PORT_IDENTIFIER_LEN, KM_SLEEP, NULL);
5244	pkt = &cmd->cmd_pkt;
5245
5246	mutex_enter(&port->fp_mutex);
5247	class = port->fp_ns_login_class;
5248	s_id = port->fp_port_id.port_id;
5249	payload.nport_id = port->fp_port_id;
5250	mutex_exit(&port->fp_mutex);
5251
5252	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
5253	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
5254	cmd->cmd_flags = FP_CMD_PLOGI_DONT_CARE;
5255	cmd->cmd_retry_count = 1;
5256	cmd->cmd_ulp_pkt = NULL;
5257
5258	if (port->fp_npiv_type == FC_NPIV_PORT) {
5259		fp_els_init(cmd, s_id, 0xFFFFFE, fp_logo_intr, job);
5260	} else {
5261		fp_els_init(cmd, s_id, 0xFFFFFC, fp_logo_intr, job);
5262	}
5263
5264	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
5265
5266	payload.ls_code.ls_code = LA_ELS_LOGO;
5267	payload.ls_code.mbz = 0;
5268	payload.nport_ww_name = port->fp_service_params.nport_ww_name;
5269
5270	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
5271	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
5272
5273	if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
5274		fp_iodone(cmd);
5275	}
5276	fp_jobwait(job);
5277}
5278
5279
5280/*
5281 * NS Registration function.
5282 *
5283 *	It should be seriously noted that FC-GS-2 currently doesn't support
5284 *	an Object Registration by a D_ID other than the owner of the object.
5285 *	What we are aiming at currently is to at least allow Symbolic Node/Port
5286 *	Name registration for any N_Port Identifier by the host software.
5287 *
5288 *	Anyway, if the second argument (fc_remote_port_t *) is NULL, this
5289 *	function treats the request as Host NS Object.
5290 */
5291static int
5292fp_ns_reg(fc_local_port_t *port, fc_remote_port_t *pd, uint16_t cmd_code,
5293    job_request_t *job, int polled, int sleep)
5294{
5295	int 		rval;
5296	fc_portid_t	s_id;
5297	fc_packet_t 	*pkt;
5298	fp_cmd_t 	*cmd;
5299
5300	if (pd == NULL) {
5301		mutex_enter(&port->fp_mutex);
5302		s_id = port->fp_port_id;
5303		mutex_exit(&port->fp_mutex);
5304	} else {
5305		mutex_enter(&pd->pd_mutex);
5306		s_id = pd->pd_port_id;
5307		mutex_exit(&pd->pd_mutex);
5308	}
5309
5310	if (polled) {
5311		job->job_counter = 1;
5312	}
5313
5314	switch (cmd_code) {
5315	case NS_RPN_ID:
5316	case NS_RNN_ID: {
5317		ns_rxn_req_t rxn;
5318
5319		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5320		    sizeof (ns_rxn_req_t), sizeof (fc_reg_resp_t), sleep, NULL);
5321		if (cmd == NULL) {
5322			return (FC_NOMEM);
5323		}
5324		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5325		pkt = &cmd->cmd_pkt;
5326
5327		if (pd == NULL) {
5328			rxn.rxn_xname = (cmd_code == NS_RPN_ID) ?
5329			    (port->fp_service_params.nport_ww_name) :
5330			    (port->fp_service_params.node_ww_name);
5331		} else {
5332			if (cmd_code == NS_RPN_ID) {
5333				mutex_enter(&pd->pd_mutex);
5334				rxn.rxn_xname = pd->pd_port_name;
5335				mutex_exit(&pd->pd_mutex);
5336			} else {
5337				fc_remote_node_t *node;
5338
5339				mutex_enter(&pd->pd_mutex);
5340				node = pd->pd_remote_nodep;
5341				mutex_exit(&pd->pd_mutex);
5342
5343				mutex_enter(&node->fd_mutex);
5344				rxn.rxn_xname = node->fd_node_name;
5345				mutex_exit(&node->fd_mutex);
5346			}
5347		}
5348		rxn.rxn_port_id = s_id;
5349
5350		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rxn,
5351		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5352		    sizeof (rxn), DDI_DEV_AUTOINCR);
5353
5354		break;
5355	}
5356
5357	case NS_RCS_ID: {
5358		ns_rcos_t rcos;
5359
5360		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5361		    sizeof (ns_rcos_t), sizeof (fc_reg_resp_t), sleep, NULL);
5362		if (cmd == NULL) {
5363			return (FC_NOMEM);
5364		}
5365		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5366		pkt = &cmd->cmd_pkt;
5367
5368		if (pd == NULL) {
5369			rcos.rcos_cos = port->fp_cos;
5370		} else {
5371			mutex_enter(&pd->pd_mutex);
5372			rcos.rcos_cos = pd->pd_cos;
5373			mutex_exit(&pd->pd_mutex);
5374		}
5375		rcos.rcos_port_id = s_id;
5376
5377		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rcos,
5378		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5379		    sizeof (rcos), DDI_DEV_AUTOINCR);
5380
5381		break;
5382	}
5383
5384	case NS_RFT_ID: {
5385		ns_rfc_type_t rfc;
5386
5387		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5388		    sizeof (ns_rfc_type_t), sizeof (fc_reg_resp_t), sleep,
5389		    NULL);
5390		if (cmd == NULL) {
5391			return (FC_NOMEM);
5392		}
5393		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5394		pkt = &cmd->cmd_pkt;
5395
5396		if (pd == NULL) {
5397			mutex_enter(&port->fp_mutex);
5398			bcopy(port->fp_fc4_types, rfc.rfc_types,
5399			    sizeof (port->fp_fc4_types));
5400			mutex_exit(&port->fp_mutex);
5401		} else {
5402			mutex_enter(&pd->pd_mutex);
5403			bcopy(pd->pd_fc4types, rfc.rfc_types,
5404			    sizeof (pd->pd_fc4types));
5405			mutex_exit(&pd->pd_mutex);
5406		}
5407		rfc.rfc_port_id = s_id;
5408
5409		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rfc,
5410		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5411		    sizeof (rfc), DDI_DEV_AUTOINCR);
5412
5413		break;
5414	}
5415
5416	case NS_RSPN_ID: {
5417		uchar_t 	name_len;
5418		int 		pl_size;
5419		fc_portid_t 	spn;
5420
5421		if (pd == NULL) {
5422			mutex_enter(&port->fp_mutex);
5423			name_len = port->fp_sym_port_namelen;
5424			mutex_exit(&port->fp_mutex);
5425		} else {
5426			mutex_enter(&pd->pd_mutex);
5427			name_len = pd->pd_spn_len;
5428			mutex_exit(&pd->pd_mutex);
5429		}
5430
5431		pl_size = sizeof (fc_portid_t) + name_len + 1;
5432
5433		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) + pl_size,
5434			sizeof (fc_reg_resp_t), sleep, NULL);
5435		if (cmd == NULL) {
5436			return (FC_NOMEM);
5437		}
5438
5439		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5440
5441		pkt = &cmd->cmd_pkt;
5442
5443		spn = s_id;
5444
5445		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&spn, (uint8_t *)
5446		    (pkt->pkt_cmd + sizeof (fc_ct_header_t)), sizeof (spn),
5447		    DDI_DEV_AUTOINCR);
5448		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&name_len,
5449		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)
5450		    + sizeof (fc_portid_t)), 1, DDI_DEV_AUTOINCR);
5451
5452		if (pd == NULL) {
5453			mutex_enter(&port->fp_mutex);
5454			ddi_rep_put8(pkt->pkt_cmd_acc,
5455			    (uint8_t *)port->fp_sym_port_name, (uint8_t *)
5456			    (pkt->pkt_cmd + sizeof (fc_ct_header_t) +
5457			    sizeof (spn) + 1), name_len, DDI_DEV_AUTOINCR);
5458			mutex_exit(&port->fp_mutex);
5459		} else {
5460			mutex_enter(&pd->pd_mutex);
5461			ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)pd->pd_spn,
5462			    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t) +
5463			    sizeof (spn) + 1), name_len, DDI_DEV_AUTOINCR);
5464			mutex_exit(&pd->pd_mutex);
5465		}
5466		break;
5467	}
5468
5469	case NS_RPT_ID: {
5470		ns_rpt_t rpt;
5471
5472		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5473		    sizeof (ns_rpt_t), sizeof (fc_reg_resp_t), sleep, NULL);
5474		if (cmd == NULL) {
5475			return (FC_NOMEM);
5476		}
5477		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5478		pkt = &cmd->cmd_pkt;
5479
5480		if (pd == NULL) {
5481			rpt.rpt_type = port->fp_port_type;
5482		} else {
5483			mutex_enter(&pd->pd_mutex);
5484			rpt.rpt_type = pd->pd_porttype;
5485			mutex_exit(&pd->pd_mutex);
5486		}
5487		rpt.rpt_port_id = s_id;
5488
5489		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rpt,
5490		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5491		    sizeof (rpt), DDI_DEV_AUTOINCR);
5492
5493		break;
5494	}
5495
5496	case NS_RIP_NN: {
5497		ns_rip_t rip;
5498
5499		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5500		    sizeof (ns_rip_t), sizeof (fc_reg_resp_t), sleep, NULL);
5501		if (cmd == NULL) {
5502			return (FC_NOMEM);
5503		}
5504		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5505		pkt = &cmd->cmd_pkt;
5506
5507		if (pd == NULL) {
5508			rip.rip_node_name =
5509			    port->fp_service_params.node_ww_name;
5510			bcopy(port->fp_ip_addr, rip.rip_ip_addr,
5511			    sizeof (port->fp_ip_addr));
5512		} else {
5513			fc_remote_node_t *node;
5514
5515			/*
5516			 * The most correct implementation should have the IP
5517			 * address in the fc_remote_node_t structure; I believe
5518			 * Node WWN and IP address should have one to one
5519			 * correlation (but guess what this is changing in
5520			 * FC-GS-2 latest draft)
5521			 */
5522			mutex_enter(&pd->pd_mutex);
5523			node = pd->pd_remote_nodep;
5524			bcopy(pd->pd_ip_addr, rip.rip_ip_addr,
5525			    sizeof (pd->pd_ip_addr));
5526			mutex_exit(&pd->pd_mutex);
5527
5528			mutex_enter(&node->fd_mutex);
5529			rip.rip_node_name = node->fd_node_name;
5530			mutex_exit(&node->fd_mutex);
5531		}
5532
5533		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rip,
5534		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5535		    sizeof (rip), DDI_DEV_AUTOINCR);
5536
5537		break;
5538	}
5539
5540	case NS_RIPA_NN: {
5541		ns_ipa_t ipa;
5542
5543		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5544		    sizeof (ns_ipa_t), sizeof (fc_reg_resp_t), sleep, NULL);
5545		if (cmd == NULL) {
5546			return (FC_NOMEM);
5547		}
5548		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5549		pkt = &cmd->cmd_pkt;
5550
5551		if (pd == NULL) {
5552			ipa.ipa_node_name =
5553			    port->fp_service_params.node_ww_name;
5554			bcopy(port->fp_ipa, ipa.ipa_value,
5555			    sizeof (port->fp_ipa));
5556		} else {
5557			fc_remote_node_t *node;
5558
5559			mutex_enter(&pd->pd_mutex);
5560			node = pd->pd_remote_nodep;
5561			mutex_exit(&pd->pd_mutex);
5562
5563			mutex_enter(&node->fd_mutex);
5564			ipa.ipa_node_name = node->fd_node_name;
5565			bcopy(node->fd_ipa, ipa.ipa_value,
5566			    sizeof (node->fd_ipa));
5567			mutex_exit(&node->fd_mutex);
5568		}
5569
5570		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&ipa,
5571		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5572		    sizeof (ipa), DDI_DEV_AUTOINCR);
5573
5574		break;
5575	}
5576
5577	case NS_RSNN_NN: {
5578		uchar_t 		name_len;
5579		int 			pl_size;
5580		la_wwn_t 		snn;
5581		fc_remote_node_t	*node = NULL;
5582
5583		if (pd == NULL) {
5584			mutex_enter(&port->fp_mutex);
5585			name_len = port->fp_sym_node_namelen;
5586			mutex_exit(&port->fp_mutex);
5587		} else {
5588			mutex_enter(&pd->pd_mutex);
5589			node = pd->pd_remote_nodep;
5590			mutex_exit(&pd->pd_mutex);
5591
5592			mutex_enter(&node->fd_mutex);
5593			name_len = node->fd_snn_len;
5594			mutex_exit(&node->fd_mutex);
5595		}
5596
5597		pl_size = sizeof (la_wwn_t) + name_len + 1;
5598
5599		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5600			pl_size, sizeof (fc_reg_resp_t), sleep, NULL);
5601		if (cmd == NULL) {
5602			return (FC_NOMEM);
5603		}
5604		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5605
5606		pkt = &cmd->cmd_pkt;
5607
5608		bcopy(&port->fp_service_params.node_ww_name,
5609		    &snn, sizeof (la_wwn_t));
5610
5611		if (pd == NULL) {
5612			mutex_enter(&port->fp_mutex);
5613			ddi_rep_put8(pkt->pkt_cmd_acc,
5614			    (uint8_t *)port->fp_sym_node_name, (uint8_t *)
5615			    (pkt->pkt_cmd + sizeof (fc_ct_header_t) +
5616			    sizeof (snn) + 1), name_len, DDI_DEV_AUTOINCR);
5617			mutex_exit(&port->fp_mutex);
5618		} else {
5619			ASSERT(node != NULL);
5620			mutex_enter(&node->fd_mutex);
5621			ddi_rep_put8(pkt->pkt_cmd_acc,
5622			    (uint8_t *)node->fd_snn,
5623			    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t) +
5624			    sizeof (snn) + 1), name_len, DDI_DEV_AUTOINCR);
5625			mutex_exit(&node->fd_mutex);
5626		}
5627
5628		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&snn,
5629		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5630		    sizeof (snn), DDI_DEV_AUTOINCR);
5631		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&name_len,
5632		    (uint8_t *)(pkt->pkt_cmd
5633		    + sizeof (fc_ct_header_t) + sizeof (snn)),
5634		    1, DDI_DEV_AUTOINCR);
5635
5636		break;
5637	}
5638
5639	case NS_DA_ID: {
5640		ns_remall_t rall;
5641		char tmp[4] = {0};
5642		char *ptr;
5643
5644		cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
5645		    sizeof (ns_remall_t), sizeof (fc_reg_resp_t), sleep, NULL);
5646
5647		if (cmd == NULL) {
5648			return (FC_NOMEM);
5649		}
5650
5651		fp_ct_init(port, cmd, NULL, cmd_code, NULL, 0, 0, job);
5652		pkt = &cmd->cmd_pkt;
5653
5654		ptr = (char *)(&s_id);
5655		tmp[3] = *ptr++;
5656		tmp[2] = *ptr++;
5657		tmp[1] = *ptr++;
5658		tmp[0] = *ptr;
5659#if defined(_BIT_FIELDS_LTOH)
5660		bcopy((caddr_t)tmp, (caddr_t)(&rall.rem_port_id), 4);
5661#else
5662		rall.rem_port_id = s_id;
5663#endif
5664		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&rall,
5665		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
5666		    sizeof (rall), DDI_DEV_AUTOINCR);
5667
5668		break;
5669	}
5670
5671	default:
5672		return (FC_FAILURE);
5673	}
5674
5675	rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
5676
5677	if (rval != FC_SUCCESS) {
5678		job->job_result = rval;
5679		fp_iodone(cmd);
5680	}
5681
5682	if (polled) {
5683		ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
5684		fp_jobwait(job);
5685	} else {
5686		rval = FC_SUCCESS;
5687	}
5688
5689	return (rval);
5690}
5691
5692
5693/*
5694 * Common interrupt handler
5695 */
5696static int
5697fp_common_intr(fc_packet_t *pkt, int iodone)
5698{
5699	int		rval = FC_FAILURE;
5700	fp_cmd_t 	*cmd;
5701	fc_local_port_t	*port;
5702
5703	cmd = pkt->pkt_ulp_private;
5704	port = cmd->cmd_port;
5705
5706	/*
5707	 * Fail fast the upper layer requests if
5708	 * a state change has occurred amidst.
5709	 */
5710	mutex_enter(&port->fp_mutex);
5711	if (cmd->cmd_ulp_pkt != NULL && port->fp_statec_busy) {
5712		mutex_exit(&port->fp_mutex);
5713		cmd->cmd_ulp_pkt->pkt_state = FC_PKT_PORT_OFFLINE;
5714		cmd->cmd_ulp_pkt->pkt_reason = FC_REASON_OFFLINE;
5715	} else if (!(port->fp_soft_state &
5716	    (FP_SOFT_IN_DETACH | FP_DETACH_INPROGRESS))) {
5717		mutex_exit(&port->fp_mutex);
5718
5719		switch (pkt->pkt_state) {
5720		case FC_PKT_LOCAL_BSY:
5721		case FC_PKT_FABRIC_BSY:
5722		case FC_PKT_NPORT_BSY:
5723		case FC_PKT_TIMEOUT:
5724			cmd->cmd_retry_interval = (pkt->pkt_state ==
5725			    FC_PKT_TIMEOUT) ? 0 : fp_retry_delay;
5726			rval = fp_retry_cmd(pkt);
5727			break;
5728
5729		case FC_PKT_FABRIC_RJT:
5730		case FC_PKT_NPORT_RJT:
5731		case FC_PKT_LOCAL_RJT:
5732		case FC_PKT_LS_RJT:
5733		case FC_PKT_FS_RJT:
5734		case FC_PKT_BA_RJT:
5735			rval = fp_handle_reject(pkt);
5736			break;
5737
5738		default:
5739			if (pkt->pkt_resp_resid) {
5740				cmd->cmd_retry_interval = 0;
5741				rval = fp_retry_cmd(pkt);
5742			}
5743			break;
5744		}
5745	} else {
5746		mutex_exit(&port->fp_mutex);
5747	}
5748
5749	if (rval != FC_SUCCESS && iodone) {
5750		fp_iodone(cmd);
5751		rval = FC_SUCCESS;
5752	}
5753
5754	return (rval);
5755}
5756
5757
5758/*
5759 * Some not so long winding theory on point to point topology:
5760 *
5761 *	In the ACC payload, if the D_ID is ZERO and the common service
5762 *	parameters indicate N_Port, then the topology is POINT TO POINT.
5763 *
5764 *	In a point to point topology with an N_Port, during Fabric Login,
5765 *	the destination N_Port will check with our WWN and decide if it
5766 * 	needs to issue PLOGI or not. That means, FLOGI could potentially
5767 *	trigger an unsolicited PLOGI from an N_Port. The Unsolicited
5768 *	PLOGI creates the device handles.
5769 *
5770 *	Assuming that the host port WWN is greater than the other N_Port
5771 *	WWN, then we become the master (be aware that this isn't the word
5772 *	used in the FC standards) and initiate the PLOGI.
5773 *
5774 */
5775static void
5776fp_flogi_intr(fc_packet_t *pkt)
5777{
5778	int			state;
5779	int			f_port;
5780	uint32_t		s_id;
5781	uint32_t		d_id;
5782	fp_cmd_t 		*cmd;
5783	fc_local_port_t 	*port;
5784	la_wwn_t		*swwn;
5785	la_wwn_t		dwwn;
5786	la_wwn_t		nwwn;
5787	fc_remote_port_t	*pd;
5788	la_els_logi_t 		*acc;
5789	com_svc_t		csp;
5790	ls_code_t		resp;
5791
5792	cmd = pkt->pkt_ulp_private;
5793	port = cmd->cmd_port;
5794
5795	FP_TRACE(FP_NHEAD1(1, 0), "fp_flogi_intr; port=%p, pkt=%p, state=%x",
5796	    port, pkt, pkt->pkt_state);
5797
5798	if (FP_IS_PKT_ERROR(pkt)) {
5799		(void) fp_common_intr(pkt, 1);
5800		return;
5801	}
5802
5803	/*
5804	 * Currently, we don't need to swap bytes here because qlc is faking the
5805	 * response for us and so endianness is getting taken care of. But we
5806	 * have to fix this and generalize this at some point
5807	 */
5808	acc = (la_els_logi_t *)pkt->pkt_resp;
5809
5810	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp, (uint8_t *)acc,
5811	    sizeof (resp), DDI_DEV_AUTOINCR);
5812
5813	ASSERT(resp.ls_code == LA_ELS_ACC);
5814	if (resp.ls_code != LA_ELS_ACC) {
5815		(void) fp_common_intr(pkt, 1);
5816		return;
5817	}
5818
5819	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&csp,
5820	    (uint8_t *)&acc->common_service, sizeof (csp), DDI_DEV_AUTOINCR);
5821
5822	f_port = FP_IS_F_PORT(csp.cmn_features) ? 1 : 0;
5823
5824	ASSERT(!MUTEX_HELD(&port->fp_mutex));
5825
5826	mutex_enter(&port->fp_mutex);
5827	state = FC_PORT_STATE_MASK(port->fp_state);
5828	mutex_exit(&port->fp_mutex);
5829
5830	if (pkt->pkt_resp_fhdr.d_id == 0) {
5831		if (f_port == 0 && state != FC_STATE_LOOP) {
5832			swwn = &port->fp_service_params.nport_ww_name;
5833
5834			ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&dwwn,
5835			    (uint8_t *)&acc->nport_ww_name, sizeof (la_wwn_t),
5836			    DDI_DEV_AUTOINCR);
5837
5838			ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&nwwn,
5839			    (uint8_t *)&acc->node_ww_name, sizeof (la_wwn_t),
5840			    DDI_DEV_AUTOINCR);
5841
5842			mutex_enter(&port->fp_mutex);
5843
5844			port->fp_topology = FC_TOP_PT_PT;
5845			port->fp_total_devices = 1;
5846			if (fctl_wwn_cmp(swwn, &dwwn) >= 0) {
5847				port->fp_ptpt_master = 1;
5848				/*
5849				 * Let us choose 'X' as S_ID and 'Y'
5850				 * as D_ID and that'll work; hopefully
5851				 * If not, it will get changed.
5852				 */
5853				s_id = port->fp_instance + FP_DEFAULT_SID;
5854				d_id = port->fp_instance + FP_DEFAULT_DID;
5855				port->fp_port_id.port_id = s_id;
5856				mutex_exit(&port->fp_mutex);
5857
5858				pd = fctl_create_remote_port(port,
5859				    &nwwn, &dwwn, d_id, PD_PLOGI_INITIATOR,
5860				    KM_NOSLEEP);
5861				if (pd == NULL) {
5862					fp_printf(port, CE_NOTE, FP_LOG_ONLY,
5863					    0, NULL, "couldn't create device"
5864					    " d_id=%X", d_id);
5865					fp_iodone(cmd);
5866					return;
5867				}
5868
5869				cmd->cmd_pkt.pkt_tran_flags =
5870				    pkt->pkt_tran_flags;
5871				cmd->cmd_pkt.pkt_tran_type = pkt->pkt_tran_type;
5872				cmd->cmd_flags = FP_CMD_PLOGI_RETAIN;
5873				cmd->cmd_retry_count = fp_retry_count;
5874
5875				fp_xlogi_init(port, cmd, s_id, d_id,
5876				    fp_plogi_intr, cmd->cmd_job, LA_ELS_PLOGI);
5877
5878				(&cmd->cmd_pkt)->pkt_pd = pd;
5879
5880				/*
5881				 * We've just created this fc_remote_port_t, and
5882				 * we're about to use it to send a PLOGI, so
5883				 * bump the reference count right now.  When
5884				 * the packet is freed, the reference count will
5885				 * be decremented.  The ULP may also start using
5886				 * it, so mark it as given away as well.
5887				 */
5888				pd->pd_ref_count++;
5889				pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
5890
5891				if (fp_sendcmd(port, cmd,
5892				    port->fp_fca_handle) == FC_SUCCESS) {
5893					return;
5894				}
5895			} else {
5896				/*
5897				 * The device handles will be created when the
5898				 * unsolicited PLOGI is completed successfully
5899				 */
5900				port->fp_ptpt_master = 0;
5901				mutex_exit(&port->fp_mutex);
5902			}
5903		}
5904		pkt->pkt_state = FC_PKT_FAILURE;
5905	} else {
5906		if (f_port) {
5907			mutex_enter(&port->fp_mutex);
5908			if (state == FC_STATE_LOOP) {
5909				port->fp_topology = FC_TOP_PUBLIC_LOOP;
5910			} else {
5911				port->fp_topology = FC_TOP_FABRIC;
5912
5913				ddi_rep_get8(pkt->pkt_resp_acc,
5914				    (uint8_t *)&port->fp_fabric_name,
5915				    (uint8_t *)&acc->node_ww_name,
5916				    sizeof (la_wwn_t),
5917				    DDI_DEV_AUTOINCR);
5918			}
5919			port->fp_port_id.port_id = pkt->pkt_resp_fhdr.d_id;
5920			mutex_exit(&port->fp_mutex);
5921		} else {
5922			pkt->pkt_state = FC_PKT_FAILURE;
5923		}
5924	}
5925	fp_iodone(cmd);
5926}
5927
5928
5929/*
5930 * Handle solicited PLOGI response
5931 */
5932static void
5933fp_plogi_intr(fc_packet_t *pkt)
5934{
5935	int			nl_port;
5936	int			bailout;
5937	uint32_t		d_id;
5938	fp_cmd_t 		*cmd;
5939	la_els_logi_t		*acc;
5940	fc_local_port_t		*port;
5941	fc_remote_port_t 	*pd;
5942	la_wwn_t		nwwn;
5943	la_wwn_t		pwwn;
5944	ls_code_t		resp;
5945
5946	nl_port = 0;
5947	cmd = pkt->pkt_ulp_private;
5948	port = cmd->cmd_port;
5949	d_id = pkt->pkt_cmd_fhdr.d_id;
5950
5951#ifndef	__lock_lint
5952	ASSERT(cmd->cmd_job && cmd->cmd_job->job_counter);
5953#endif
5954
5955	FP_TRACE(FP_NHEAD1(3, 0), "fp_plogi_intr: port=%p, job=%p, d_id=%x,"
5956	    " jcount=%d pkt=%p, state=%x", port, cmd->cmd_job, d_id,
5957	    cmd->cmd_job->job_counter, pkt, pkt->pkt_state);
5958
5959	/*
5960	 * Bail out early on ULP initiated requests if the
5961	 * state change has occurred
5962	 */
5963	mutex_enter(&port->fp_mutex);
5964	bailout = ((port->fp_statec_busy ||
5965	    FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) &&
5966	    cmd->cmd_ulp_pkt) ? 1 : 0;
5967	mutex_exit(&port->fp_mutex);
5968
5969	if (FP_IS_PKT_ERROR(pkt) || bailout) {
5970		int skip_msg = 0;
5971		int giveup = 0;
5972
5973		if (cmd->cmd_ulp_pkt) {
5974			cmd->cmd_ulp_pkt->pkt_state = pkt->pkt_state;
5975			cmd->cmd_ulp_pkt->pkt_reason = pkt->pkt_reason;
5976			cmd->cmd_ulp_pkt->pkt_action = pkt->pkt_action;
5977			cmd->cmd_ulp_pkt->pkt_expln = pkt->pkt_expln;
5978		}
5979
5980		/*
5981		 * If an unsolicited cross login already created
5982		 * a device speed up the discovery by not retrying
5983		 * the command mindlessly.
5984		 */
5985		if (pkt->pkt_pd == NULL &&
5986		    fctl_get_remote_port_by_did(port, d_id) != NULL) {
5987			fp_iodone(cmd);
5988			return;
5989		}
5990
5991		if (pkt->pkt_pd != NULL) {
5992			giveup = (pkt->pkt_pd->pd_recepient ==
5993			    PD_PLOGI_RECEPIENT) ? 1 : 0;
5994			if (giveup) {
5995				/*
5996				 * This pd is marked as plogi
5997				 * recipient, stop retrying
5998				 */
5999				FP_TRACE(FP_NHEAD1(3, 0),
6000				    "fp_plogi_intr: stop retry as"
6001				    " a cross login was accepted"
6002				    " from d_id=%x, port=%p.",
6003				    d_id, port);
6004				fp_iodone(cmd);
6005				return;
6006			}
6007		}
6008
6009		if (fp_common_intr(pkt, 0) == FC_SUCCESS) {
6010			return;
6011		}
6012
6013		if ((pd = fctl_get_remote_port_by_did(port, d_id)) != NULL) {
6014			mutex_enter(&pd->pd_mutex);
6015			if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
6016				skip_msg++;
6017			}
6018			mutex_exit(&pd->pd_mutex);
6019		}
6020
6021		mutex_enter(&port->fp_mutex);
6022		if (!bailout && !(skip_msg && port->fp_statec_busy) &&
6023		    port->fp_statec_busy <= 1 &&
6024		    pkt->pkt_reason != FC_REASON_FCAL_OPN_FAIL) {
6025			mutex_exit(&port->fp_mutex);
6026			/*
6027			 * In case of Login Collisions, JNI HBAs returns the
6028			 * FC pkt back to the Initiator with the state set to
6029			 * FC_PKT_LS_RJT and reason to FC_REASON_LOGICAL_ERROR.
6030			 * QLC HBAs handles such cases in the FW and doesnot
6031			 * return the LS_RJT with Logical error when
6032			 * login collision happens.
6033			 */
6034			if ((pkt->pkt_state != FC_PKT_LS_RJT) ||
6035			    (pkt->pkt_reason != FC_REASON_LOGICAL_ERROR)) {
6036				fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, pkt,
6037				    "PLOGI to %x failed", d_id);
6038			}
6039			FP_TRACE(FP_NHEAD2(9, 0),
6040			    "PLOGI to %x failed. state=%x reason=%x.",
6041			    d_id, pkt->pkt_state, pkt->pkt_reason);
6042		} else {
6043			mutex_exit(&port->fp_mutex);
6044		}
6045
6046		fp_iodone(cmd);
6047		return;
6048	}
6049
6050	acc = (la_els_logi_t *)pkt->pkt_resp;
6051
6052	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp, (uint8_t *)acc,
6053	    sizeof (resp), DDI_DEV_AUTOINCR);
6054
6055	ASSERT(resp.ls_code == LA_ELS_ACC);
6056	if (resp.ls_code != LA_ELS_ACC) {
6057		(void) fp_common_intr(pkt, 1);
6058		return;
6059	}
6060
6061	if (d_id == FS_NAME_SERVER || d_id == FS_FABRIC_CONTROLLER) {
6062		mutex_enter(&port->fp_mutex);
6063		port->fp_ns_login_class = FC_TRAN_CLASS(pkt->pkt_tran_flags);
6064		mutex_exit(&port->fp_mutex);
6065		fp_iodone(cmd);
6066		return;
6067	}
6068
6069	ASSERT(acc == (la_els_logi_t *)pkt->pkt_resp);
6070
6071	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&pwwn,
6072	    (uint8_t *)&acc->nport_ww_name, sizeof (la_wwn_t),
6073	    DDI_DEV_AUTOINCR);
6074
6075	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&nwwn,
6076	    (uint8_t *)&acc->node_ww_name, sizeof (la_wwn_t),
6077	    DDI_DEV_AUTOINCR);
6078
6079	ASSERT(fctl_is_wwn_zero(&pwwn) == FC_FAILURE);
6080	ASSERT(fctl_is_wwn_zero(&nwwn) == FC_FAILURE);
6081
6082	if ((pd = pkt->pkt_pd) == NULL) {
6083		pd = fctl_get_remote_port_by_pwwn(port, &pwwn);
6084		if (pd == NULL) {
6085			pd = fctl_create_remote_port(port, &nwwn, &pwwn, d_id,
6086			    PD_PLOGI_INITIATOR, KM_NOSLEEP);
6087			if (pd == NULL) {
6088				fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
6089				    "couldn't create port device handles"
6090				    " d_id=%x", d_id);
6091				fp_iodone(cmd);
6092				return;
6093			}
6094		} else {
6095			fc_remote_port_t *tmp_pd;
6096
6097			tmp_pd = fctl_get_remote_port_by_did(port, d_id);
6098			if (tmp_pd != NULL) {
6099				fp_iodone(cmd);
6100				return;
6101			}
6102
6103			mutex_enter(&port->fp_mutex);
6104			mutex_enter(&pd->pd_mutex);
6105			if ((pd->pd_state == PORT_DEVICE_LOGGED_IN) ||
6106			    (pd->pd_aux_flags & PD_LOGGED_OUT)) {
6107				cmd->cmd_flags |= FP_CMD_PLOGI_RETAIN;
6108			}
6109
6110			if (pd->pd_type == PORT_DEVICE_OLD) {
6111				if (pd->pd_port_id.port_id != d_id) {
6112					fctl_delist_did_table(port, pd);
6113					pd->pd_type = PORT_DEVICE_CHANGED;
6114					pd->pd_port_id.port_id = d_id;
6115				} else {
6116					pd->pd_type = PORT_DEVICE_NOCHANGE;
6117				}
6118			}
6119
6120			if (pd->pd_aux_flags & PD_IN_DID_QUEUE) {
6121				char ww_name[17];
6122
6123				fc_wwn_to_str(&pd->pd_port_name, ww_name);
6124
6125				mutex_exit(&pd->pd_mutex);
6126				mutex_exit(&port->fp_mutex);
6127				FP_TRACE(FP_NHEAD2(9, 0),
6128				    "Possible Duplicate name or address"
6129				    " identifiers in the PLOGI response"
6130				    " D_ID=%x, PWWN=%s: Please check the"
6131				    " configuration", d_id, ww_name);
6132				fp_iodone(cmd);
6133				return;
6134			}
6135			fctl_enlist_did_table(port, pd);
6136			pd->pd_aux_flags &= ~PD_LOGGED_OUT;
6137			mutex_exit(&pd->pd_mutex);
6138			mutex_exit(&port->fp_mutex);
6139		}
6140	} else {
6141		fc_remote_port_t *tmp_pd, *new_wwn_pd;
6142
6143		tmp_pd = fctl_get_remote_port_by_did(port, d_id);
6144		new_wwn_pd = fctl_get_remote_port_by_pwwn(port, &pwwn);
6145
6146		mutex_enter(&port->fp_mutex);
6147		mutex_enter(&pd->pd_mutex);
6148		if (fctl_wwn_cmp(&pd->pd_port_name, &pwwn) == 0) {
6149			FP_TRACE(FP_NHEAD1(3, 0), "fp_plogi_intr: d_id=%x,"
6150			    " pd_state=%x pd_type=%x", d_id, pd->pd_state,
6151			    pd->pd_type);
6152			if ((pd->pd_state == PORT_DEVICE_LOGGED_IN &&
6153			    pd->pd_type == PORT_DEVICE_OLD) ||
6154				(pd->pd_aux_flags & PD_LOGGED_OUT)) {
6155				pd->pd_type = PORT_DEVICE_NOCHANGE;
6156			} else if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
6157				pd->pd_type = PORT_DEVICE_NEW;
6158			}
6159		} else {
6160			char 	old_name[17];
6161			char 	new_name[17];
6162
6163			fc_wwn_to_str(&pd->pd_port_name, old_name);
6164			fc_wwn_to_str(&pwwn, new_name);
6165
6166			FP_TRACE(FP_NHEAD1(9, 0),
6167			    "fp_plogi_intr: PWWN of a device with D_ID=%x "
6168			    "changed. New PWWN = %s, OLD PWWN = %s ; tmp_pd:%p "
6169			    "pd:%p new_wwn_pd:%p, cmd_ulp_pkt:%p, bailout:0x%x",
6170			    d_id, new_name, old_name, tmp_pd, pd, new_wwn_pd,
6171			    cmd->cmd_ulp_pkt, bailout);
6172
6173			FP_TRACE(FP_NHEAD2(9, 0),
6174			    "PWWN of a device with D_ID=%x changed."
6175			    " New PWWN = %s, OLD PWWN = %s", d_id,
6176			    new_name, old_name);
6177
6178			if (cmd->cmd_ulp_pkt && !bailout) {
6179				fc_remote_node_t	*rnodep;
6180				fc_portmap_t	*changelist;
6181				fc_portmap_t	*listptr;
6182				int		len = 1;
6183						/* # entries in changelist */
6184
6185				fctl_delist_pwwn_table(port, pd);
6186
6187				/*
6188				 * Lets now check if there already is a pd with
6189				 * this new WWN in the table. If so, we'll mark
6190				 * it as invalid
6191				 */
6192
6193				if (new_wwn_pd) {
6194					/*
6195					 * There is another pd with in the pwwn
6196					 * table with the same WWN that we got
6197					 * in the PLOGI payload. We have to get
6198					 * it out of the pwwn table, update the
6199					 * pd's state (fp_fillout_old_map does
6200					 * this for us) and add it to the
6201					 * changelist that goes up to ULPs.
6202					 *
6203					 * len is length of changelist and so
6204					 * increment it.
6205					 */
6206					len++;
6207
6208					if (tmp_pd != pd) {
6209						/*
6210						 * Odd case where pwwn and did
6211						 * tables are out of sync but
6212						 * we will handle that too. See
6213						 * more comments below.
6214						 *
6215						 * One more device that ULPs
6216						 * should know about and so len
6217						 * gets incremented again.
6218						 */
6219						len++;
6220					}
6221
6222					listptr = changelist = kmem_zalloc(len *
6223					    sizeof (*changelist), KM_SLEEP);
6224
6225					mutex_enter(&new_wwn_pd->pd_mutex);
6226					rnodep = new_wwn_pd->pd_remote_nodep;
6227					mutex_exit(&new_wwn_pd->pd_mutex);
6228
6229					/*
6230					 * Hold the fd_mutex since
6231					 * fctl_copy_portmap_held expects it.
6232					 * Preserve lock hierarchy by grabbing
6233					 * fd_mutex before pd_mutex
6234					 */
6235					if (rnodep) {
6236						mutex_enter(&rnodep->fd_mutex);
6237					}
6238					mutex_enter(&new_wwn_pd->pd_mutex);
6239					fp_fillout_old_map_held(listptr++,
6240					    new_wwn_pd, 0);
6241					mutex_exit(&new_wwn_pd->pd_mutex);
6242					if (rnodep) {
6243						mutex_exit(&rnodep->fd_mutex);
6244					}
6245
6246					/*
6247					 * Safety check :
6248					 * Lets ensure that the pwwn and did
6249					 * tables are in sync. Ideally, we
6250					 * should not find that these two pd's
6251					 * are different.
6252					 */
6253					if (tmp_pd != pd) {
6254						mutex_enter(&tmp_pd->pd_mutex);
6255						rnodep =
6256						    tmp_pd->pd_remote_nodep;
6257						mutex_exit(&tmp_pd->pd_mutex);
6258
6259						/* As above grab fd_mutex */
6260						if (rnodep) {
6261							mutex_enter(&rnodep->
6262							    fd_mutex);
6263						}
6264						mutex_enter(&tmp_pd->pd_mutex);
6265
6266						fp_fillout_old_map_held(
6267						    listptr++, tmp_pd, 0);
6268
6269						mutex_exit(&tmp_pd->pd_mutex);
6270						if (rnodep) {
6271							mutex_exit(&rnodep->
6272							    fd_mutex);
6273						}
6274
6275						/*
6276						 * Now add "pd" (not tmp_pd)
6277						 * to fp_did_table to sync it up
6278						 * with fp_pwwn_table
6279						 *
6280						 * pd->pd_mutex is already held
6281						 * at this point
6282						 */
6283						fctl_enlist_did_table(port, pd);
6284					}
6285				} else {
6286					listptr = changelist = kmem_zalloc(
6287					    sizeof (*changelist), KM_SLEEP);
6288				}
6289
6290				ASSERT(changelist != NULL);
6291
6292				fp_fillout_changed_map(listptr, pd, &d_id,
6293					&pwwn);
6294				fctl_enlist_pwwn_table(port, pd);
6295
6296				mutex_exit(&pd->pd_mutex);
6297				mutex_exit(&port->fp_mutex);
6298
6299				fp_iodone(cmd);
6300
6301				(void) fp_ulp_devc_cb(port, changelist, len,
6302						len, KM_NOSLEEP, 0);
6303
6304				return;
6305			}
6306		}
6307
6308		if (pd->pd_porttype.port_type == FC_NS_PORT_NL) {
6309			nl_port = 1;
6310		}
6311		if (pd->pd_aux_flags & PD_DISABLE_RELOGIN)
6312			pd->pd_aux_flags &= ~PD_LOGGED_OUT;
6313
6314		mutex_exit(&pd->pd_mutex);
6315		mutex_exit(&port->fp_mutex);
6316
6317		if (tmp_pd == NULL) {
6318			mutex_enter(&port->fp_mutex);
6319			mutex_enter(&pd->pd_mutex);
6320			if (pd->pd_aux_flags & PD_IN_DID_QUEUE) {
6321				char ww_name[17];
6322
6323				fc_wwn_to_str(&pd->pd_port_name, ww_name);
6324				mutex_exit(&pd->pd_mutex);
6325				mutex_exit(&port->fp_mutex);
6326				FP_TRACE(FP_NHEAD2(9, 0),
6327				    "Possible Duplicate name or address"
6328				    " identifiers in the PLOGI response"
6329				    " D_ID=%x, PWWN=%s: Please check the"
6330				    " configuration", d_id, ww_name);
6331				fp_iodone(cmd);
6332				return;
6333			}
6334			fctl_enlist_did_table(port, pd);
6335			pd->pd_aux_flags &= ~PD_LOGGED_OUT;
6336			mutex_exit(&pd->pd_mutex);
6337			mutex_exit(&port->fp_mutex);
6338		}
6339	}
6340	fp_register_login(&pkt->pkt_resp_acc, pd, acc,
6341	    FC_TRAN_CLASS(pkt->pkt_tran_flags));
6342
6343	if (cmd->cmd_ulp_pkt) {
6344		cmd->cmd_ulp_pkt->pkt_state = pkt->pkt_state;
6345		cmd->cmd_ulp_pkt->pkt_action = pkt->pkt_action;
6346		cmd->cmd_ulp_pkt->pkt_expln = pkt->pkt_expln;
6347		if (cmd->cmd_ulp_pkt->pkt_pd == NULL) {
6348			if (pd != NULL) {
6349				FP_TRACE(FP_NHEAD1(9, 0),
6350				    "fp_plogi_intr;"
6351				    "ulp_pkt's pd is NULL, get a pd %p",
6352				    pd);
6353				mutex_enter(&pd->pd_mutex);
6354				pd->pd_ref_count++;
6355				mutex_exit(&pd->pd_mutex);
6356			}
6357			cmd->cmd_ulp_pkt->pkt_pd = pd;
6358		}
6359		bcopy((caddr_t)&pkt->pkt_resp_fhdr,
6360		    (caddr_t)&cmd->cmd_ulp_pkt->pkt_resp_fhdr,
6361		    sizeof (fc_frame_hdr_t));
6362		bcopy((caddr_t)pkt->pkt_resp,
6363		    (caddr_t)cmd->cmd_ulp_pkt->pkt_resp,
6364		    sizeof (la_els_logi_t));
6365	}
6366
6367	mutex_enter(&port->fp_mutex);
6368	if (port->fp_topology == FC_TOP_PRIVATE_LOOP || nl_port) {
6369		mutex_enter(&pd->pd_mutex);
6370
6371		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
6372		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
6373		cmd->cmd_retry_count = fp_retry_count;
6374
6375		/*
6376		 * If the fc_remote_port_t pointer is not set in the given
6377		 * fc_packet_t, then this fc_remote_port_t must have just
6378		 * been created.  Save the pointer and also increment the
6379		 * fc_remote_port_t reference count.
6380		 */
6381		if (pkt->pkt_pd == NULL) {
6382			pkt->pkt_pd = pd;
6383			pd->pd_ref_count++;	/* It's in use! */
6384		}
6385
6386		fp_adisc_init(cmd, cmd->cmd_job);
6387
6388		pkt->pkt_cmdlen = sizeof (la_els_adisc_t);
6389		pkt->pkt_rsplen = sizeof (la_els_adisc_t);
6390
6391		mutex_exit(&pd->pd_mutex);
6392		mutex_exit(&port->fp_mutex);
6393
6394		if (fp_sendcmd(port, cmd, port->fp_fca_handle) == FC_SUCCESS) {
6395			return;
6396		}
6397	} else {
6398		mutex_exit(&port->fp_mutex);
6399	}
6400
6401	if ((cmd->cmd_flags & FP_CMD_PLOGI_RETAIN) == 0) {
6402		mutex_enter(&port->fp_mutex);
6403		mutex_enter(&pd->pd_mutex);
6404
6405		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
6406		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
6407		cmd->cmd_retry_count = fp_retry_count;
6408
6409		fp_logo_init(pd, cmd, cmd->cmd_job);
6410
6411		pkt->pkt_cmdlen = sizeof (la_els_logo_t);
6412		pkt->pkt_rsplen = FP_PORT_IDENTIFIER_LEN;
6413
6414		mutex_exit(&pd->pd_mutex);
6415		mutex_exit(&port->fp_mutex);
6416
6417		if (fp_sendcmd(port, cmd, port->fp_fca_handle) == FC_SUCCESS) {
6418			return;
6419		}
6420
6421	}
6422	fp_iodone(cmd);
6423}
6424
6425
6426/*
6427 * Handle solicited ADISC response
6428 */
6429static void
6430fp_adisc_intr(fc_packet_t *pkt)
6431{
6432	int			rval;
6433	int			bailout;
6434	fp_cmd_t		*cmd;
6435	fc_local_port_t		*port;
6436	fc_remote_port_t	*pd;
6437	la_els_adisc_t 		*acc;
6438	ls_code_t		resp;
6439	fc_hardaddr_t		ha;
6440	fc_portmap_t		*changelist;
6441	int			initiator, adiscfail = 0;
6442
6443	pd = pkt->pkt_pd;
6444	cmd = pkt->pkt_ulp_private;
6445	port = cmd->cmd_port;
6446
6447#ifndef	__lock_lint
6448	ASSERT(cmd->cmd_job && cmd->cmd_job->job_counter);
6449#endif
6450
6451	ASSERT(pd != NULL && port != NULL && cmd != NULL);
6452
6453	mutex_enter(&port->fp_mutex);
6454	bailout = ((port->fp_statec_busy ||
6455	    FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) &&
6456	    cmd->cmd_ulp_pkt) ? 1 : 0;
6457	mutex_exit(&port->fp_mutex);
6458
6459	if (bailout) {
6460		fp_iodone(cmd);
6461		return;
6462	}
6463
6464	if (pkt->pkt_state == FC_PKT_SUCCESS && pkt->pkt_resp_resid == 0) {
6465		acc = (la_els_adisc_t *)pkt->pkt_resp;
6466
6467		ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp,
6468		    (uint8_t *)acc, sizeof (resp), DDI_DEV_AUTOINCR);
6469
6470		if (resp.ls_code == LA_ELS_ACC) {
6471			int	is_private;
6472
6473			ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&ha,
6474			    (uint8_t *)&acc->hard_addr, sizeof (ha),
6475			    DDI_DEV_AUTOINCR);
6476
6477			mutex_enter(&port->fp_mutex);
6478
6479			is_private =
6480			    (port->fp_topology == FC_TOP_PRIVATE_LOOP) ? 1 : 0;
6481
6482			mutex_enter(&pd->pd_mutex);
6483			if ((pd->pd_aux_flags & PD_IN_DID_QUEUE) == 0) {
6484				fctl_enlist_did_table(port, pd);
6485			}
6486			mutex_exit(&pd->pd_mutex);
6487
6488			mutex_exit(&port->fp_mutex);
6489
6490			mutex_enter(&pd->pd_mutex);
6491			if (pd->pd_type != PORT_DEVICE_NEW) {
6492				if (is_private && (pd->pd_hard_addr.hard_addr !=
6493				    ha.hard_addr)) {
6494					pd->pd_type = PORT_DEVICE_CHANGED;
6495				} else {
6496					pd->pd_type = PORT_DEVICE_NOCHANGE;
6497				}
6498			}
6499
6500			if (is_private && (ha.hard_addr &&
6501			    pd->pd_port_id.port_id != ha.hard_addr)) {
6502				char ww_name[17];
6503
6504				fc_wwn_to_str(&pd->pd_port_name, ww_name);
6505
6506				fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
6507				    "NL_Port Identifier %x doesn't match"
6508				    " with Hard Address %x, Will use Port"
6509				    " WWN %s", pd->pd_port_id.port_id,
6510				    ha.hard_addr, ww_name);
6511
6512				pd->pd_hard_addr.hard_addr = 0;
6513			} else {
6514				pd->pd_hard_addr.hard_addr = ha.hard_addr;
6515			}
6516			mutex_exit(&pd->pd_mutex);
6517		} else {
6518			if (fp_common_intr(pkt, 0) == FC_SUCCESS) {
6519				return;
6520			}
6521		}
6522	} else {
6523		if (fp_common_intr(pkt, 0) == FC_SUCCESS) {
6524			return;
6525		}
6526
6527		mutex_enter(&port->fp_mutex);
6528		if (port->fp_statec_busy <= 1) {
6529			mutex_exit(&port->fp_mutex);
6530			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, pkt,
6531			    "ADISC to %x failed, cmd_flags=%x",
6532			    pkt->pkt_cmd_fhdr.d_id, cmd->cmd_flags);
6533			cmd->cmd_flags &= ~FP_CMD_PLOGI_RETAIN;
6534			adiscfail = 1;
6535		} else {
6536			mutex_exit(&port->fp_mutex);
6537		}
6538	}
6539
6540	if (cmd->cmd_ulp_pkt) {
6541		cmd->cmd_ulp_pkt->pkt_state = pkt->pkt_state;
6542		cmd->cmd_ulp_pkt->pkt_action = pkt->pkt_action;
6543		cmd->cmd_ulp_pkt->pkt_expln = pkt->pkt_expln;
6544		if (cmd->cmd_ulp_pkt->pkt_pd == NULL) {
6545			cmd->cmd_ulp_pkt->pkt_pd = pd;
6546			FP_TRACE(FP_NHEAD1(9, 0),
6547			    "fp_adisc__intr;"
6548			    "ulp_pkt's pd is NULL, get a pd %p",
6549			    pd);
6550
6551		}
6552		bcopy((caddr_t)&pkt->pkt_resp_fhdr,
6553		    (caddr_t)&cmd->cmd_ulp_pkt->pkt_resp_fhdr,
6554		    sizeof (fc_frame_hdr_t));
6555		bcopy((caddr_t)pkt->pkt_resp,
6556		    (caddr_t)cmd->cmd_ulp_pkt->pkt_resp,
6557		    sizeof (la_els_logi_t));
6558	}
6559
6560	if ((cmd->cmd_flags & FP_CMD_PLOGI_RETAIN) == 0) {
6561		FP_TRACE(FP_NHEAD1(9, 0),
6562		    "fp_adisc_intr: Perform LOGO.cmd_flags=%x, "
6563		    "fp_retry_count=%x, ulp_pkt=%p",
6564		    cmd->cmd_flags, fp_retry_count, cmd->cmd_ulp_pkt);
6565
6566		mutex_enter(&port->fp_mutex);
6567		mutex_enter(&pd->pd_mutex);
6568
6569		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
6570		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
6571		cmd->cmd_retry_count = fp_retry_count;
6572
6573		fp_logo_init(pd, cmd, cmd->cmd_job);
6574
6575		pkt->pkt_cmdlen = sizeof (la_els_logo_t);
6576		pkt->pkt_rsplen = FP_PORT_IDENTIFIER_LEN;
6577
6578		mutex_exit(&pd->pd_mutex);
6579		mutex_exit(&port->fp_mutex);
6580
6581		rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
6582		if (adiscfail) {
6583			mutex_enter(&pd->pd_mutex);
6584			initiator =
6585			    (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
6586			pd->pd_state = PORT_DEVICE_VALID;
6587			pd->pd_aux_flags |= PD_LOGGED_OUT;
6588			if (pd->pd_aux_flags & PD_DISABLE_RELOGIN)
6589				pd->pd_type = PORT_DEVICE_NEW;
6590			else
6591				pd->pd_type = PORT_DEVICE_NOCHANGE;
6592			mutex_exit(&pd->pd_mutex);
6593
6594			changelist =
6595			    kmem_zalloc(sizeof (*changelist), KM_SLEEP);
6596
6597			if (initiator) {
6598				fp_unregister_login(pd);
6599				fctl_copy_portmap(changelist, pd);
6600			} else {
6601				fp_fillout_old_map(changelist, pd, 0);
6602			}
6603
6604			FP_TRACE(FP_NHEAD1(9, 0),
6605			    "fp_adisc_intr: Dev change notification "
6606			    "to ULP port=%p, pd=%p, map_type=%x map_state=%x "
6607			    "map_flags=%x initiator=%d", port, pd,
6608			    changelist->map_type, changelist->map_state,
6609			    changelist->map_flags, initiator);
6610
6611			(void) fp_ulp_devc_cb(port, changelist,
6612			    1, 1, KM_SLEEP, 0);
6613		}
6614		if (rval == FC_SUCCESS) {
6615			return;
6616		}
6617	}
6618	fp_iodone(cmd);
6619}
6620
6621
6622/*
6623 * Handle solicited LOGO response
6624 */
6625static void
6626fp_logo_intr(fc_packet_t *pkt)
6627{
6628	ls_code_t	resp;
6629
6630	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp,
6631	    (uint8_t *)pkt->pkt_resp, sizeof (resp), DDI_DEV_AUTOINCR);
6632
6633	if (FP_IS_PKT_ERROR(pkt)) {
6634		(void) fp_common_intr(pkt, 1);
6635		return;
6636	}
6637
6638	ASSERT(resp.ls_code == LA_ELS_ACC);
6639	if (resp.ls_code != LA_ELS_ACC) {
6640		(void) fp_common_intr(pkt, 1);
6641		return;
6642	}
6643
6644	if (pkt->pkt_pd != NULL) {
6645		fp_unregister_login(pkt->pkt_pd);
6646	}
6647	fp_iodone(pkt->pkt_ulp_private);
6648}
6649
6650
6651/*
6652 * Handle solicited RNID response
6653 */
6654static void
6655fp_rnid_intr(fc_packet_t *pkt)
6656{
6657	ls_code_t		resp;
6658	job_request_t		*job;
6659	fp_cmd_t		*cmd;
6660	la_els_rnid_acc_t	*acc;
6661
6662	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp,
6663	    (uint8_t *)pkt->pkt_resp, sizeof (resp), DDI_DEV_AUTOINCR);
6664
6665	cmd = pkt->pkt_ulp_private;
6666	job = cmd->cmd_job;
6667	ASSERT(job->job_private != NULL);
6668
6669	/* If failure or LS_RJT then retry the packet, if needed */
6670	if (pkt->pkt_state != FC_PKT_SUCCESS || resp.ls_code != LA_ELS_ACC) {
6671		(void) fp_common_intr(pkt, 1);
6672		return;
6673	}
6674
6675	/* Save node_id memory allocated in ioctl code */
6676	acc = (la_els_rnid_acc_t *)pkt->pkt_resp;
6677
6678	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)job->job_private,
6679	    (uint8_t *)acc, sizeof (la_els_rnid_acc_t), DDI_DEV_AUTOINCR);
6680
6681	/* wakeup the ioctl thread and free the pkt */
6682	fp_iodone(cmd);
6683}
6684
6685
6686/*
6687 * Handle solicited RLS response
6688 */
6689static void
6690fp_rls_intr(fc_packet_t *pkt)
6691{
6692	ls_code_t		resp;
6693	job_request_t		*job;
6694	fp_cmd_t		*cmd;
6695	la_els_rls_acc_t	*acc;
6696
6697	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp,
6698	    (uint8_t *)pkt->pkt_resp, sizeof (resp), DDI_DEV_AUTOINCR);
6699
6700	cmd = pkt->pkt_ulp_private;
6701	job = cmd->cmd_job;
6702	ASSERT(job->job_private != NULL);
6703
6704	/* If failure or LS_RJT then retry the packet, if needed */
6705	if (FP_IS_PKT_ERROR(pkt) || resp.ls_code != LA_ELS_ACC) {
6706		(void) fp_common_intr(pkt, 1);
6707		return;
6708	}
6709
6710	/* Save link error status block in memory allocated in ioctl code */
6711	acc = (la_els_rls_acc_t *)pkt->pkt_resp;
6712
6713	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)job->job_private,
6714	    (uint8_t *)&acc->rls_link_params, sizeof (fc_rls_acc_t),
6715	    DDI_DEV_AUTOINCR);
6716
6717	/* wakeup the ioctl thread and free the pkt */
6718	fp_iodone(cmd);
6719}
6720
6721
6722/*
6723 * A solicited command completion interrupt (mostly for commands
6724 * that require almost no post processing such as SCR ELS)
6725 */
6726static void
6727fp_intr(fc_packet_t *pkt)
6728{
6729	if (FP_IS_PKT_ERROR(pkt)) {
6730		(void) fp_common_intr(pkt, 1);
6731		return;
6732	}
6733	fp_iodone(pkt->pkt_ulp_private);
6734}
6735
6736
6737/*
6738 * Handle the underlying port's state change
6739 */
6740static void
6741fp_statec_cb(opaque_t port_handle, uint32_t state)
6742{
6743	fc_local_port_t *port = port_handle;
6744	job_request_t 	*job;
6745
6746	/*
6747	 * If it is not possible to process the callbacks
6748	 * just drop the callback on the floor; Don't bother
6749	 * to do something that isn't safe at this time
6750	 */
6751	mutex_enter(&port->fp_mutex);
6752	if ((port->fp_soft_state &
6753	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN)) ||
6754	    (FC_PORT_STATE_MASK(port->fp_state) == FC_PORT_STATE_MASK(state))) {
6755		mutex_exit(&port->fp_mutex);
6756		return;
6757	}
6758
6759	if (port->fp_statec_busy == 0) {
6760		port->fp_soft_state |= FP_SOFT_IN_STATEC_CB;
6761#ifdef	DEBUG
6762	} else {
6763		ASSERT(port->fp_soft_state & FP_SOFT_IN_STATEC_CB);
6764#endif
6765	}
6766
6767	port->fp_statec_busy++;
6768
6769	/*
6770	 * For now, force the trusted method of device authentication (by
6771	 * PLOGI) when LIPs do not involve OFFLINE to ONLINE transition.
6772	 */
6773	if (FC_PORT_STATE_MASK(state) == FC_STATE_LIP ||
6774	    FC_PORT_STATE_MASK(state) == FC_STATE_LIP_LBIT_SET) {
6775		state = FC_PORT_SPEED_MASK(port->fp_state) | FC_STATE_LOOP;
6776		fp_port_offline(port, 0);
6777	}
6778	mutex_exit(&port->fp_mutex);
6779
6780	switch (FC_PORT_STATE_MASK(state)) {
6781	case FC_STATE_OFFLINE:
6782		job = fctl_alloc_job(JOB_PORT_OFFLINE,
6783		    JOB_TYPE_FCTL_ASYNC, NULL, NULL, KM_NOSLEEP);
6784		if (job == NULL) {
6785			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
6786			    " fp_statec_cb() couldn't submit a job "
6787			    " to the thread: failing..");
6788			mutex_enter(&port->fp_mutex);
6789			if (--port->fp_statec_busy == 0) {
6790				port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
6791			}
6792			mutex_exit(&port->fp_mutex);
6793			return;
6794		}
6795		mutex_enter(&port->fp_mutex);
6796		/*
6797		 * Zero out this field so that we do not retain
6798		 * the fabric name as its no longer valid
6799		 */
6800		bzero(&port->fp_fabric_name, sizeof (la_wwn_t));
6801		port->fp_state = state;
6802		mutex_exit(&port->fp_mutex);
6803
6804		fctl_enque_job(port, job);
6805		break;
6806
6807	case FC_STATE_ONLINE:
6808	case FC_STATE_LOOP:
6809		mutex_enter(&port->fp_mutex);
6810		port->fp_state = state;
6811
6812		if (port->fp_offline_tid) {
6813			timeout_id_t tid;
6814
6815			tid = port->fp_offline_tid;
6816			port->fp_offline_tid = NULL;
6817			mutex_exit(&port->fp_mutex);
6818			(void) untimeout(tid);
6819		} else {
6820			mutex_exit(&port->fp_mutex);
6821		}
6822
6823		job = fctl_alloc_job(JOB_PORT_ONLINE,
6824		    JOB_TYPE_FCTL_ASYNC, NULL, NULL, KM_NOSLEEP);
6825		if (job == NULL) {
6826			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
6827			    "fp_statec_cb() couldn't submit a job "
6828			    "to the thread: failing..");
6829
6830			mutex_enter(&port->fp_mutex);
6831			if (--port->fp_statec_busy == 0) {
6832				port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
6833			}
6834			mutex_exit(&port->fp_mutex);
6835			return;
6836		}
6837		fctl_enque_job(port, job);
6838		break;
6839
6840	case FC_STATE_RESET_REQUESTED:
6841		mutex_enter(&port->fp_mutex);
6842		port->fp_state = FC_STATE_OFFLINE;
6843		port->fp_soft_state |= FP_SOFT_IN_FCA_RESET;
6844		mutex_exit(&port->fp_mutex);
6845		/* FALLTHROUGH */
6846
6847	case FC_STATE_RESET:
6848		job = fctl_alloc_job(JOB_ULP_NOTIFY,
6849		    JOB_TYPE_FCTL_ASYNC, NULL, NULL, KM_NOSLEEP);
6850		if (job == NULL) {
6851			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
6852			    "fp_statec_cb() couldn't submit a job"
6853			    " to the thread: failing..");
6854
6855			mutex_enter(&port->fp_mutex);
6856			if (--port->fp_statec_busy == 0) {
6857				port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
6858			}
6859			mutex_exit(&port->fp_mutex);
6860			return;
6861		}
6862
6863		/* squeeze into some field in the job structure */
6864		job->job_ulp_listlen = FC_PORT_STATE_MASK(state);
6865		fctl_enque_job(port, job);
6866		break;
6867
6868	case FC_STATE_TARGET_PORT_RESET:
6869		(void) fp_ulp_notify(port, state, KM_NOSLEEP);
6870		/* FALLTHROUGH */
6871
6872	case FC_STATE_NAMESERVICE:
6873		/* FALLTHROUGH */
6874
6875	default:
6876		mutex_enter(&port->fp_mutex);
6877		if (--port->fp_statec_busy == 0) {
6878			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
6879		}
6880		mutex_exit(&port->fp_mutex);
6881		break;
6882	}
6883}
6884
6885
6886/*
6887 * Register with the Name Server for RSCNs
6888 */
6889static int
6890fp_ns_scr(fc_local_port_t *port, job_request_t *job, uchar_t scr_func,
6891    int sleep)
6892{
6893	uint32_t	s_id;
6894	uchar_t		class;
6895	fc_scr_req_t 	payload;
6896	fp_cmd_t 	*cmd;
6897	fc_packet_t 	*pkt;
6898
6899	mutex_enter(&port->fp_mutex);
6900	s_id = port->fp_port_id.port_id;
6901	class = port->fp_ns_login_class;
6902	mutex_exit(&port->fp_mutex);
6903
6904	cmd = fp_alloc_pkt(port, sizeof (fc_scr_req_t),
6905	    sizeof (fc_scr_resp_t), sleep, NULL);
6906	if (cmd == NULL) {
6907		return (FC_NOMEM);
6908	}
6909
6910	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
6911	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
6912	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
6913	cmd->cmd_retry_count = fp_retry_count;
6914	cmd->cmd_ulp_pkt = NULL;
6915
6916	pkt = &cmd->cmd_pkt;
6917	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
6918
6919	fp_els_init(cmd, s_id, 0xFFFFFD, fp_intr, job);
6920
6921	payload.ls_code.ls_code = LA_ELS_SCR;
6922	payload.ls_code.mbz = 0;
6923	payload.scr_rsvd = 0;
6924	payload.scr_func = scr_func;
6925
6926	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
6927	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
6928
6929	job->job_counter = 1;
6930
6931	if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
6932		fp_iodone(cmd);
6933	}
6934
6935	return (FC_SUCCESS);
6936}
6937
6938
6939/*
6940 * There are basically two methods to determine the total number of
6941 * devices out in the NS database; Reading the details of the two
6942 * methods described below, it shouldn't be hard to identify which
6943 * of the two methods is better.
6944 *
6945 *	Method 1.
6946 *		Iteratively issue GANs until all ports identifiers are walked
6947 *
6948 *	Method 2.
6949 *		Issue GID_PT (get port Identifiers) with Maximum residual
6950 *		field in the request CT HEADER set to accommodate only the
6951 *		CT HEADER in the response frame. And if FC-GS2 has been
6952 *		carefully read, the NS here has a chance to FS_ACC the
6953 *		request and indicate the residual size in the FS_ACC.
6954 *
6955 *	Method 2 is wonderful, although it's not mandatory for the NS
6956 *	to update the Maximum/Residual Field as can be seen in 4.3.1.6
6957 *	(note with particular care the use of the auxiliary verb 'may')
6958 *
6959 */
6960static int
6961fp_ns_get_devcount(fc_local_port_t *port, job_request_t *job, int create,
6962    int sleep)
6963{
6964	int		flags;
6965	int		rval;
6966	uint32_t	src_id;
6967	fctl_ns_req_t	*ns_cmd;
6968
6969	ASSERT(!MUTEX_HELD(&port->fp_mutex));
6970
6971	mutex_enter(&port->fp_mutex);
6972	src_id = port->fp_port_id.port_id;
6973	mutex_exit(&port->fp_mutex);
6974
6975	if (!create && (port->fp_options & FP_NS_SMART_COUNT)) {
6976		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pt_t),
6977		    sizeof (ns_resp_gid_pt_t), 0,
6978		    (FCTL_NS_GET_DEV_COUNT | FCTL_NS_NO_DATA_BUF), sleep);
6979
6980		if (ns_cmd == NULL) {
6981			return (FC_NOMEM);
6982		}
6983
6984		ns_cmd->ns_cmd_code = NS_GID_PT;
6985		((ns_req_gid_pt_t *)(ns_cmd->ns_cmd_buf))->port_type.port_type
6986		    = FC_NS_PORT_NX;	/* All port types */
6987		((ns_req_gid_pt_t *)(ns_cmd->ns_cmd_buf))->port_type.rsvd = 0;
6988
6989	} else {
6990		uint32_t ns_flags;
6991
6992		ns_flags = FCTL_NS_GET_DEV_COUNT | FCTL_NS_NO_DATA_BUF;
6993		if (create) {
6994			ns_flags |= FCTL_NS_CREATE_DEVICE;
6995		}
6996		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
6997		    sizeof (ns_resp_gan_t), sizeof (int), ns_flags, sleep);
6998
6999		if (ns_cmd == NULL) {
7000			return (FC_NOMEM);
7001		}
7002		ns_cmd->ns_gan_index = 0;
7003		ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
7004		ns_cmd->ns_cmd_code = NS_GA_NXT;
7005		ns_cmd->ns_gan_max = 0xFFFF;
7006
7007		((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = src_id;
7008		((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
7009	}
7010
7011	flags = job->job_flags;
7012	job->job_flags &= ~JOB_TYPE_FP_ASYNC;
7013	job->job_counter = 1;
7014
7015	rval = fp_ns_query(port, ns_cmd, job, 1, sleep);
7016	job->job_flags = flags;
7017
7018	if (!create && (port->fp_options & FP_NS_SMART_COUNT)) {
7019		uint16_t max_resid;
7020
7021		/*
7022		 * Revert to scanning the NS if NS_GID_PT isn't
7023		 * helping us figure out total number of devices.
7024		 */
7025		if (job->job_result != FC_SUCCESS ||
7026		    ns_cmd->ns_resp_hdr.ct_cmdrsp != FS_ACC_IU) {
7027			mutex_enter(&port->fp_mutex);
7028			port->fp_options &= ~FP_NS_SMART_COUNT;
7029			mutex_exit(&port->fp_mutex);
7030
7031			fctl_free_ns_cmd(ns_cmd);
7032			return (fp_ns_get_devcount(port, job, create, sleep));
7033		}
7034
7035		mutex_enter(&port->fp_mutex);
7036		port->fp_total_devices = 1;
7037		max_resid = ns_cmd->ns_resp_hdr.ct_aiusize;
7038		if (max_resid) {
7039			/*
7040			 * Since port identifier is 4 bytes and max_resid
7041			 * is also in WORDS, max_resid simply indicates
7042			 * the total number of port identifiers	not
7043			 * transferred
7044			 */
7045			port->fp_total_devices += max_resid;
7046		}
7047		mutex_exit(&port->fp_mutex);
7048	}
7049	mutex_enter(&port->fp_mutex);
7050	port->fp_total_devices = *((int *)ns_cmd->ns_data_buf);
7051	mutex_exit(&port->fp_mutex);
7052	fctl_free_ns_cmd(ns_cmd);
7053
7054	return (rval);
7055}
7056
7057/*
7058 * One heck of a function to serve userland.
7059 */
7060static int
7061fp_fciocmd(fc_local_port_t *port, intptr_t data, int mode, fcio_t *fcio)
7062{
7063	int 		rval = 0;
7064	int		jcode;
7065	uint32_t	ret;
7066	uchar_t		open_flag;
7067	fcio_t		*kfcio;
7068	job_request_t 	*job;
7069	boolean_t	use32 = B_FALSE;
7070
7071#ifdef _MULTI_DATAMODEL
7072	switch (ddi_model_convert_from(mode & FMODELS)) {
7073	case DDI_MODEL_ILP32:
7074		use32 = B_TRUE;
7075		break;
7076
7077	case DDI_MODEL_NONE:
7078	default:
7079		break;
7080	}
7081#endif
7082
7083	mutex_enter(&port->fp_mutex);
7084	if (port->fp_soft_state & (FP_SOFT_IN_STATEC_CB |
7085	    FP_SOFT_IN_UNSOL_CB)) {
7086		fcio->fcio_errno = FC_STATEC_BUSY;
7087		mutex_exit(&port->fp_mutex);
7088		rval = EAGAIN;
7089		if (fp_fcio_copyout(fcio, data, mode)) {
7090			rval = EFAULT;
7091		}
7092		return (rval);
7093	}
7094	open_flag = port->fp_flag;
7095	mutex_exit(&port->fp_mutex);
7096
7097	if (fp_check_perms(open_flag, fcio->fcio_cmd) != FC_SUCCESS) {
7098		fcio->fcio_errno = FC_FAILURE;
7099		rval = EACCES;
7100		if (fp_fcio_copyout(fcio, data, mode)) {
7101			rval = EFAULT;
7102		}
7103		return (rval);
7104	}
7105
7106	/*
7107	 * If an exclusive open was demanded during open, don't let
7108	 * either innocuous or devil threads to share the file
7109	 * descriptor and fire down exclusive access commands
7110	 */
7111	mutex_enter(&port->fp_mutex);
7112	if (port->fp_flag & FP_EXCL) {
7113		if (port->fp_flag & FP_EXCL_BUSY) {
7114			mutex_exit(&port->fp_mutex);
7115			fcio->fcio_errno = FC_FAILURE;
7116			return (EBUSY);
7117		}
7118		port->fp_flag |= FP_EXCL_BUSY;
7119	}
7120	mutex_exit(&port->fp_mutex);
7121
7122	switch (fcio->fcio_cmd) {
7123	case FCIO_GET_HOST_PARAMS: {
7124		fc_port_dev_t	*val;
7125		fc_port_dev32_t	*val32;
7126		int		index;
7127		int		lilp_device_count;
7128		fc_lilpmap_t	*lilp_map;
7129		uchar_t		*alpa_list;
7130
7131		if (use32 == B_TRUE) {
7132			if (fcio->fcio_olen != sizeof (*val32) ||
7133			    fcio->fcio_xfer != FCIO_XFER_READ) {
7134				rval = EINVAL;
7135				break;
7136			}
7137		} else {
7138			if (fcio->fcio_olen != sizeof (*val) ||
7139			    fcio->fcio_xfer != FCIO_XFER_READ) {
7140				rval = EINVAL;
7141				break;
7142			}
7143		}
7144
7145		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7146
7147		mutex_enter(&port->fp_mutex);
7148		val->dev_did = port->fp_port_id;
7149		val->dev_hard_addr = port->fp_hard_addr;
7150		val->dev_pwwn = port->fp_service_params.nport_ww_name;
7151		val->dev_nwwn = port->fp_service_params.node_ww_name;
7152		val->dev_state = port->fp_state;
7153
7154		lilp_map = &port->fp_lilp_map;
7155		alpa_list = &lilp_map->lilp_alpalist[0];
7156		lilp_device_count = lilp_map->lilp_length;
7157		for (index = 0; index < lilp_device_count; index++) {
7158			uint32_t d_id;
7159
7160			d_id = alpa_list[index];
7161			if (d_id == port->fp_port_id.port_id) {
7162				break;
7163			}
7164		}
7165		val->dev_did.priv_lilp_posit = (uint8_t)(index & 0xff);
7166
7167		bcopy(port->fp_fc4_types, val->dev_type,
7168		    sizeof (port->fp_fc4_types));
7169		mutex_exit(&port->fp_mutex);
7170
7171		if (use32 == B_TRUE) {
7172			val32 = kmem_zalloc(sizeof (*val32), KM_SLEEP);
7173
7174			val32->dev_did = val->dev_did;
7175			val32->dev_hard_addr = val->dev_hard_addr;
7176			val32->dev_pwwn = val->dev_pwwn;
7177			val32->dev_nwwn = val->dev_nwwn;
7178			val32->dev_state = val->dev_state;
7179			val32->dev_did.priv_lilp_posit =
7180			    val->dev_did.priv_lilp_posit;
7181
7182			bcopy(val->dev_type, val32->dev_type,
7183			    sizeof (port->fp_fc4_types));
7184
7185			if (fp_copyout((void *)val32, (void *)fcio->fcio_obuf,
7186			    fcio->fcio_olen, mode) == 0) {
7187				if (fp_fcio_copyout(fcio, data, mode)) {
7188					rval = EFAULT;
7189				}
7190			} else {
7191				rval = EFAULT;
7192			}
7193
7194			kmem_free(val32, sizeof (*val32));
7195		} else {
7196			if (fp_copyout((void *)val, (void *)fcio->fcio_obuf,
7197			    fcio->fcio_olen, mode) == 0) {
7198				if (fp_fcio_copyout(fcio, data, mode)) {
7199					rval = EFAULT;
7200				}
7201			} else {
7202				rval = EFAULT;
7203			}
7204		}
7205
7206		/* need to free "val" here */
7207		kmem_free(val, sizeof (*val));
7208		break;
7209	}
7210
7211	case FCIO_GET_OTHER_ADAPTER_PORTS: {
7212		uint32_t    index;
7213		char	    *tmpPath;
7214		fc_local_port_t   *tmpPort;
7215
7216		if (fcio->fcio_olen < MAXPATHLEN ||
7217		    fcio->fcio_ilen != sizeof (uint32_t)) {
7218			rval = EINVAL;
7219			break;
7220		}
7221		if (ddi_copyin(fcio->fcio_ibuf, &index, sizeof (index), mode)) {
7222			rval = EFAULT;
7223			break;
7224		}
7225
7226		tmpPort = fctl_get_adapter_port_by_index(port, index);
7227		if (tmpPort == NULL) {
7228			FP_TRACE(FP_NHEAD1(9, 0),
7229			    "User supplied index out of range");
7230			fcio->fcio_errno = FC_BADPORT;
7231			rval = EFAULT;
7232			if (fp_fcio_copyout(fcio, data, mode)) {
7233				rval = EFAULT;
7234			}
7235			break;
7236		}
7237
7238		tmpPath = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
7239		(void) ddi_pathname(tmpPort->fp_port_dip, tmpPath);
7240		if (fp_copyout((void *)tmpPath, (void *)fcio->fcio_obuf,
7241		    MAXPATHLEN, mode) == 0) {
7242			if (fp_fcio_copyout(fcio, data, mode)) {
7243				rval = EFAULT;
7244			}
7245		} else {
7246			rval = EFAULT;
7247		}
7248		kmem_free(tmpPath, MAXPATHLEN);
7249		break;
7250	}
7251
7252	case FCIO_NPIV_GET_ADAPTER_ATTRIBUTES:
7253	case FCIO_GET_ADAPTER_ATTRIBUTES: {
7254		fc_hba_adapter_attributes_t	*val;
7255		fc_hba_adapter_attributes32_t	*val32;
7256
7257		if (use32 == B_TRUE) {
7258			if (fcio->fcio_olen < sizeof (*val32) ||
7259			    fcio->fcio_xfer != FCIO_XFER_READ) {
7260				rval = EINVAL;
7261				break;
7262			}
7263		} else {
7264			if (fcio->fcio_olen < sizeof (*val) ||
7265			    fcio->fcio_xfer != FCIO_XFER_READ) {
7266				rval = EINVAL;
7267				break;
7268			}
7269		}
7270
7271		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7272		val->version = FC_HBA_ADAPTER_ATTRIBUTES_VERSION;
7273		mutex_enter(&port->fp_mutex);
7274		bcopy(port->fp_hba_port_attrs.manufacturer,
7275			val->Manufacturer,
7276			sizeof (val->Manufacturer));
7277		bcopy(port->fp_hba_port_attrs.serial_number,
7278			val->SerialNumber,
7279			sizeof (val->SerialNumber));
7280		bcopy(port->fp_hba_port_attrs.model,
7281			val->Model,
7282			sizeof (val->Model));
7283		bcopy(port->fp_hba_port_attrs.model_description,
7284			val->ModelDescription,
7285			sizeof (val->ModelDescription));
7286		bcopy(port->fp_sym_node_name, val->NodeSymbolicName,
7287		    sizeof (val->NodeSymbolicName));
7288		bcopy(port->fp_hba_port_attrs.hardware_version,
7289			val->HardwareVersion,
7290			sizeof (val->HardwareVersion));
7291		bcopy(port->fp_hba_port_attrs.option_rom_version,
7292			val->OptionROMVersion,
7293			sizeof (val->OptionROMVersion));
7294		bcopy(port->fp_hba_port_attrs.firmware_version,
7295			val->FirmwareVersion,
7296			sizeof (val->FirmwareVersion));
7297		val->VendorSpecificID =
7298			port->fp_hba_port_attrs.vendor_specific_id;
7299		bcopy(&port->fp_service_params.node_ww_name.raw_wwn,
7300			&val->NodeWWN.raw_wwn,
7301			sizeof (val->NodeWWN.raw_wwn));
7302
7303
7304		bcopy(port->fp_hba_port_attrs.driver_name,
7305			val->DriverName,
7306			sizeof (val->DriverName));
7307		bcopy(port->fp_hba_port_attrs.driver_version,
7308			val->DriverVersion,
7309			sizeof (val->DriverVersion));
7310		mutex_exit(&port->fp_mutex);
7311
7312		if (fcio->fcio_cmd == FCIO_GET_ADAPTER_ATTRIBUTES) {
7313			val->NumberOfPorts = fctl_count_fru_ports(port, 0);
7314		} else {
7315			val->NumberOfPorts = fctl_count_fru_ports(port, 1);
7316		}
7317
7318		if (use32 == B_TRUE) {
7319			val32 = kmem_zalloc(sizeof (*val32), KM_SLEEP);
7320			val32->version = val->version;
7321			bcopy(val->Manufacturer, val32->Manufacturer,
7322				sizeof (val->Manufacturer));
7323			bcopy(val->SerialNumber, val32->SerialNumber,
7324				sizeof (val->SerialNumber));
7325			bcopy(val->Model, val32->Model,
7326				sizeof (val->Model));
7327			bcopy(val->ModelDescription, val32->ModelDescription,
7328				sizeof (val->ModelDescription));
7329			bcopy(val->NodeSymbolicName, val32->NodeSymbolicName,
7330			    sizeof (val->NodeSymbolicName));
7331			bcopy(val->HardwareVersion, val32->HardwareVersion,
7332				sizeof (val->HardwareVersion));
7333			bcopy(val->OptionROMVersion, val32->OptionROMVersion,
7334				sizeof (val->OptionROMVersion));
7335			bcopy(val->FirmwareVersion, val32->FirmwareVersion,
7336				sizeof (val->FirmwareVersion));
7337			val32->VendorSpecificID = val->VendorSpecificID;
7338			bcopy(&val->NodeWWN.raw_wwn, &val32->NodeWWN.raw_wwn,
7339				sizeof (val->NodeWWN.raw_wwn));
7340			bcopy(val->DriverName, val32->DriverName,
7341				sizeof (val->DriverName));
7342			bcopy(val->DriverVersion, val32->DriverVersion,
7343				sizeof (val->DriverVersion));
7344
7345			val32->NumberOfPorts = val->NumberOfPorts;
7346
7347			if (fp_copyout((void *)val32, (void *)fcio->fcio_obuf,
7348			    fcio->fcio_olen, mode) == 0) {
7349				if (fp_fcio_copyout(fcio, data, mode)) {
7350					rval = EFAULT;
7351				}
7352			} else {
7353				rval = EFAULT;
7354			}
7355
7356			kmem_free(val32, sizeof (*val32));
7357		} else {
7358			if (fp_copyout((void *)val, (void *)fcio->fcio_obuf,
7359			    fcio->fcio_olen, mode) == 0) {
7360				if (fp_fcio_copyout(fcio, data, mode)) {
7361					rval = EFAULT;
7362				}
7363			} else {
7364				rval = EFAULT;
7365			}
7366		}
7367
7368		kmem_free(val, sizeof (*val));
7369		break;
7370	}
7371
7372	case FCIO_GET_NPIV_ATTRIBUTES: {
7373		fc_hba_npiv_attributes_t *attrs;
7374
7375		attrs = kmem_zalloc(sizeof (*attrs), KM_SLEEP);
7376		mutex_enter(&port->fp_mutex);
7377		bcopy(&port->fp_service_params.node_ww_name.raw_wwn,
7378		    &attrs->NodeWWN.raw_wwn,
7379		    sizeof (attrs->NodeWWN.raw_wwn));
7380		bcopy(&port->fp_service_params.nport_ww_name.raw_wwn,
7381		    &attrs->PortWWN.raw_wwn,
7382		    sizeof (attrs->PortWWN.raw_wwn));
7383		mutex_exit(&port->fp_mutex);
7384		if (fp_copyout((void *)attrs, (void *)fcio->fcio_obuf,
7385		    fcio->fcio_olen, mode) == 0) {
7386			if (fp_fcio_copyout(fcio, data, mode)) {
7387				rval = EFAULT;
7388			}
7389		} else {
7390			rval = EFAULT;
7391		}
7392		kmem_free(attrs, sizeof (*attrs));
7393		break;
7394	}
7395
7396	case FCIO_DELETE_NPIV_PORT: {
7397		fc_local_port_t *tmpport;
7398		char	ww_pname[17];
7399		la_wwn_t	vwwn[1];
7400
7401		FP_TRACE(FP_NHEAD1(1, 0), "Delete NPIV Port");
7402		if (ddi_copyin(fcio->fcio_ibuf,
7403		    &vwwn, sizeof (la_wwn_t), mode)) {
7404			rval = EFAULT;
7405			break;
7406		}
7407
7408		fc_wwn_to_str(&vwwn[0], ww_pname);
7409		FP_TRACE(FP_NHEAD1(3, 0),
7410		    "Delete NPIV Port %s", ww_pname);
7411		tmpport = fc_delete_npiv_port(port, &vwwn[0]);
7412		if (tmpport == NULL) {
7413			FP_TRACE(FP_NHEAD1(3, 0),
7414			    "Delete NPIV Port : no found");
7415			rval = EFAULT;
7416		} else {
7417			fc_local_port_t *nextport = tmpport->fp_port_next;
7418			fc_local_port_t *prevport = tmpport->fp_port_prev;
7419			int portlen, portindex, ret;
7420
7421			portlen = sizeof (portindex);
7422			ret = ddi_prop_op(DDI_DEV_T_ANY,
7423			    tmpport->fp_port_dip, PROP_LEN_AND_VAL_BUF,
7424			    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "port",
7425			    (caddr_t)&portindex, &portlen);
7426			if (ret != DDI_SUCCESS) {
7427				rval = EFAULT;
7428				break;
7429			}
7430			if (ndi_devi_offline(tmpport->fp_port_dip,
7431			    NDI_DEVI_REMOVE) != DDI_SUCCESS) {
7432				FP_TRACE(FP_NHEAD1(1, 0),
7433				    "Delete NPIV Port failed");
7434				mutex_enter(&port->fp_mutex);
7435				tmpport->fp_npiv_state = 0;
7436				mutex_exit(&port->fp_mutex);
7437				rval = EFAULT;
7438			} else {
7439				mutex_enter(&port->fp_mutex);
7440				nextport->fp_port_prev = prevport;
7441				prevport->fp_port_next = nextport;
7442				if (port == port->fp_port_next) {
7443					port->fp_port_next =
7444					    port->fp_port_prev = NULL;
7445				}
7446				port->fp_npiv_portnum--;
7447				FP_TRACE(FP_NHEAD1(3, 0),
7448				    "Delete NPIV Port %d", portindex);
7449				port->fp_npiv_portindex[portindex-1] = 0;
7450				mutex_exit(&port->fp_mutex);
7451			}
7452		}
7453		break;
7454	}
7455
7456	case FCIO_CREATE_NPIV_PORT: {
7457		char ww_nname[17], ww_pname[17];
7458		la_npiv_create_entry_t entrybuf;
7459		uint32_t vportindex = 0;
7460		int npiv_ret = 0;
7461		char *portname, *fcaname;
7462
7463		portname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
7464		(void) ddi_pathname(port->fp_port_dip, portname);
7465		fcaname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
7466		(void) ddi_pathname(port->fp_fca_dip, fcaname);
7467		FP_TRACE(FP_NHEAD1(1, 0),
7468		    "Create NPIV port %s %s %s", portname, fcaname,
7469		    ddi_driver_name(port->fp_fca_dip));
7470		kmem_free(portname, MAXPATHLEN);
7471		kmem_free(fcaname, MAXPATHLEN);
7472		if (ddi_copyin(fcio->fcio_ibuf,
7473		    &entrybuf, sizeof (la_npiv_create_entry_t), mode)) {
7474			rval = EFAULT;
7475			break;
7476		}
7477
7478		fc_wwn_to_str(&entrybuf.VNodeWWN, ww_nname);
7479		fc_wwn_to_str(&entrybuf.VPortWWN, ww_pname);
7480		vportindex = entrybuf.vindex;
7481		FP_TRACE(FP_NHEAD1(3, 0),
7482		    "Create NPIV Port %s %s %d",
7483		    ww_nname, ww_pname, vportindex);
7484
7485		if (fc_get_npiv_port(port, &entrybuf.VPortWWN)) {
7486			rval = EFAULT;
7487			break;
7488		}
7489		npiv_ret = fctl_fca_create_npivport(port->fp_fca_dip,
7490		    port->fp_port_dip, ww_nname, ww_pname, &vportindex);
7491		if (npiv_ret == NDI_SUCCESS) {
7492			mutex_enter(&port->fp_mutex);
7493			port->fp_npiv_portnum++;
7494			mutex_exit(&port->fp_mutex);
7495			if (fp_copyout((void *)&vportindex,
7496			    (void *)fcio->fcio_obuf,
7497			    fcio->fcio_olen, mode) == 0) {
7498				if (fp_fcio_copyout(fcio, data, mode)) {
7499					rval = EFAULT;
7500				}
7501			} else {
7502				rval = EFAULT;
7503			}
7504		} else {
7505			rval = EFAULT;
7506		}
7507		FP_TRACE(FP_NHEAD1(3, 0),
7508		    "Create NPIV Port %d %d", npiv_ret, vportindex);
7509		break;
7510	}
7511
7512	case FCIO_GET_NPIV_PORT_LIST: {
7513		fc_hba_npiv_port_list_t *list;
7514		int count;
7515
7516		if ((fcio->fcio_xfer != FCIO_XFER_READ) ||
7517		    (fcio->fcio_olen == 0) || (fcio->fcio_obuf == 0)) {
7518			rval = EINVAL;
7519			break;
7520		}
7521
7522		list = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
7523		list->version = FC_HBA_LIST_VERSION;
7524		/* build npiv port list */
7525		count = fc_ulp_get_npiv_port_list(port, (char *)list->hbaPaths);
7526		if (count < 0) {
7527			rval = ENXIO;
7528			FP_TRACE(FP_NHEAD1(1, 0), "Build NPIV Port List error");
7529			kmem_free(list, fcio->fcio_olen);
7530			break;
7531		}
7532		list->numAdapters = count;
7533
7534		if (fp_copyout((void *)list, (void *)fcio->fcio_obuf,
7535		    fcio->fcio_olen, mode) == 0) {
7536			if (fp_fcio_copyout(fcio, data, mode)) {
7537				FP_TRACE(FP_NHEAD1(1, 0),
7538				    "Copy NPIV Port data error");
7539				rval = EFAULT;
7540			}
7541		} else {
7542			FP_TRACE(FP_NHEAD1(1, 0), "Copy NPIV Port List error");
7543			rval = EFAULT;
7544		}
7545		kmem_free(list, fcio->fcio_olen);
7546		break;
7547	}
7548
7549	case FCIO_GET_ADAPTER_PORT_NPIV_ATTRIBUTES: {
7550		fc_hba_port_npiv_attributes_t   *val;
7551
7552		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7553		val->version = FC_HBA_PORT_NPIV_ATTRIBUTES_VERSION;
7554
7555		mutex_enter(&port->fp_mutex);
7556		val->npivflag = port->fp_npiv_flag;
7557		val->lastChange = port->fp_last_change;
7558		bcopy(&port->fp_service_params.nport_ww_name.raw_wwn,
7559		    &val->PortWWN.raw_wwn,
7560		    sizeof (val->PortWWN.raw_wwn));
7561		bcopy(&port->fp_service_params.node_ww_name.raw_wwn,
7562		    &val->NodeWWN.raw_wwn,
7563		    sizeof (val->NodeWWN.raw_wwn));
7564		mutex_exit(&port->fp_mutex);
7565
7566		val->NumberOfNPIVPorts = fc_ulp_get_npiv_port_num(port);
7567		if (port->fp_npiv_type != FC_NPIV_PORT) {
7568			val->MaxNumberOfNPIVPorts =
7569			    port->fp_fca_tran->fca_num_npivports;
7570		} else {
7571			val->MaxNumberOfNPIVPorts = 0;
7572		}
7573
7574		if (fp_copyout((void *)val, (void *)fcio->fcio_obuf,
7575		    fcio->fcio_olen, mode) == 0) {
7576			if (fp_fcio_copyout(fcio, data, mode)) {
7577				rval = EFAULT;
7578			}
7579		} else {
7580			rval = EFAULT;
7581		}
7582		kmem_free(val, sizeof (*val));
7583		break;
7584	}
7585
7586	case FCIO_GET_ADAPTER_PORT_ATTRIBUTES: {
7587		fc_hba_port_attributes_t	*val;
7588		fc_hba_port_attributes32_t	*val32;
7589
7590		if (use32 == B_TRUE) {
7591			if (fcio->fcio_olen < sizeof (*val32) ||
7592			    fcio->fcio_xfer != FCIO_XFER_READ) {
7593				rval = EINVAL;
7594				break;
7595			}
7596		} else {
7597			if (fcio->fcio_olen < sizeof (*val) ||
7598			    fcio->fcio_xfer != FCIO_XFER_READ) {
7599				rval = EINVAL;
7600				break;
7601			}
7602		}
7603
7604		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7605		val->version = FC_HBA_PORT_ATTRIBUTES_VERSION;
7606		mutex_enter(&port->fp_mutex);
7607		val->lastChange = port->fp_last_change;
7608		val->fp_minor = port->fp_instance;
7609
7610		bcopy(&port->fp_service_params.nport_ww_name.raw_wwn,
7611		    &val->PortWWN.raw_wwn,
7612		    sizeof (val->PortWWN.raw_wwn));
7613		bcopy(&port->fp_service_params.node_ww_name.raw_wwn,
7614		    &val->NodeWWN.raw_wwn,
7615		    sizeof (val->NodeWWN.raw_wwn));
7616		bcopy(&port->fp_fabric_name, &val->FabricName.raw_wwn,
7617		    sizeof (val->FabricName.raw_wwn));
7618
7619		val->PortFcId = port->fp_port_id.port_id;
7620
7621		switch (FC_PORT_STATE_MASK(port->fp_state)) {
7622		case FC_STATE_OFFLINE:
7623		    val->PortState = FC_HBA_PORTSTATE_OFFLINE;
7624		    break;
7625		case FC_STATE_ONLINE:
7626		case FC_STATE_LOOP:
7627		case FC_STATE_NAMESERVICE:
7628		    val->PortState = FC_HBA_PORTSTATE_ONLINE;
7629		    break;
7630		default:
7631		    val->PortState = FC_HBA_PORTSTATE_UNKNOWN;
7632		    break;
7633		}
7634
7635		/* Translate from LV to FC-HBA port type codes */
7636		switch (port->fp_port_type.port_type) {
7637		case FC_NS_PORT_N:
7638		    val->PortType = FC_HBA_PORTTYPE_NPORT;
7639		    break;
7640		case FC_NS_PORT_NL: /* Actually means loop for us */
7641		    val->PortType = FC_HBA_PORTTYPE_LPORT;
7642		    break;
7643		case FC_NS_PORT_F:
7644		    val->PortType = FC_HBA_PORTTYPE_FPORT;
7645		    break;
7646		case FC_NS_PORT_FL:
7647		    val->PortType = FC_HBA_PORTTYPE_FLPORT;
7648		    break;
7649		case FC_NS_PORT_E:
7650		    val->PortType = FC_HBA_PORTTYPE_EPORT;
7651		    break;
7652		default:
7653		    val->PortType = FC_HBA_PORTTYPE_OTHER;
7654		    break;
7655		}
7656
7657
7658		/*
7659		 * If fp has decided that the topology is public loop,
7660		 * we will indicate that using the appropriate
7661		 * FC HBA API constant.
7662		 */
7663		switch (port->fp_topology) {
7664		case FC_TOP_PUBLIC_LOOP:
7665			val->PortType = FC_HBA_PORTTYPE_NLPORT;
7666			break;
7667
7668		case FC_TOP_PT_PT:
7669			val->PortType = FC_HBA_PORTTYPE_PTP;
7670			break;
7671
7672		case FC_TOP_UNKNOWN:
7673			/*
7674			 * This should cover the case where nothing is connected
7675			 * to the port. Crystal+ is p'bly an exception here.
7676			 * For Crystal+, port 0 will come up as private loop
7677			 * (i.e fp_bind_state will be FC_STATE_LOOP) even when
7678			 * nothing is connected to it.
7679			 * Current plan is to let userland handle this.
7680			 */
7681			if (port->fp_bind_state == FC_STATE_OFFLINE)
7682				val->PortType = FC_HBA_PORTTYPE_UNKNOWN;
7683			break;
7684
7685		default:
7686			/*
7687			 * Do Nothing.
7688			 * Unused:
7689			 *   val->PortType = FC_HBA_PORTTYPE_GPORT;
7690			 */
7691			break;
7692		}
7693
7694		val->PortSupportedClassofService =
7695			port->fp_hba_port_attrs.supported_cos;
7696		val->PortSupportedFc4Types[0] = 0;
7697		bcopy(port->fp_fc4_types, val->PortActiveFc4Types,
7698		    sizeof (val->PortActiveFc4Types));
7699		bcopy(port->fp_sym_port_name, val->PortSymbolicName,
7700		    sizeof (val->PortSymbolicName));
7701		val->PortSupportedSpeed =
7702			port->fp_hba_port_attrs.supported_speed;
7703
7704		switch (FC_PORT_SPEED_MASK(port->fp_state)) {
7705		case FC_STATE_1GBIT_SPEED:
7706		    val->PortSpeed = FC_HBA_PORTSPEED_1GBIT;
7707		    break;
7708		case FC_STATE_2GBIT_SPEED:
7709		    val->PortSpeed = FC_HBA_PORTSPEED_2GBIT;
7710		    break;
7711		case FC_STATE_4GBIT_SPEED:
7712		    val->PortSpeed = FC_HBA_PORTSPEED_4GBIT;
7713		    break;
7714		case FC_STATE_8GBIT_SPEED:
7715		    val->PortSpeed = FC_HBA_PORTSPEED_8GBIT;
7716		    break;
7717		case FC_STATE_10GBIT_SPEED:
7718		    val->PortSpeed = FC_HBA_PORTSPEED_10GBIT;
7719		    break;
7720		case FC_STATE_16GBIT_SPEED:
7721		    val->PortSpeed = FC_HBA_PORTSPEED_16GBIT;
7722		    break;
7723		default:
7724		    val->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
7725		    break;
7726		}
7727		val->PortMaxFrameSize = port->fp_hba_port_attrs.max_frame_size;
7728		val->NumberofDiscoveredPorts = port->fp_dev_count;
7729		mutex_exit(&port->fp_mutex);
7730
7731		if (use32 == B_TRUE) {
7732			val32 = kmem_zalloc(sizeof (*val32), KM_SLEEP);
7733			val32->version = val->version;
7734			val32->lastChange = val->lastChange;
7735			val32->fp_minor = val->fp_minor;
7736
7737			bcopy(&val->PortWWN.raw_wwn, &val32->PortWWN.raw_wwn,
7738				sizeof (val->PortWWN.raw_wwn));
7739			bcopy(&val->NodeWWN.raw_wwn, &val32->NodeWWN.raw_wwn,
7740				sizeof (val->NodeWWN.raw_wwn));
7741			val32->PortFcId = val->PortFcId;
7742			val32->PortState = val->PortState;
7743			val32->PortType = val->PortType;
7744
7745			val32->PortSupportedClassofService =
7746			    val->PortSupportedClassofService;
7747			bcopy(val->PortActiveFc4Types,
7748			    val32->PortActiveFc4Types,
7749			    sizeof (val->PortActiveFc4Types));
7750			bcopy(val->PortSymbolicName, val32->PortSymbolicName,
7751			    sizeof (val->PortSymbolicName));
7752			bcopy(&val->FabricName, &val32->FabricName,
7753			    sizeof (val->FabricName.raw_wwn));
7754			val32->PortSupportedSpeed = val->PortSupportedSpeed;
7755			val32->PortSpeed = val->PortSpeed;
7756
7757			val32->PortMaxFrameSize = val->PortMaxFrameSize;
7758			val32->NumberofDiscoveredPorts =
7759			    val->NumberofDiscoveredPorts;
7760
7761			if (fp_copyout((void *)val32, (void *)fcio->fcio_obuf,
7762			    fcio->fcio_olen, mode) == 0) {
7763				if (fp_fcio_copyout(fcio, data, mode)) {
7764					rval = EFAULT;
7765				}
7766			} else {
7767				rval = EFAULT;
7768			}
7769
7770			kmem_free(val32, sizeof (*val32));
7771		} else {
7772			if (fp_copyout((void *)val, (void *)fcio->fcio_obuf,
7773			    fcio->fcio_olen, mode) == 0) {
7774				if (fp_fcio_copyout(fcio, data, mode)) {
7775					rval = EFAULT;
7776				}
7777			} else {
7778				rval = EFAULT;
7779			}
7780		}
7781
7782		kmem_free(val, sizeof (*val));
7783		break;
7784	}
7785
7786	case FCIO_GET_DISCOVERED_PORT_ATTRIBUTES: {
7787		fc_hba_port_attributes_t	*val;
7788		fc_hba_port_attributes32_t	*val32;
7789		uint32_t	index = 0;
7790		fc_remote_port_t *tmp_pd;
7791
7792		if (use32 == B_TRUE) {
7793			if (fcio->fcio_olen < sizeof (*val32) ||
7794			    fcio->fcio_xfer != FCIO_XFER_READ) {
7795				rval = EINVAL;
7796				break;
7797			}
7798		} else {
7799			if (fcio->fcio_olen < sizeof (*val) ||
7800			    fcio->fcio_xfer != FCIO_XFER_READ) {
7801				rval = EINVAL;
7802				break;
7803			}
7804		}
7805
7806		if (ddi_copyin(fcio->fcio_ibuf, &index, sizeof (index), mode)) {
7807			rval = EFAULT;
7808			break;
7809		}
7810
7811		if (index >= port->fp_dev_count) {
7812			FP_TRACE(FP_NHEAD1(9, 0),
7813			    "User supplied index out of range");
7814			fcio->fcio_errno = FC_OUTOFBOUNDS;
7815			rval = EINVAL;
7816			if (fp_fcio_copyout(fcio, data, mode)) {
7817				rval = EFAULT;
7818			}
7819			break;
7820		}
7821
7822		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7823		val->version = FC_HBA_PORT_ATTRIBUTES_VERSION;
7824
7825		mutex_enter(&port->fp_mutex);
7826		tmp_pd = fctl_lookup_pd_by_index(port, index);
7827
7828		if (tmp_pd == NULL) {
7829			fcio->fcio_errno = FC_BADPORT;
7830			rval = EINVAL;
7831		} else {
7832			val->lastChange = port->fp_last_change;
7833			val->fp_minor = port->fp_instance;
7834
7835			mutex_enter(&tmp_pd->pd_mutex);
7836			bcopy(&tmp_pd->pd_port_name.raw_wwn,
7837			    &val->PortWWN.raw_wwn,
7838			    sizeof (val->PortWWN.raw_wwn));
7839			bcopy(&tmp_pd->pd_remote_nodep->fd_node_name.raw_wwn,
7840			    &val->NodeWWN.raw_wwn,
7841			    sizeof (val->NodeWWN.raw_wwn));
7842			val->PortFcId = tmp_pd->pd_port_id.port_id;
7843			bcopy(tmp_pd->pd_spn, val->PortSymbolicName,
7844			    sizeof (val->PortSymbolicName));
7845			val->PortSupportedClassofService = tmp_pd->pd_cos;
7846			/*
7847			 * we will assume the sizeof these pd_fc4types and
7848			 * portActiveFc4Types will remain the same.  we could
7849			 * add in a check for it, but we decided it was unneeded
7850			 */
7851			bcopy((caddr_t)tmp_pd->pd_fc4types,
7852			    val->PortActiveFc4Types,
7853			    sizeof (tmp_pd->pd_fc4types));
7854			val->PortState =
7855			    fp_map_remote_port_state(tmp_pd->pd_state);
7856			mutex_exit(&tmp_pd->pd_mutex);
7857
7858			val->PortType = FC_HBA_PORTTYPE_UNKNOWN;
7859			val->PortSupportedFc4Types[0] = 0;
7860			val->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
7861			val->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
7862			val->PortMaxFrameSize = 0;
7863			val->NumberofDiscoveredPorts = 0;
7864
7865			if (use32 == B_TRUE) {
7866				val32 = kmem_zalloc(sizeof (*val32), KM_SLEEP);
7867				val32->version = val->version;
7868				val32->lastChange = val->lastChange;
7869				val32->fp_minor = val->fp_minor;
7870
7871				bcopy(&val->PortWWN.raw_wwn,
7872				    &val32->PortWWN.raw_wwn,
7873				    sizeof (val->PortWWN.raw_wwn));
7874				bcopy(&val->NodeWWN.raw_wwn,
7875				    &val32->NodeWWN.raw_wwn,
7876				    sizeof (val->NodeWWN.raw_wwn));
7877				val32->PortFcId = val->PortFcId;
7878				bcopy(val->PortSymbolicName,
7879				    val32->PortSymbolicName,
7880				    sizeof (val->PortSymbolicName));
7881				val32->PortSupportedClassofService =
7882				    val->PortSupportedClassofService;
7883				bcopy(val->PortActiveFc4Types,
7884				    val32->PortActiveFc4Types,
7885				    sizeof (tmp_pd->pd_fc4types));
7886
7887				val32->PortType = val->PortType;
7888				val32->PortState = val->PortState;
7889				val32->PortSupportedFc4Types[0] =
7890				    val->PortSupportedFc4Types[0];
7891				val32->PortSupportedSpeed =
7892				    val->PortSupportedSpeed;
7893				val32->PortSpeed = val->PortSpeed;
7894				val32->PortMaxFrameSize =
7895				    val->PortMaxFrameSize;
7896				val32->NumberofDiscoveredPorts =
7897				    val->NumberofDiscoveredPorts;
7898
7899				if (fp_copyout((void *)val32,
7900				    (void *)fcio->fcio_obuf,
7901				    fcio->fcio_olen, mode) == 0) {
7902					    if (fp_fcio_copyout(fcio,
7903						data, mode)) {
7904						    rval = EFAULT;
7905					    }
7906				} else {
7907					rval = EFAULT;
7908				}
7909
7910				kmem_free(val32, sizeof (*val32));
7911			} else {
7912				if (fp_copyout((void *)val,
7913				    (void *)fcio->fcio_obuf,
7914				    fcio->fcio_olen, mode) == 0) {
7915					if (fp_fcio_copyout(fcio, data, mode)) {
7916						rval = EFAULT;
7917					}
7918				} else {
7919					rval = EFAULT;
7920				}
7921			}
7922		}
7923
7924		mutex_exit(&port->fp_mutex);
7925		kmem_free(val, sizeof (*val));
7926		break;
7927	}
7928
7929	case FCIO_GET_PORT_ATTRIBUTES: {
7930		fc_hba_port_attributes_t    *val;
7931		fc_hba_port_attributes32_t  *val32;
7932		la_wwn_t		    wwn;
7933		fc_remote_port_t	    *tmp_pd;
7934
7935		if (use32 == B_TRUE) {
7936			if (fcio->fcio_olen < sizeof (*val32) ||
7937			    fcio->fcio_xfer != FCIO_XFER_READ) {
7938				rval = EINVAL;
7939				break;
7940			}
7941		} else {
7942			if (fcio->fcio_olen < sizeof (*val) ||
7943			    fcio->fcio_xfer != FCIO_XFER_READ) {
7944				rval = EINVAL;
7945				break;
7946			}
7947		}
7948
7949		if (ddi_copyin(fcio->fcio_ibuf, &wwn, sizeof (wwn), mode)) {
7950			rval = EFAULT;
7951			break;
7952		}
7953
7954		val = kmem_zalloc(sizeof (*val), KM_SLEEP);
7955		val->version = FC_HBA_PORT_ATTRIBUTES_VERSION;
7956
7957		mutex_enter(&port->fp_mutex);
7958		tmp_pd = fctl_lookup_pd_by_wwn(port, wwn);
7959		val->lastChange = port->fp_last_change;
7960		val->fp_minor = port->fp_instance;
7961		mutex_exit(&port->fp_mutex);
7962
7963		if (tmp_pd == NULL) {
7964			fcio->fcio_errno = FC_BADWWN;
7965			rval = EINVAL;
7966		} else {
7967			mutex_enter(&tmp_pd->pd_mutex);
7968			bcopy(&tmp_pd->pd_port_name.raw_wwn,
7969			    &val->PortWWN.raw_wwn,
7970			    sizeof (val->PortWWN.raw_wwn));
7971			bcopy(&tmp_pd->pd_remote_nodep->fd_node_name.raw_wwn,
7972			    &val->NodeWWN.raw_wwn,
7973			    sizeof (val->NodeWWN.raw_wwn));
7974			val->PortFcId = tmp_pd->pd_port_id.port_id;
7975			bcopy(tmp_pd->pd_spn, val->PortSymbolicName,
7976			    sizeof (val->PortSymbolicName));
7977			val->PortSupportedClassofService = tmp_pd->pd_cos;
7978			val->PortType = FC_HBA_PORTTYPE_UNKNOWN;
7979			val->PortState =
7980			    fp_map_remote_port_state(tmp_pd->pd_state);
7981			val->PortSupportedFc4Types[0] = 0;
7982			/*
7983			 * we will assume the sizeof these pd_fc4types and
7984			 * portActiveFc4Types will remain the same.  we could
7985			 * add in a check for it, but we decided it was unneeded
7986			 */
7987			bcopy((caddr_t)tmp_pd->pd_fc4types,
7988			    val->PortActiveFc4Types,
7989			    sizeof (tmp_pd->pd_fc4types));
7990			val->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
7991			val->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
7992			val->PortMaxFrameSize = 0;
7993			val->NumberofDiscoveredPorts = 0;
7994			mutex_exit(&tmp_pd->pd_mutex);
7995
7996			if (use32 == B_TRUE) {
7997				val32 = kmem_zalloc(sizeof (*val32), KM_SLEEP);
7998				val32->version = val->version;
7999				val32->lastChange = val->lastChange;
8000				val32->fp_minor = val->fp_minor;
8001				bcopy(&val->PortWWN.raw_wwn,
8002				    &val32->PortWWN.raw_wwn,
8003				    sizeof (val->PortWWN.raw_wwn));
8004				bcopy(&val->NodeWWN.raw_wwn,
8005				    &val32->NodeWWN.raw_wwn,
8006				    sizeof (val->NodeWWN.raw_wwn));
8007				val32->PortFcId = val->PortFcId;
8008				bcopy(val->PortSymbolicName,
8009				    val32->PortSymbolicName,
8010				    sizeof (val->PortSymbolicName));
8011				val32->PortSupportedClassofService =
8012				    val->PortSupportedClassofService;
8013				val32->PortType = val->PortType;
8014				val32->PortState = val->PortState;
8015				val32->PortSupportedFc4Types[0] =
8016				    val->PortSupportedFc4Types[0];
8017				bcopy(val->PortActiveFc4Types,
8018				    val32->PortActiveFc4Types,
8019				    sizeof (tmp_pd->pd_fc4types));
8020				val32->PortSupportedSpeed =
8021				    val->PortSupportedSpeed;
8022				val32->PortSpeed = val->PortSpeed;
8023				val32->PortMaxFrameSize = val->PortMaxFrameSize;
8024				val32->NumberofDiscoveredPorts =
8025				    val->NumberofDiscoveredPorts;
8026
8027				if (fp_copyout((void *)val32,
8028				    (void *)fcio->fcio_obuf,
8029				    fcio->fcio_olen, mode) == 0) {
8030					if (fp_fcio_copyout(fcio, data, mode)) {
8031						rval = EFAULT;
8032					}
8033				} else {
8034					rval = EFAULT;
8035				}
8036
8037				kmem_free(val32, sizeof (*val32));
8038			} else {
8039				if (fp_copyout((void *)val,
8040				    (void *)fcio->fcio_obuf,
8041				    fcio->fcio_olen, mode) == 0) {
8042					if (fp_fcio_copyout(fcio, data, mode)) {
8043						rval = EFAULT;
8044					}
8045				} else {
8046					rval = EFAULT;
8047				}
8048			}
8049		}
8050		kmem_free(val, sizeof (*val));
8051		break;
8052	}
8053
8054	case FCIO_GET_NUM_DEVS: {
8055		int num_devices;
8056
8057		if (fcio->fcio_olen != sizeof (num_devices) ||
8058		    fcio->fcio_xfer != FCIO_XFER_READ) {
8059			rval = EINVAL;
8060			break;
8061		}
8062
8063		mutex_enter(&port->fp_mutex);
8064		switch (port->fp_topology) {
8065		case FC_TOP_PRIVATE_LOOP:
8066		case FC_TOP_PT_PT:
8067			num_devices = port->fp_total_devices;
8068			fcio->fcio_errno = FC_SUCCESS;
8069			break;
8070
8071		case FC_TOP_PUBLIC_LOOP:
8072		case FC_TOP_FABRIC:
8073			mutex_exit(&port->fp_mutex);
8074			job = fctl_alloc_job(JOB_NS_CMD, 0, NULL,
8075			    NULL, KM_SLEEP);
8076			ASSERT(job != NULL);
8077
8078			/*
8079			 * In FC-GS-2 the Name Server doesn't send out
8080			 * RSCNs for any Name Server Database updates
8081			 * When it is finally fixed there is no need
8082			 * to probe as below and should be removed.
8083			 */
8084			(void) fp_ns_get_devcount(port, job, 0, KM_SLEEP);
8085			fctl_dealloc_job(job);
8086
8087			mutex_enter(&port->fp_mutex);
8088			num_devices = port->fp_total_devices;
8089			fcio->fcio_errno = FC_SUCCESS;
8090			break;
8091
8092		case FC_TOP_NO_NS:
8093			/* FALLTHROUGH */
8094		case FC_TOP_UNKNOWN:
8095			/* FALLTHROUGH */
8096		default:
8097			num_devices = 0;
8098			fcio->fcio_errno = FC_SUCCESS;
8099			break;
8100		}
8101		mutex_exit(&port->fp_mutex);
8102
8103		if (fp_copyout((void *)&num_devices,
8104		    (void *)fcio->fcio_obuf, fcio->fcio_olen,
8105		    mode) == 0) {
8106			if (fp_fcio_copyout(fcio, data, mode)) {
8107				rval = EFAULT;
8108			}
8109		} else {
8110			rval = EFAULT;
8111		}
8112		break;
8113	}
8114
8115	case FCIO_GET_DEV_LIST: {
8116		int num_devices;
8117		int new_count;
8118		int map_size;
8119
8120		if (fcio->fcio_xfer != FCIO_XFER_READ ||
8121		    fcio->fcio_alen != sizeof (new_count)) {
8122			rval = EINVAL;
8123			break;
8124		}
8125
8126		num_devices = fcio->fcio_olen / sizeof (fc_port_dev_t);
8127
8128		mutex_enter(&port->fp_mutex);
8129		if (num_devices < port->fp_total_devices) {
8130			fcio->fcio_errno = FC_TOOMANY;
8131			new_count = port->fp_total_devices;
8132			mutex_exit(&port->fp_mutex);
8133
8134			if (fp_copyout((void *)&new_count,
8135			    (void *)fcio->fcio_abuf,
8136			    sizeof (new_count), mode)) {
8137				rval = EFAULT;
8138				break;
8139			}
8140
8141			if (fp_fcio_copyout(fcio, data, mode)) {
8142				rval = EFAULT;
8143				break;
8144			}
8145			rval = EINVAL;
8146			break;
8147		}
8148
8149		if (port->fp_total_devices <= 0) {
8150			fcio->fcio_errno = FC_NO_MAP;
8151			new_count = port->fp_total_devices;
8152			mutex_exit(&port->fp_mutex);
8153
8154			if (fp_copyout((void *)&new_count,
8155			    (void *)fcio->fcio_abuf,
8156			    sizeof (new_count), mode)) {
8157				rval = EFAULT;
8158				break;
8159			}
8160
8161			if (fp_fcio_copyout(fcio, data, mode)) {
8162				rval = EFAULT;
8163				break;
8164			}
8165			rval = EINVAL;
8166			break;
8167		}
8168
8169		switch (port->fp_topology) {
8170		case FC_TOP_PRIVATE_LOOP:
8171			if (fp_fillout_loopmap(port, fcio,
8172			    mode) != FC_SUCCESS) {
8173				rval = EFAULT;
8174				break;
8175			}
8176			if (fp_fcio_copyout(fcio, data, mode)) {
8177				rval = EFAULT;
8178			}
8179			break;
8180
8181		case FC_TOP_PT_PT:
8182			if (fp_fillout_p2pmap(port, fcio,
8183			    mode) != FC_SUCCESS) {
8184				rval = EFAULT;
8185				break;
8186			}
8187			if (fp_fcio_copyout(fcio, data, mode)) {
8188				rval = EFAULT;
8189			}
8190			break;
8191
8192		case FC_TOP_PUBLIC_LOOP:
8193		case FC_TOP_FABRIC: {
8194			fctl_ns_req_t *ns_cmd;
8195
8196			map_size =
8197			    sizeof (fc_port_dev_t) * port->fp_total_devices;
8198
8199			mutex_exit(&port->fp_mutex);
8200
8201			ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
8202			    sizeof (ns_resp_gan_t), map_size,
8203			    (FCTL_NS_FILL_NS_MAP | FCTL_NS_BUF_IS_USERLAND),
8204			    KM_SLEEP);
8205			ASSERT(ns_cmd != NULL);
8206
8207			ns_cmd->ns_gan_index = 0;
8208			ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
8209			ns_cmd->ns_cmd_code = NS_GA_NXT;
8210			ns_cmd->ns_gan_max = map_size / sizeof (fc_port_dev_t);
8211
8212			job = fctl_alloc_job(JOB_PORT_GETMAP, 0, NULL,
8213			    NULL, KM_SLEEP);
8214			ASSERT(job != NULL);
8215
8216			ret = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
8217
8218			if (ret != FC_SUCCESS ||
8219			    job->job_result != FC_SUCCESS) {
8220				fctl_free_ns_cmd(ns_cmd);
8221
8222				fcio->fcio_errno = job->job_result;
8223				new_count = 0;
8224				if (fp_copyout((void *)&new_count,
8225				    (void *)fcio->fcio_abuf,
8226				    sizeof (new_count), mode)) {
8227					fctl_dealloc_job(job);
8228					mutex_enter(&port->fp_mutex);
8229					rval = EFAULT;
8230					break;
8231				}
8232
8233				if (fp_fcio_copyout(fcio, data, mode)) {
8234					fctl_dealloc_job(job);
8235					mutex_enter(&port->fp_mutex);
8236					rval = EFAULT;
8237					break;
8238				}
8239				rval = EIO;
8240				mutex_enter(&port->fp_mutex);
8241				break;
8242			}
8243			fctl_dealloc_job(job);
8244
8245			new_count = ns_cmd->ns_gan_index;
8246			if (fp_copyout((void *)&new_count,
8247			    (void *)fcio->fcio_abuf, sizeof (new_count),
8248			    mode)) {
8249				rval = EFAULT;
8250				fctl_free_ns_cmd(ns_cmd);
8251				mutex_enter(&port->fp_mutex);
8252				break;
8253			}
8254
8255			if (fp_copyout((void *)ns_cmd->ns_data_buf,
8256			    (void *)fcio->fcio_obuf, sizeof (fc_port_dev_t) *
8257			    ns_cmd->ns_gan_index, mode)) {
8258				rval = EFAULT;
8259				fctl_free_ns_cmd(ns_cmd);
8260				mutex_enter(&port->fp_mutex);
8261				break;
8262			}
8263			fctl_free_ns_cmd(ns_cmd);
8264
8265			if (fp_fcio_copyout(fcio, data, mode)) {
8266				rval = EFAULT;
8267			}
8268			mutex_enter(&port->fp_mutex);
8269			break;
8270		}
8271
8272		case FC_TOP_NO_NS:
8273			/* FALLTHROUGH */
8274		case FC_TOP_UNKNOWN:
8275			/* FALLTHROUGH */
8276		default:
8277			fcio->fcio_errno = FC_NO_MAP;
8278			num_devices = port->fp_total_devices;
8279
8280			if (fp_copyout((void *)&new_count,
8281			    (void *)fcio->fcio_abuf,
8282			    sizeof (new_count), mode)) {
8283				rval = EFAULT;
8284				break;
8285			}
8286
8287			if (fp_fcio_copyout(fcio, data, mode)) {
8288				rval = EFAULT;
8289				break;
8290			}
8291			rval = EINVAL;
8292			break;
8293		}
8294		mutex_exit(&port->fp_mutex);
8295		break;
8296	}
8297
8298	case FCIO_GET_SYM_PNAME: {
8299		rval = ENOTSUP;
8300		break;
8301	}
8302
8303	case FCIO_GET_SYM_NNAME: {
8304		rval = ENOTSUP;
8305		break;
8306	}
8307
8308	case FCIO_SET_SYM_PNAME: {
8309		rval = ENOTSUP;
8310		break;
8311	}
8312
8313	case FCIO_SET_SYM_NNAME: {
8314		rval = ENOTSUP;
8315		break;
8316	}
8317
8318	case FCIO_GET_LOGI_PARAMS: {
8319		la_wwn_t		pwwn;
8320		la_wwn_t		*my_pwwn;
8321		la_els_logi_t		*params;
8322		la_els_logi32_t		*params32;
8323		fc_remote_node_t	*node;
8324		fc_remote_port_t	*pd;
8325
8326		if (fcio->fcio_ilen != sizeof (la_wwn_t) ||
8327		    (fcio->fcio_xfer & FCIO_XFER_READ) == 0 ||
8328		    (fcio->fcio_xfer & FCIO_XFER_WRITE) == 0) {
8329			rval = EINVAL;
8330			break;
8331		}
8332
8333		if (use32 == B_TRUE) {
8334			if (fcio->fcio_olen != sizeof (la_els_logi32_t)) {
8335				rval = EINVAL;
8336				break;
8337			}
8338		} else {
8339			if (fcio->fcio_olen != sizeof (la_els_logi_t)) {
8340				rval = EINVAL;
8341				break;
8342			}
8343		}
8344
8345		if (ddi_copyin(fcio->fcio_ibuf, &pwwn, sizeof (pwwn), mode)) {
8346			rval = EFAULT;
8347			break;
8348		}
8349
8350		pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
8351		if (pd == NULL) {
8352			mutex_enter(&port->fp_mutex);
8353			my_pwwn = &port->fp_service_params.nport_ww_name;
8354			mutex_exit(&port->fp_mutex);
8355
8356			if (fctl_wwn_cmp(&pwwn, my_pwwn) != 0) {
8357				rval = ENXIO;
8358				break;
8359			}
8360
8361			params = kmem_zalloc(sizeof (*params), KM_SLEEP);
8362			mutex_enter(&port->fp_mutex);
8363			*params = port->fp_service_params;
8364			mutex_exit(&port->fp_mutex);
8365		} else {
8366			params = kmem_zalloc(sizeof (*params), KM_SLEEP);
8367
8368			mutex_enter(&pd->pd_mutex);
8369			params->ls_code.mbz = params->ls_code.ls_code = 0;
8370			params->common_service = pd->pd_csp;
8371			params->nport_ww_name = pd->pd_port_name;
8372			params->class_1 = pd->pd_clsp1;
8373			params->class_2 = pd->pd_clsp2;
8374			params->class_3 = pd->pd_clsp3;
8375			node = pd->pd_remote_nodep;
8376			mutex_exit(&pd->pd_mutex);
8377
8378			bzero(params->reserved, sizeof (params->reserved));
8379
8380			mutex_enter(&node->fd_mutex);
8381			bcopy(node->fd_vv, params->vendor_version,
8382			    sizeof (node->fd_vv));
8383			params->node_ww_name = node->fd_node_name;
8384			mutex_exit(&node->fd_mutex);
8385
8386			fctl_release_remote_port(pd);
8387		}
8388
8389		if (use32 == B_TRUE) {
8390			params32 = kmem_zalloc(sizeof (*params32), KM_SLEEP);
8391
8392			params32->ls_code.mbz = params->ls_code.mbz;
8393			params32->common_service = params->common_service;
8394			params32->nport_ww_name = params->nport_ww_name;
8395			params32->class_1 = params->class_1;
8396			params32->class_2 = params->class_2;
8397			params32->class_3 = params->class_3;
8398			bzero(params32->reserved, sizeof (params32->reserved));
8399			bcopy(params->vendor_version, params32->vendor_version,
8400			    sizeof (node->fd_vv));
8401			params32->node_ww_name = params->node_ww_name;
8402
8403			if (ddi_copyout((void *)params32,
8404			    (void *)fcio->fcio_obuf,
8405			    sizeof (*params32), mode)) {
8406				rval = EFAULT;
8407			}
8408
8409			kmem_free(params32, sizeof (*params32));
8410		} else {
8411			if (ddi_copyout((void *)params, (void *)fcio->fcio_obuf,
8412			    sizeof (*params), mode)) {
8413				rval = EFAULT;
8414			}
8415		}
8416
8417		kmem_free(params, sizeof (*params));
8418		if (fp_fcio_copyout(fcio, data, mode)) {
8419			rval = EFAULT;
8420		}
8421		break;
8422	}
8423
8424	case FCIO_DEV_LOGOUT:
8425	case FCIO_DEV_LOGIN:
8426		if (fcio->fcio_ilen != sizeof (la_wwn_t) ||
8427		    fcio->fcio_xfer != FCIO_XFER_WRITE) {
8428			rval = EINVAL;
8429
8430			if (fp_fcio_copyout(fcio, data, mode)) {
8431				rval = EFAULT;
8432			}
8433			break;
8434		}
8435
8436		if (fcio->fcio_cmd == FCIO_DEV_LOGIN) {
8437			jcode = JOB_FCIO_LOGIN;
8438		} else {
8439			jcode = JOB_FCIO_LOGOUT;
8440		}
8441
8442		kfcio = kmem_zalloc(sizeof (*kfcio), KM_SLEEP);
8443		bcopy(fcio, kfcio, sizeof (*fcio));
8444
8445		if (kfcio->fcio_ilen) {
8446			kfcio->fcio_ibuf = kmem_zalloc(kfcio->fcio_ilen,
8447			    KM_SLEEP);
8448
8449			if (ddi_copyin((void *)fcio->fcio_ibuf,
8450			    (void *)kfcio->fcio_ibuf, kfcio->fcio_ilen,
8451			    mode)) {
8452				rval = EFAULT;
8453
8454				kmem_free(kfcio->fcio_ibuf, kfcio->fcio_ilen);
8455				kmem_free(kfcio, sizeof (*kfcio));
8456				fcio->fcio_errno = job->job_result;
8457				if (fp_fcio_copyout(fcio, data, mode)) {
8458					rval = EFAULT;
8459				}
8460				break;
8461			}
8462		}
8463
8464		job = fctl_alloc_job(jcode, 0, NULL, NULL, KM_SLEEP);
8465		job->job_private = kfcio;
8466
8467		fctl_enque_job(port, job);
8468		fctl_jobwait(job);
8469
8470		rval = job->job_result;
8471
8472		fcio->fcio_errno = kfcio->fcio_errno;
8473		if (fp_fcio_copyout(fcio, data, mode)) {
8474			rval = EFAULT;
8475		}
8476
8477		kmem_free(kfcio->fcio_ibuf, kfcio->fcio_ilen);
8478		kmem_free(kfcio, sizeof (*kfcio));
8479		fctl_dealloc_job(job);
8480		break;
8481
8482	case FCIO_GET_STATE: {
8483		la_wwn_t		pwwn;
8484		uint32_t		state;
8485		fc_remote_port_t 	*pd;
8486		fctl_ns_req_t		*ns_cmd;
8487
8488		if (fcio->fcio_ilen != sizeof (la_wwn_t) ||
8489		    fcio->fcio_olen != sizeof (state) ||
8490		    (fcio->fcio_xfer & FCIO_XFER_WRITE) == 0 ||
8491		    (fcio->fcio_xfer & FCIO_XFER_READ) == 0) {
8492			rval = EINVAL;
8493			break;
8494		}
8495
8496		if (ddi_copyin(fcio->fcio_ibuf, &pwwn, sizeof (pwwn), mode)) {
8497			rval = EFAULT;
8498			break;
8499		}
8500		fcio->fcio_errno = 0;
8501
8502		pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
8503		if (pd == NULL) {
8504			mutex_enter(&port->fp_mutex);
8505			if (FC_IS_TOP_SWITCH(port->fp_topology)) {
8506				mutex_exit(&port->fp_mutex);
8507				job = fctl_alloc_job(JOB_PLOGI_ONE, 0,
8508				    NULL, NULL, KM_SLEEP);
8509
8510				job->job_counter = 1;
8511				job->job_result = FC_SUCCESS;
8512
8513				ns_cmd = fctl_alloc_ns_cmd(
8514				    sizeof (ns_req_gid_pn_t),
8515				    sizeof (ns_resp_gid_pn_t),
8516				    sizeof (ns_resp_gid_pn_t),
8517				    FCTL_NS_BUF_IS_USERLAND, KM_SLEEP);
8518				ASSERT(ns_cmd != NULL);
8519
8520				ns_cmd->ns_cmd_code = NS_GID_PN;
8521				((ns_req_gid_pn_t *)
8522				    (ns_cmd->ns_cmd_buf))->pwwn = pwwn;
8523
8524				ret = fp_ns_query(port, ns_cmd, job,
8525				    1, KM_SLEEP);
8526
8527				if (ret != FC_SUCCESS || job->job_result !=
8528				    FC_SUCCESS) {
8529					if (ret != FC_SUCCESS) {
8530						fcio->fcio_errno = ret;
8531					} else {
8532						fcio->fcio_errno =
8533						    job->job_result;
8534					}
8535					rval = EIO;
8536				} else {
8537					state = PORT_DEVICE_INVALID;
8538				}
8539				fctl_free_ns_cmd(ns_cmd);
8540				fctl_dealloc_job(job);
8541			} else {
8542				mutex_exit(&port->fp_mutex);
8543				fcio->fcio_errno = FC_BADWWN;
8544				rval = ENXIO;
8545			}
8546		} else {
8547			mutex_enter(&pd->pd_mutex);
8548			state = pd->pd_state;
8549			mutex_exit(&pd->pd_mutex);
8550
8551			fctl_release_remote_port(pd);
8552		}
8553
8554		if (!rval) {
8555			if (ddi_copyout((void *)&state,
8556			    (void *)fcio->fcio_obuf, sizeof (state),
8557			    mode)) {
8558				rval = EFAULT;
8559			}
8560		}
8561		if (fp_fcio_copyout(fcio, data, mode)) {
8562			rval = EFAULT;
8563		}
8564		break;
8565	}
8566
8567	case FCIO_DEV_REMOVE: {
8568		la_wwn_t	pwwn;
8569		fc_portmap_t	*changelist;
8570		fc_remote_port_t *pd;
8571
8572		if (fcio->fcio_ilen != sizeof (la_wwn_t) ||
8573		    fcio->fcio_xfer != FCIO_XFER_WRITE) {
8574			rval = EINVAL;
8575			break;
8576		}
8577
8578		if (ddi_copyin(fcio->fcio_ibuf, &pwwn, sizeof (pwwn), mode)) {
8579			rval = EFAULT;
8580			break;
8581		}
8582
8583		pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
8584		if (pd == NULL) {
8585			rval = ENXIO;
8586			fcio->fcio_errno = FC_BADWWN;
8587			if (fp_fcio_copyout(fcio, data, mode)) {
8588				rval = EFAULT;
8589			}
8590			break;
8591		}
8592
8593		mutex_enter(&pd->pd_mutex);
8594		if (pd->pd_ref_count > 1) {
8595			mutex_exit(&pd->pd_mutex);
8596
8597			rval = EBUSY;
8598			fcio->fcio_errno = FC_FAILURE;
8599			fctl_release_remote_port(pd);
8600
8601			if (fp_fcio_copyout(fcio, data, mode)) {
8602				rval = EFAULT;
8603			}
8604			break;
8605		}
8606		mutex_exit(&pd->pd_mutex);
8607
8608		changelist = kmem_zalloc(sizeof (*changelist), KM_SLEEP);
8609
8610		fctl_copy_portmap(changelist, pd);
8611		changelist->map_type = PORT_DEVICE_USER_LOGOUT;
8612		(void) fp_ulp_devc_cb(port, changelist, 1, 1, KM_SLEEP, 1);
8613
8614		fctl_release_remote_port(pd);
8615		break;
8616	}
8617
8618	case FCIO_GET_FCODE_REV: {
8619		caddr_t 	fcode_rev;
8620		fc_fca_pm_t	pm;
8621
8622		if (fcio->fcio_olen < FC_FCODE_REV_SIZE ||
8623		    fcio->fcio_xfer != FCIO_XFER_READ) {
8624			rval = EINVAL;
8625			break;
8626		}
8627		bzero((caddr_t)&pm, sizeof (pm));
8628
8629		fcode_rev = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
8630
8631		pm.pm_cmd_flags = FC_FCA_PM_READ;
8632		pm.pm_cmd_code = FC_PORT_GET_FCODE_REV;
8633		pm.pm_data_len = fcio->fcio_olen;
8634		pm.pm_data_buf = fcode_rev;
8635
8636		ret = port->fp_fca_tran->fca_port_manage(
8637		    port->fp_fca_handle, &pm);
8638
8639		if (ret == FC_SUCCESS) {
8640			if (ddi_copyout((void *)fcode_rev,
8641			    (void *)fcio->fcio_obuf,
8642			    fcio->fcio_olen, mode) == 0) {
8643				if (fp_fcio_copyout(fcio, data, mode)) {
8644					rval = EFAULT;
8645				}
8646			} else {
8647				rval = EFAULT;
8648			}
8649		} else {
8650			/*
8651			 * check if buffer was not large enough to obtain
8652			 * FCODE version.
8653			 */
8654			if (pm.pm_data_len > fcio->fcio_olen) {
8655				rval = ENOMEM;
8656			} else {
8657				rval = EIO;
8658			}
8659			fcio->fcio_errno = ret;
8660			if (fp_fcio_copyout(fcio, data, mode)) {
8661				rval = EFAULT;
8662			}
8663		}
8664		kmem_free(fcode_rev, fcio->fcio_olen);
8665		break;
8666	}
8667
8668	case FCIO_GET_FW_REV: {
8669		caddr_t 	fw_rev;
8670		fc_fca_pm_t	pm;
8671
8672		if (fcio->fcio_olen < FC_FW_REV_SIZE ||
8673		    fcio->fcio_xfer != FCIO_XFER_READ) {
8674			rval = EINVAL;
8675			break;
8676		}
8677		bzero((caddr_t)&pm, sizeof (pm));
8678
8679		fw_rev = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
8680
8681		pm.pm_cmd_flags = FC_FCA_PM_READ;
8682		pm.pm_cmd_code = FC_PORT_GET_FW_REV;
8683		pm.pm_data_len = fcio->fcio_olen;
8684		pm.pm_data_buf = fw_rev;
8685
8686		ret = port->fp_fca_tran->fca_port_manage(
8687		    port->fp_fca_handle, &pm);
8688
8689		if (ret == FC_SUCCESS) {
8690			if (ddi_copyout((void *)fw_rev,
8691			    (void *)fcio->fcio_obuf,
8692			    fcio->fcio_olen, mode) == 0) {
8693				if (fp_fcio_copyout(fcio, data, mode)) {
8694					rval = EFAULT;
8695				}
8696			} else {
8697				rval = EFAULT;
8698			}
8699		} else {
8700			if (fp_fcio_copyout(fcio, data, mode)) {
8701				rval = EFAULT;
8702			}
8703			rval = EIO;
8704		}
8705		kmem_free(fw_rev, fcio->fcio_olen);
8706		break;
8707	}
8708
8709	case FCIO_GET_DUMP_SIZE: {
8710		uint32_t 	dump_size;
8711		fc_fca_pm_t	pm;
8712
8713		if (fcio->fcio_olen != sizeof (dump_size) ||
8714		    fcio->fcio_xfer != FCIO_XFER_READ) {
8715			rval = EINVAL;
8716			break;
8717		}
8718		bzero((caddr_t)&pm, sizeof (pm));
8719		pm.pm_cmd_flags = FC_FCA_PM_READ;
8720		pm.pm_cmd_code = FC_PORT_GET_DUMP_SIZE;
8721		pm.pm_data_len = sizeof (dump_size);
8722		pm.pm_data_buf = (caddr_t)&dump_size;
8723
8724		ret = port->fp_fca_tran->fca_port_manage(
8725		    port->fp_fca_handle, &pm);
8726
8727		if (ret == FC_SUCCESS) {
8728			if (ddi_copyout((void *)&dump_size,
8729			    (void *)fcio->fcio_obuf, sizeof (dump_size),
8730			    mode) == 0) {
8731				if (fp_fcio_copyout(fcio, data, mode)) {
8732					rval = EFAULT;
8733				}
8734			} else {
8735				rval = EFAULT;
8736			}
8737		} else {
8738			fcio->fcio_errno = ret;
8739			rval = EIO;
8740			if (fp_fcio_copyout(fcio, data, mode)) {
8741				rval = EFAULT;
8742			}
8743		}
8744		break;
8745	}
8746
8747	case FCIO_DOWNLOAD_FW: {
8748		caddr_t		firmware;
8749		fc_fca_pm_t	pm;
8750
8751		if (fcio->fcio_ilen <= 0 ||
8752		    fcio->fcio_xfer != FCIO_XFER_WRITE) {
8753			rval = EINVAL;
8754			break;
8755		}
8756
8757		firmware = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
8758		if (ddi_copyin(fcio->fcio_ibuf, firmware,
8759		    fcio->fcio_ilen, mode)) {
8760			rval = EFAULT;
8761			kmem_free(firmware, fcio->fcio_ilen);
8762			break;
8763		}
8764
8765		bzero((caddr_t)&pm, sizeof (pm));
8766		pm.pm_cmd_flags = FC_FCA_PM_WRITE;
8767		pm.pm_cmd_code = FC_PORT_DOWNLOAD_FW;
8768		pm.pm_data_len = fcio->fcio_ilen;
8769		pm.pm_data_buf = firmware;
8770
8771		ret = port->fp_fca_tran->fca_port_manage(
8772		    port->fp_fca_handle, &pm);
8773
8774		kmem_free(firmware, fcio->fcio_ilen);
8775
8776		if (ret != FC_SUCCESS) {
8777			fcio->fcio_errno = ret;
8778			rval = EIO;
8779			if (fp_fcio_copyout(fcio, data, mode)) {
8780				rval = EFAULT;
8781			}
8782		}
8783		break;
8784	}
8785
8786	case FCIO_DOWNLOAD_FCODE: {
8787		caddr_t		fcode;
8788		fc_fca_pm_t	pm;
8789
8790		if (fcio->fcio_ilen <= 0 ||
8791		    fcio->fcio_xfer != FCIO_XFER_WRITE) {
8792			rval = EINVAL;
8793			break;
8794		}
8795
8796		fcode = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
8797		if (ddi_copyin(fcio->fcio_ibuf, fcode,
8798		    fcio->fcio_ilen, mode)) {
8799			rval = EFAULT;
8800			kmem_free(fcode, fcio->fcio_ilen);
8801			break;
8802		}
8803
8804		bzero((caddr_t)&pm, sizeof (pm));
8805		pm.pm_cmd_flags = FC_FCA_PM_WRITE;
8806		pm.pm_cmd_code = FC_PORT_DOWNLOAD_FCODE;
8807		pm.pm_data_len = fcio->fcio_ilen;
8808		pm.pm_data_buf = fcode;
8809
8810		ret = port->fp_fca_tran->fca_port_manage(
8811		    port->fp_fca_handle, &pm);
8812
8813		kmem_free(fcode, fcio->fcio_ilen);
8814
8815		if (ret != FC_SUCCESS) {
8816			fcio->fcio_errno = ret;
8817			rval = EIO;
8818			if (fp_fcio_copyout(fcio, data, mode)) {
8819				rval = EFAULT;
8820			}
8821		}
8822		break;
8823	}
8824
8825	case FCIO_FORCE_DUMP:
8826		ret = port->fp_fca_tran->fca_reset(
8827		    port->fp_fca_handle, FC_FCA_CORE);
8828
8829		if (ret != FC_SUCCESS) {
8830			fcio->fcio_errno = ret;
8831			rval = EIO;
8832			if (fp_fcio_copyout(fcio, data, mode)) {
8833				rval = EFAULT;
8834			}
8835		}
8836		break;
8837
8838	case FCIO_GET_DUMP: {
8839		caddr_t		dump;
8840		uint32_t 	dump_size;
8841		fc_fca_pm_t	pm;
8842
8843		if (fcio->fcio_xfer != FCIO_XFER_READ) {
8844			rval = EINVAL;
8845			break;
8846		}
8847		bzero((caddr_t)&pm, sizeof (pm));
8848
8849		pm.pm_cmd_flags = FC_FCA_PM_READ;
8850		pm.pm_cmd_code = FC_PORT_GET_DUMP_SIZE;
8851		pm.pm_data_len = sizeof (dump_size);
8852		pm.pm_data_buf = (caddr_t)&dump_size;
8853
8854		ret = port->fp_fca_tran->fca_port_manage(
8855		    port->fp_fca_handle, &pm);
8856
8857		if (ret != FC_SUCCESS) {
8858			fcio->fcio_errno = ret;
8859			rval = EIO;
8860			if (fp_fcio_copyout(fcio, data, mode)) {
8861				rval = EFAULT;
8862			}
8863			break;
8864		}
8865		if (fcio->fcio_olen != dump_size) {
8866			fcio->fcio_errno = FC_NOMEM;
8867			rval = EINVAL;
8868			if (fp_fcio_copyout(fcio, data, mode)) {
8869				rval = EFAULT;
8870			}
8871			break;
8872		}
8873
8874		dump = kmem_zalloc(dump_size, KM_SLEEP);
8875
8876		bzero((caddr_t)&pm, sizeof (pm));
8877		pm.pm_cmd_flags = FC_FCA_PM_READ;
8878		pm.pm_cmd_code = FC_PORT_GET_DUMP;
8879		pm.pm_data_len = dump_size;
8880		pm.pm_data_buf = dump;
8881
8882		ret = port->fp_fca_tran->fca_port_manage(
8883		    port->fp_fca_handle, &pm);
8884
8885		if (ret == FC_SUCCESS) {
8886			if (ddi_copyout((void *)dump, (void *)fcio->fcio_obuf,
8887			    dump_size, mode) == 0) {
8888				if (fp_fcio_copyout(fcio, data, mode)) {
8889					rval = EFAULT;
8890				}
8891			} else {
8892				rval = EFAULT;
8893			}
8894		} else {
8895			fcio->fcio_errno = ret;
8896			rval = EIO;
8897			if (fp_fcio_copyout(fcio, data, mode)) {
8898				rval = EFAULT;
8899			}
8900		}
8901		kmem_free(dump, dump_size);
8902		break;
8903	}
8904
8905	case FCIO_GET_TOPOLOGY: {
8906		uint32_t user_topology;
8907
8908		if (fcio->fcio_xfer != FCIO_XFER_READ ||
8909		    fcio->fcio_olen != sizeof (user_topology)) {
8910			rval = EINVAL;
8911			break;
8912		}
8913
8914		mutex_enter(&port->fp_mutex);
8915		if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
8916			user_topology = FC_TOP_UNKNOWN;
8917		} else {
8918			user_topology = port->fp_topology;
8919		}
8920		mutex_exit(&port->fp_mutex);
8921
8922		if (ddi_copyout((void *)&user_topology,
8923		    (void *)fcio->fcio_obuf, sizeof (user_topology),
8924		    mode)) {
8925			rval = EFAULT;
8926		}
8927		break;
8928	}
8929
8930	case FCIO_RESET_LINK: {
8931		la_wwn_t pwwn;
8932
8933		/*
8934		 * Look at the output buffer field; if this field has zero
8935		 * bytes then attempt to reset the local link/loop. If the
8936		 * fcio_ibuf field points to a WWN, see if it's an NL_Port,
8937		 * and if yes, determine the LFA and reset the remote LIP
8938		 * by LINIT ELS.
8939		 */
8940
8941		if (fcio->fcio_xfer != FCIO_XFER_WRITE ||
8942		    fcio->fcio_ilen != sizeof (pwwn)) {
8943			rval = EINVAL;
8944			break;
8945		}
8946
8947		if (ddi_copyin(fcio->fcio_ibuf, &pwwn,
8948		    sizeof (pwwn), mode)) {
8949			rval = EFAULT;
8950			break;
8951		}
8952
8953		mutex_enter(&port->fp_mutex);
8954		if (port->fp_soft_state & FP_SOFT_IN_LINK_RESET) {
8955			mutex_exit(&port->fp_mutex);
8956			break;
8957		}
8958		port->fp_soft_state |= FP_SOFT_IN_LINK_RESET;
8959		mutex_exit(&port->fp_mutex);
8960
8961		job = fctl_alloc_job(JOB_LINK_RESET, 0, NULL, NULL, KM_SLEEP);
8962		if (job == NULL) {
8963			rval = ENOMEM;
8964			break;
8965		}
8966		job->job_counter = 1;
8967		job->job_private = (void *)&pwwn;
8968
8969		fctl_enque_job(port, job);
8970		fctl_jobwait(job);
8971
8972		mutex_enter(&port->fp_mutex);
8973		port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
8974		mutex_exit(&port->fp_mutex);
8975
8976		if (job->job_result != FC_SUCCESS) {
8977			fcio->fcio_errno = job->job_result;
8978			rval = EIO;
8979			if (fp_fcio_copyout(fcio, data, mode)) {
8980				rval = EFAULT;
8981			}
8982		}
8983		fctl_dealloc_job(job);
8984		break;
8985	}
8986
8987	case FCIO_RESET_HARD:
8988		ret = port->fp_fca_tran->fca_reset(
8989		    port->fp_fca_handle, FC_FCA_RESET);
8990		if (ret != FC_SUCCESS) {
8991			fcio->fcio_errno = ret;
8992			rval = EIO;
8993			if (fp_fcio_copyout(fcio, data, mode)) {
8994				rval = EFAULT;
8995			}
8996		}
8997		break;
8998
8999	case FCIO_RESET_HARD_CORE:
9000		ret = port->fp_fca_tran->fca_reset(
9001		    port->fp_fca_handle, FC_FCA_RESET_CORE);
9002		if (ret != FC_SUCCESS) {
9003			rval = EIO;
9004			fcio->fcio_errno = ret;
9005			if (fp_fcio_copyout(fcio, data, mode)) {
9006				rval = EFAULT;
9007			}
9008		}
9009		break;
9010
9011	case FCIO_DIAG: {
9012		fc_fca_pm_t pm;
9013
9014		bzero((caddr_t)&pm, sizeof (fc_fca_pm_t));
9015
9016		/* Validate user buffer from ioctl call. */
9017		if (((fcio->fcio_ilen > 0) && (fcio->fcio_ibuf == NULL)) ||
9018		    ((fcio->fcio_ilen <= 0) && (fcio->fcio_ibuf != NULL)) ||
9019		    ((fcio->fcio_alen > 0) && (fcio->fcio_abuf == NULL)) ||
9020		    ((fcio->fcio_alen <= 0) && (fcio->fcio_abuf != NULL)) ||
9021		    ((fcio->fcio_olen > 0) && (fcio->fcio_obuf == NULL)) ||
9022		    ((fcio->fcio_olen <= 0) && (fcio->fcio_obuf != NULL))) {
9023			rval = EFAULT;
9024			break;
9025		}
9026
9027		if ((pm.pm_cmd_len = fcio->fcio_ilen) > 0) {
9028			pm.pm_cmd_buf = kmem_zalloc(fcio->fcio_ilen, KM_SLEEP);
9029			if (ddi_copyin(fcio->fcio_ibuf, pm.pm_cmd_buf,
9030			    fcio->fcio_ilen, mode)) {
9031				rval = EFAULT;
9032				goto fp_fcio_diag_cleanup;
9033			}
9034		}
9035
9036		if ((pm.pm_data_len = fcio->fcio_alen) > 0) {
9037			pm.pm_data_buf = kmem_zalloc(fcio->fcio_alen, KM_SLEEP);
9038			if (ddi_copyin(fcio->fcio_abuf, pm.pm_data_buf,
9039			    fcio->fcio_alen, mode)) {
9040				rval = EFAULT;
9041				goto fp_fcio_diag_cleanup;
9042			}
9043		}
9044
9045		if ((pm.pm_stat_len = fcio->fcio_olen) > 0) {
9046			pm.pm_stat_buf = kmem_zalloc(fcio->fcio_olen, KM_SLEEP);
9047		}
9048
9049		pm.pm_cmd_code = FC_PORT_DIAG;
9050		pm.pm_cmd_flags = fcio->fcio_cmd_flags;
9051
9052		ret = port->fp_fca_tran->fca_port_manage(
9053		    port->fp_fca_handle, &pm);
9054
9055		if (ret != FC_SUCCESS) {
9056			if (ret == FC_INVALID_REQUEST) {
9057				rval = ENOTTY;
9058			} else {
9059				rval = EIO;
9060			}
9061
9062			fcio->fcio_errno = ret;
9063			if (fp_fcio_copyout(fcio, data, mode)) {
9064				rval = EFAULT;
9065			}
9066			goto fp_fcio_diag_cleanup;
9067		}
9068
9069		/*
9070		 * pm_stat_len will contain the number of status bytes
9071		 * an FCA driver requires to return the complete status
9072		 * of the requested diag operation. If the user buffer
9073		 * is not large enough to hold the entire status, We
9074		 * copy only the portion of data the fits in the buffer and
9075		 * return a ENOMEM to the user application.
9076		 */
9077		if (pm.pm_stat_len > fcio->fcio_olen) {
9078			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
9079			    "fp:FCIO_DIAG:status buffer too small\n");
9080
9081			rval = ENOMEM;
9082			if (ddi_copyout(pm.pm_stat_buf, fcio->fcio_obuf,
9083			    fcio->fcio_olen, mode)) {
9084				rval = EFAULT;
9085				goto fp_fcio_diag_cleanup;
9086			}
9087		} else {
9088			/*
9089			 * Copy only data pm_stat_len bytes of data
9090			 */
9091			if (ddi_copyout(pm.pm_stat_buf, fcio->fcio_obuf,
9092			    pm.pm_stat_len, mode)) {
9093				rval = EFAULT;
9094				goto fp_fcio_diag_cleanup;
9095			}
9096		}
9097
9098		if (fp_fcio_copyout(fcio, data, mode)) {
9099			rval = EFAULT;
9100		}
9101
9102fp_fcio_diag_cleanup:
9103		if (pm.pm_cmd_buf != NULL) {
9104			kmem_free(pm.pm_cmd_buf, fcio->fcio_ilen);
9105		}
9106		if (pm.pm_data_buf != NULL) {
9107			kmem_free(pm.pm_data_buf, fcio->fcio_alen);
9108		}
9109		if (pm.pm_stat_buf != NULL) {
9110			kmem_free(pm.pm_stat_buf, fcio->fcio_olen);
9111		}
9112
9113		break;
9114	}
9115
9116	case FCIO_GET_NODE_ID: {
9117		/* validate parameters */
9118		if (fcio->fcio_xfer != FCIO_XFER_READ ||
9119		    fcio->fcio_olen < sizeof (fc_rnid_t)) {
9120			rval = EINVAL;
9121			break;
9122		}
9123
9124		rval = fp_get_rnid(port, data, mode, fcio);
9125
9126		/* ioctl handling is over */
9127		break;
9128	}
9129
9130	case FCIO_SEND_NODE_ID: {
9131		la_wwn_t 		pwwn;
9132
9133		/* validate parameters */
9134		if (fcio->fcio_ilen != sizeof (la_wwn_t) ||
9135		    fcio->fcio_xfer != FCIO_XFER_READ) {
9136			rval = EINVAL;
9137			break;
9138		}
9139
9140		if (ddi_copyin(fcio->fcio_ibuf, &pwwn,
9141		    sizeof (la_wwn_t), mode)) {
9142			rval = EFAULT;
9143			break;
9144		}
9145
9146		rval = fp_send_rnid(port, data, mode, fcio, &pwwn);
9147
9148		/* ioctl handling is over */
9149		break;
9150	}
9151
9152	case FCIO_SET_NODE_ID: {
9153		if (fcio->fcio_ilen != sizeof (fc_rnid_t) ||
9154		    (fcio->fcio_xfer != FCIO_XFER_WRITE)) {
9155			rval = EINVAL;
9156			break;
9157		}
9158
9159		rval = fp_set_rnid(port, data, mode, fcio);
9160		break;
9161	}
9162
9163	case FCIO_LINK_STATUS: {
9164		fc_portid_t		rls_req;
9165		fc_rls_acc_t		*rls_acc;
9166		fc_fca_pm_t		pm;
9167		uint32_t		dest, src_id;
9168		fp_cmd_t		*cmd;
9169		fc_remote_port_t	*pd;
9170		uchar_t			pd_flags;
9171
9172		/* validate parameters */
9173		if (fcio->fcio_ilen != sizeof (fc_portid_t) ||
9174		    fcio->fcio_olen != sizeof (fc_rls_acc_t) ||
9175		    fcio->fcio_xfer != FCIO_XFER_RW) {
9176			rval = EINVAL;
9177			break;
9178		}
9179
9180		if ((fcio->fcio_cmd_flags != FCIO_CFLAGS_RLS_DEST_FPORT) &&
9181		    (fcio->fcio_cmd_flags != FCIO_CFLAGS_RLS_DEST_NPORT)) {
9182			rval = EINVAL;
9183			break;
9184		}
9185
9186		if (ddi_copyin((void *)fcio->fcio_ibuf, (void *)&rls_req,
9187		    sizeof (fc_portid_t), mode)) {
9188			rval = EFAULT;
9189			break;
9190		}
9191
9192
9193		/* Determine the destination of the RLS frame */
9194		if (fcio->fcio_cmd_flags == FCIO_CFLAGS_RLS_DEST_FPORT) {
9195			dest = FS_FABRIC_F_PORT;
9196		} else {
9197			dest = rls_req.port_id;
9198		}
9199
9200		mutex_enter(&port->fp_mutex);
9201		src_id = port->fp_port_id.port_id;
9202		mutex_exit(&port->fp_mutex);
9203
9204		/* If dest is zero OR same as FCA ID, then use port_manage() */
9205		if (dest == 0 || dest == src_id) {
9206
9207			/* Allocate memory for link error status block */
9208			rls_acc = kmem_zalloc(sizeof (*rls_acc), KM_SLEEP);
9209			ASSERT(rls_acc != NULL);
9210
9211			/* Prepare the port management structure */
9212			bzero((caddr_t)&pm, sizeof (pm));
9213
9214			pm.pm_cmd_flags = FC_FCA_PM_READ;
9215			pm.pm_cmd_code  = FC_PORT_RLS;
9216			pm.pm_data_len  = sizeof (*rls_acc);
9217			pm.pm_data_buf  = (caddr_t)rls_acc;
9218
9219			/* Get the adapter's link error status block */
9220			ret = port->fp_fca_tran->fca_port_manage(
9221			    port->fp_fca_handle, &pm);
9222
9223			if (ret == FC_SUCCESS) {
9224				/* xfer link status block to userland */
9225				if (ddi_copyout((void *)rls_acc,
9226				    (void *)fcio->fcio_obuf,
9227				    sizeof (*rls_acc), mode) == 0) {
9228					if (fp_fcio_copyout(fcio, data,
9229					    mode)) {
9230						rval = EFAULT;
9231					}
9232				} else {
9233					rval = EFAULT;
9234				}
9235			} else {
9236				rval = EIO;
9237				fcio->fcio_errno = ret;
9238				if (fp_fcio_copyout(fcio, data, mode)) {
9239					rval = EFAULT;
9240				}
9241			}
9242
9243			kmem_free(rls_acc, sizeof (*rls_acc));
9244
9245			/* ioctl handling is over */
9246			break;
9247		}
9248
9249		/*
9250		 * Send RLS to the destination port.
9251		 * Having RLS frame destination is as FPORT is not yet
9252		 * supported and will be implemented in future, if needed.
9253		 * Following call to get "pd" will fail if dest is FPORT
9254		 */
9255		pd = fctl_hold_remote_port_by_did(port, dest);
9256		if (pd == NULL) {
9257			fcio->fcio_errno = FC_BADOBJECT;
9258			rval = ENXIO;
9259			if (fp_fcio_copyout(fcio, data, mode)) {
9260				rval = EFAULT;
9261			}
9262			break;
9263		}
9264
9265		mutex_enter(&pd->pd_mutex);
9266		if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
9267			mutex_exit(&pd->pd_mutex);
9268			fctl_release_remote_port(pd);
9269
9270			fcio->fcio_errno = FC_LOGINREQ;
9271			rval = EINVAL;
9272			if (fp_fcio_copyout(fcio, data, mode)) {
9273				rval = EFAULT;
9274			}
9275			break;
9276		}
9277		ASSERT(pd->pd_login_count >= 1);
9278		mutex_exit(&pd->pd_mutex);
9279
9280		/*
9281		 * Allocate job structure and set job_code as DUMMY,
9282		 * because we will not go through the job thread.
9283		 * Instead fp_sendcmd() is called directly here.
9284		 */
9285		job = fctl_alloc_job(JOB_DUMMY, JOB_TYPE_FP_ASYNC,
9286		    NULL, NULL, KM_SLEEP);
9287		ASSERT(job != NULL);
9288
9289		job->job_counter = 1;
9290
9291		cmd = fp_alloc_pkt(port, sizeof (la_els_rls_t),
9292		    sizeof (la_els_rls_acc_t), KM_SLEEP, pd);
9293		if (cmd == NULL) {
9294			fcio->fcio_errno = FC_NOMEM;
9295			rval = ENOMEM;
9296
9297			fctl_release_remote_port(pd);
9298
9299			fctl_dealloc_job(job);
9300			if (fp_fcio_copyout(fcio, data, mode)) {
9301				rval = EFAULT;
9302			}
9303			break;
9304		}
9305
9306		/* Allocate memory for link error status block */
9307		rls_acc = kmem_zalloc(sizeof (*rls_acc), KM_SLEEP);
9308
9309		mutex_enter(&port->fp_mutex);
9310		mutex_enter(&pd->pd_mutex);
9311
9312		cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
9313		cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
9314		cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
9315		cmd->cmd_retry_count = 1;
9316		cmd->cmd_ulp_pkt = NULL;
9317
9318		fp_rls_init(cmd, job);
9319
9320		job->job_private = (void *)rls_acc;
9321
9322		pd_flags = pd->pd_flags;
9323		pd->pd_flags = PD_ELS_IN_PROGRESS;
9324
9325		mutex_exit(&pd->pd_mutex);
9326		mutex_exit(&port->fp_mutex);
9327
9328		if (fp_sendcmd(port, cmd, port->fp_fca_handle) == FC_SUCCESS) {
9329			fctl_jobwait(job);
9330
9331			fcio->fcio_errno = job->job_result;
9332			if (job->job_result == FC_SUCCESS) {
9333				ASSERT(pd != NULL);
9334				/*
9335				 * link error status block is now available.
9336				 * Copy it to userland
9337				 */
9338				ASSERT(job->job_private == (void *)rls_acc);
9339				if (ddi_copyout((void *)rls_acc,
9340				    (void *)fcio->fcio_obuf,
9341				    sizeof (*rls_acc), mode) == 0) {
9342					if (fp_fcio_copyout(fcio, data,
9343					    mode)) {
9344						rval = EFAULT;
9345					}
9346				} else {
9347					rval = EFAULT;
9348				}
9349			} else {
9350				rval = EIO;
9351			}
9352		} else {
9353			rval = EIO;
9354			fp_free_pkt(cmd);
9355		}
9356
9357		if (rval) {
9358			mutex_enter(&port->fp_mutex);
9359			mutex_enter(&pd->pd_mutex);
9360			if (pd->pd_flags == PD_ELS_IN_PROGRESS) {
9361				pd->pd_flags = pd_flags;
9362			}
9363			mutex_exit(&pd->pd_mutex);
9364			mutex_exit(&port->fp_mutex);
9365		}
9366
9367		fctl_release_remote_port(pd);
9368		fctl_dealloc_job(job);
9369		kmem_free(rls_acc, sizeof (*rls_acc));
9370
9371		if (fp_fcio_copyout(fcio, data, mode)) {
9372			rval = EFAULT;
9373		}
9374		break;
9375	}
9376
9377	case FCIO_NS: {
9378		fc_ns_cmd_t 	*ns_req;
9379		fc_ns_cmd32_t 	*ns_req32;
9380		fctl_ns_req_t 	*ns_cmd;
9381
9382		if (use32 == B_TRUE) {
9383			if (fcio->fcio_ilen != sizeof (*ns_req32)) {
9384				rval = EINVAL;
9385				break;
9386			}
9387
9388			ns_req = kmem_zalloc(sizeof (*ns_req), KM_SLEEP);
9389			ns_req32 = kmem_zalloc(sizeof (*ns_req32), KM_SLEEP);
9390
9391			if (ddi_copyin(fcio->fcio_ibuf, ns_req32,
9392			    sizeof (*ns_req32), mode)) {
9393				rval = EFAULT;
9394				kmem_free(ns_req, sizeof (*ns_req));
9395				kmem_free(ns_req32, sizeof (*ns_req32));
9396				break;
9397			}
9398
9399			ns_req->ns_flags = ns_req32->ns_flags;
9400			ns_req->ns_cmd = ns_req32->ns_cmd;
9401			ns_req->ns_req_len = ns_req32->ns_req_len;
9402			ns_req->ns_req_payload = ns_req32->ns_req_payload;
9403			ns_req->ns_resp_len = ns_req32->ns_resp_len;
9404			ns_req->ns_resp_payload = ns_req32->ns_resp_payload;
9405			ns_req->ns_fctl_private = ns_req32->ns_fctl_private;
9406			ns_req->ns_resp_hdr = ns_req32->ns_resp_hdr;
9407
9408			kmem_free(ns_req32, sizeof (*ns_req32));
9409		} else {
9410			if (fcio->fcio_ilen != sizeof (*ns_req)) {
9411				rval = EINVAL;
9412				break;
9413			}
9414
9415			ns_req = kmem_zalloc(sizeof (*ns_req), KM_SLEEP);
9416
9417			if (ddi_copyin(fcio->fcio_ibuf, ns_req,
9418			    sizeof (fc_ns_cmd_t), mode)) {
9419				rval = EFAULT;
9420				kmem_free(ns_req, sizeof (*ns_req));
9421				break;
9422			}
9423		}
9424
9425		if (ns_req->ns_req_len <= 0) {
9426			rval = EINVAL;
9427			kmem_free(ns_req, sizeof (*ns_req));
9428			break;
9429		}
9430
9431		job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
9432		ASSERT(job != NULL);
9433
9434		ns_cmd = fctl_alloc_ns_cmd(ns_req->ns_req_len,
9435		    ns_req->ns_resp_len, ns_req->ns_resp_len,
9436		    FCTL_NS_FILL_NS_MAP, KM_SLEEP);
9437		ASSERT(ns_cmd != NULL);
9438		ns_cmd->ns_cmd_code = ns_req->ns_cmd;
9439
9440		if (ns_cmd->ns_cmd_code == NS_GA_NXT) {
9441			ns_cmd->ns_gan_max = 1;
9442			ns_cmd->ns_gan_index = 0;
9443			ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
9444		}
9445
9446		if (ddi_copyin(ns_req->ns_req_payload,
9447		    ns_cmd->ns_cmd_buf, ns_req->ns_req_len, mode)) {
9448			rval = EFAULT;
9449			fctl_free_ns_cmd(ns_cmd);
9450			fctl_dealloc_job(job);
9451			kmem_free(ns_req, sizeof (*ns_req));
9452			break;
9453		}
9454
9455		job->job_private = (void *)ns_cmd;
9456		fctl_enque_job(port, job);
9457		fctl_jobwait(job);
9458		rval = job->job_result;
9459
9460		if (rval == FC_SUCCESS) {
9461			if (ns_req->ns_resp_len) {
9462				if (ddi_copyout(ns_cmd->ns_data_buf,
9463				    ns_req->ns_resp_payload,
9464				    ns_cmd->ns_data_len, mode)) {
9465					rval = EFAULT;
9466					fctl_free_ns_cmd(ns_cmd);
9467					fctl_dealloc_job(job);
9468					kmem_free(ns_req, sizeof (*ns_req));
9469					break;
9470				}
9471			}
9472		} else {
9473			rval = EIO;
9474		}
9475		ns_req->ns_resp_hdr = ns_cmd->ns_resp_hdr;
9476		fctl_free_ns_cmd(ns_cmd);
9477		fctl_dealloc_job(job);
9478		kmem_free(ns_req, sizeof (*ns_req));
9479
9480		if (fp_fcio_copyout(fcio, data, mode)) {
9481			rval = EFAULT;
9482		}
9483		break;
9484	}
9485
9486	default:
9487		rval = ENOTTY;
9488		break;
9489	}
9490
9491	/*
9492	 * If set, reset the EXCL busy bit to
9493	 * receive other exclusive access commands
9494	 */
9495	mutex_enter(&port->fp_mutex);
9496	if (port->fp_flag & FP_EXCL_BUSY) {
9497		port->fp_flag &= ~FP_EXCL_BUSY;
9498	}
9499	mutex_exit(&port->fp_mutex);
9500
9501	return (rval);
9502}
9503
9504
9505/*
9506 * This function assumes that the response length
9507 * is same regardless of data model (LP32 or LP64)
9508 * which is true for all the ioctls currently
9509 * supported.
9510 */
9511static int
9512fp_copyout(void *from, void *to, size_t len, int mode)
9513{
9514	return (ddi_copyout(from, to, len, mode));
9515}
9516
9517/*
9518 * This function does the set rnid
9519 */
9520static int
9521fp_set_rnid(fc_local_port_t *port, intptr_t data, int mode, fcio_t *fcio)
9522{
9523	int 		rval = 0;
9524	fc_rnid_t	*rnid;
9525	fc_fca_pm_t	pm;
9526
9527	/* Allocate memory for node id block */
9528	rnid = kmem_zalloc(sizeof (fc_rnid_t), KM_SLEEP);
9529
9530	if (ddi_copyin(fcio->fcio_ibuf, rnid, sizeof (fc_rnid_t), mode)) {
9531		FP_TRACE(FP_NHEAD1(3, 0), "fp_set_rnid: failed = %d", EFAULT);
9532		kmem_free(rnid, sizeof (fc_rnid_t));
9533		return (EFAULT);
9534	}
9535
9536	/* Prepare the port management structure */
9537	bzero((caddr_t)&pm, sizeof (pm));
9538
9539	pm.pm_cmd_flags = FC_FCA_PM_WRITE;
9540	pm.pm_cmd_code  = FC_PORT_SET_NODE_ID;
9541	pm.pm_data_len  = sizeof (*rnid);
9542	pm.pm_data_buf  = (caddr_t)rnid;
9543
9544	/* Get the adapter's node data */
9545	rval = port->fp_fca_tran->fca_port_manage(
9546	    port->fp_fca_handle, &pm);
9547
9548	if (rval != FC_SUCCESS) {
9549		fcio->fcio_errno = rval;
9550		rval = EIO;
9551		if (fp_fcio_copyout(fcio, data, mode)) {
9552			rval = EFAULT;
9553		}
9554	} else {
9555		mutex_enter(&port->fp_mutex);
9556		/* copy to the port structure */
9557		bcopy(rnid, &port->fp_rnid_params,
9558			sizeof (port->fp_rnid_params));
9559		mutex_exit(&port->fp_mutex);
9560	}
9561
9562	kmem_free(rnid, sizeof (fc_rnid_t));
9563
9564	if (rval != FC_SUCCESS) {
9565		FP_TRACE(FP_NHEAD1(3, 0), "fp_set_rnid: failed = %d", rval);
9566	}
9567
9568	return (rval);
9569}
9570
9571/*
9572 * This function does the local pwwn get rnid
9573 */
9574static int
9575fp_get_rnid(fc_local_port_t *port, intptr_t data, int mode, fcio_t *fcio)
9576{
9577	fc_rnid_t		*rnid;
9578	fc_fca_pm_t		pm;
9579	int 			rval = 0;
9580	uint32_t		ret;
9581
9582	/* Allocate memory for rnid data block */
9583	rnid = kmem_zalloc(sizeof (fc_rnid_t), KM_SLEEP);
9584
9585	mutex_enter(&port->fp_mutex);
9586	if (port->fp_rnid_init == 1) {
9587		bcopy(&port->fp_rnid_params, rnid, sizeof (fc_rnid_t));
9588		mutex_exit(&port->fp_mutex);
9589		/* xfer node info to userland */
9590		if (ddi_copyout((void *)rnid, (void *)fcio->fcio_obuf,
9591		    sizeof (*rnid), mode) == 0) {
9592			if (fp_fcio_copyout(fcio, data, mode)) {
9593				rval = EFAULT;
9594			}
9595		} else {
9596			rval = EFAULT;
9597		}
9598
9599		kmem_free(rnid, sizeof (fc_rnid_t));
9600
9601		if (rval != FC_SUCCESS) {
9602			FP_TRACE(FP_NHEAD1(3, 0), "fp_get_rnid: failed = %d",
9603			rval);
9604		}
9605
9606		return (rval);
9607	}
9608	mutex_exit(&port->fp_mutex);
9609
9610	/* Prepare the port management structure */
9611	bzero((caddr_t)&pm, sizeof (pm));
9612
9613	pm.pm_cmd_flags = FC_FCA_PM_READ;
9614	pm.pm_cmd_code  = FC_PORT_GET_NODE_ID;
9615	pm.pm_data_len  = sizeof (fc_rnid_t);
9616	pm.pm_data_buf  = (caddr_t)rnid;
9617
9618	/* Get the adapter's node data */
9619	ret = port->fp_fca_tran->fca_port_manage(
9620			port->fp_fca_handle,
9621			&pm);
9622
9623	if (ret == FC_SUCCESS) {
9624		/* initialize in the port_info */
9625		mutex_enter(&port->fp_mutex);
9626		port->fp_rnid_init = 1;
9627		bcopy(rnid, &port->fp_rnid_params, sizeof (*rnid));
9628		mutex_exit(&port->fp_mutex);
9629
9630		/* xfer node info to userland */
9631		if (ddi_copyout((void *)rnid,
9632				(void *)fcio->fcio_obuf,
9633				sizeof (*rnid), mode) == 0) {
9634			if (fp_fcio_copyout(fcio, data,
9635					mode)) {
9636				rval = EFAULT;
9637			}
9638		} else {
9639			rval = EFAULT;
9640		}
9641	} else {
9642		rval = EIO;
9643		fcio->fcio_errno = ret;
9644		if (fp_fcio_copyout(fcio, data, mode)) {
9645			rval = EFAULT;
9646		}
9647	}
9648
9649	kmem_free(rnid, sizeof (fc_rnid_t));
9650
9651	if (rval != FC_SUCCESS) {
9652		FP_TRACE(FP_NHEAD1(3, 0), "fp_get_rnid: failed = %d", rval);
9653	}
9654
9655	return (rval);
9656}
9657
9658static int
9659fp_send_rnid(fc_local_port_t *port, intptr_t data, int mode, fcio_t *fcio,
9660	la_wwn_t *pwwn)
9661{
9662	int 			rval = 0;
9663	fc_remote_port_t	*pd;
9664	fp_cmd_t		*cmd;
9665	job_request_t 		*job;
9666	la_els_rnid_acc_t	*rnid_acc;
9667
9668	pd = fctl_get_remote_port_by_pwwn(port, pwwn);
9669	if (pd == NULL) {
9670		/*
9671		 * We can safely assume that the destination port
9672		 * is logged in. Either the user land will explicitly
9673		 * login before issuing RNID ioctl or the device would
9674		 * have been configured, meaning already logged in.
9675		 */
9676
9677		FP_TRACE(FP_NHEAD1(3, 0), "fp_send_rnid: failed = %d", ENXIO);
9678
9679		return (ENXIO);
9680	}
9681	/*
9682	 * Allocate job structure and set job_code as DUMMY,
9683	 * because we will not go thorugh the job thread.
9684	 * Instead fp_sendcmd() is called directly here.
9685	 */
9686	job = fctl_alloc_job(JOB_DUMMY, JOB_TYPE_FP_ASYNC,
9687	    NULL, NULL, KM_SLEEP);
9688
9689	ASSERT(job != NULL);
9690
9691	job->job_counter = 1;
9692
9693	cmd = fp_alloc_pkt(port, sizeof (la_els_rnid_t),
9694	    sizeof (la_els_rnid_acc_t), KM_SLEEP, pd);
9695	if (cmd == NULL) {
9696		fcio->fcio_errno = FC_NOMEM;
9697		rval = ENOMEM;
9698
9699		fctl_dealloc_job(job);
9700		if (fp_fcio_copyout(fcio, data, mode)) {
9701			rval = EFAULT;
9702		}
9703
9704		FP_TRACE(FP_NHEAD1(3, 0), "fp_send_rnid: failed = %d", rval);
9705
9706		return (rval);
9707	}
9708
9709	/* Allocate memory for node id accept block */
9710	rnid_acc = kmem_zalloc(sizeof (la_els_rnid_acc_t), KM_SLEEP);
9711
9712	mutex_enter(&port->fp_mutex);
9713	mutex_enter(&pd->pd_mutex);
9714
9715	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
9716	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
9717	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
9718	cmd->cmd_retry_count = 1;
9719	cmd->cmd_ulp_pkt = NULL;
9720
9721	fp_rnid_init(cmd, fcio->fcio_cmd_flags, job);
9722
9723	job->job_private = (void *)rnid_acc;
9724
9725	pd->pd_flags = PD_ELS_IN_PROGRESS;
9726
9727	mutex_exit(&pd->pd_mutex);
9728	mutex_exit(&port->fp_mutex);
9729
9730	if (fp_sendcmd(port, cmd, port->fp_fca_handle) == FC_SUCCESS) {
9731		fctl_jobwait(job);
9732		fcio->fcio_errno = job->job_result;
9733		if (job->job_result == FC_SUCCESS) {
9734			int rnid_cnt;
9735			ASSERT(pd != NULL);
9736			/*
9737			 * node id block is now available.
9738			 * Copy it to userland
9739			 */
9740			ASSERT(job->job_private == (void *)rnid_acc);
9741
9742			/* get the response length */
9743			rnid_cnt = sizeof (ls_code_t) + sizeof (fc_rnid_hdr_t) +
9744					rnid_acc->hdr.cmn_len +
9745					rnid_acc->hdr.specific_len;
9746
9747			if (fcio->fcio_olen < rnid_cnt) {
9748				rval = EINVAL;
9749			} else if (ddi_copyout((void *)rnid_acc,
9750				(void *)fcio->fcio_obuf,
9751				rnid_cnt, mode) == 0) {
9752				if (fp_fcio_copyout(fcio, data,
9753					mode)) {
9754					rval = EFAULT;
9755				}
9756			} else {
9757				rval = EFAULT;
9758			}
9759		} else {
9760			rval = EIO;
9761		}
9762	} else {
9763		rval = EIO;
9764		if (pd) {
9765			mutex_enter(&pd->pd_mutex);
9766			pd->pd_flags = PD_IDLE;
9767			mutex_exit(&pd->pd_mutex);
9768		}
9769		fp_free_pkt(cmd);
9770	}
9771
9772	fctl_dealloc_job(job);
9773	kmem_free(rnid_acc, sizeof (la_els_rnid_acc_t));
9774
9775	if (fp_fcio_copyout(fcio, data, mode)) {
9776		rval = EFAULT;
9777	}
9778
9779	if (rval != FC_SUCCESS) {
9780		FP_TRACE(FP_NHEAD1(3, 0), "fp_send_rnid: failed = %d", rval);
9781	}
9782
9783	return (rval);
9784}
9785
9786/*
9787 * Copy out to userland
9788 */
9789static int
9790fp_fcio_copyout(fcio_t *fcio, intptr_t data, int mode)
9791{
9792	int rval;
9793
9794#ifdef	_MULTI_DATAMODEL
9795	switch (ddi_model_convert_from(mode & FMODELS)) {
9796	case DDI_MODEL_ILP32: {
9797		struct fcio32 fcio32;
9798
9799		fcio32.fcio_xfer = fcio->fcio_xfer;
9800		fcio32.fcio_cmd = fcio->fcio_cmd;
9801		fcio32.fcio_flags = fcio->fcio_flags;
9802		fcio32.fcio_cmd_flags = fcio->fcio_cmd_flags;
9803		fcio32.fcio_ilen = fcio->fcio_ilen;
9804		fcio32.fcio_ibuf =
9805			(caddr32_t)(uintptr_t)fcio->fcio_ibuf;
9806		fcio32.fcio_olen = fcio->fcio_olen;
9807		fcio32.fcio_obuf =
9808			(caddr32_t)(uintptr_t)fcio->fcio_obuf;
9809		fcio32.fcio_alen = fcio->fcio_alen;
9810		fcio32.fcio_abuf =
9811			(caddr32_t)(uintptr_t)fcio->fcio_abuf;
9812		fcio32.fcio_errno = fcio->fcio_errno;
9813
9814		rval = ddi_copyout((void *)&fcio32, (void *)data,
9815		    sizeof (struct fcio32), mode);
9816		break;
9817	}
9818	case DDI_MODEL_NONE:
9819		rval = ddi_copyout((void *)fcio, (void *)data,
9820		    sizeof (fcio_t), mode);
9821		break;
9822	}
9823#else
9824	rval = ddi_copyout((void *)fcio, (void *)data, sizeof (fcio_t), mode);
9825#endif
9826
9827	return (rval);
9828}
9829
9830
9831static void
9832fp_p2p_online(fc_local_port_t *port, job_request_t *job)
9833{
9834	uint32_t		listlen;
9835	fc_portmap_t		*changelist;
9836
9837	ASSERT(MUTEX_HELD(&port->fp_mutex));
9838	ASSERT(port->fp_topology == FC_TOP_PT_PT);
9839	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
9840
9841	listlen = 0;
9842	changelist = NULL;
9843
9844	if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
9845		if (port->fp_statec_busy > 1) {
9846			job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
9847		}
9848	}
9849	mutex_exit(&port->fp_mutex);
9850
9851	if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
9852		fctl_fillout_map(port, &changelist, &listlen, 1, 0, 0);
9853		(void) fp_ulp_statec_cb(port, FC_STATE_ONLINE, changelist,
9854		    listlen, listlen, KM_SLEEP);
9855
9856		mutex_enter(&port->fp_mutex);
9857	} else {
9858		ASSERT(changelist == NULL && listlen == 0);
9859		mutex_enter(&port->fp_mutex);
9860		if (--port->fp_statec_busy == 0) {
9861			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
9862		}
9863	}
9864}
9865
9866static int
9867fp_fillout_p2pmap(fc_local_port_t *port, fcio_t *fcio, int mode)
9868{
9869	int			rval;
9870	int			count;
9871	int			index;
9872	int			num_devices;
9873	fc_remote_node_t	*node;
9874	fc_port_dev_t 		*devlist;
9875	struct pwwn_hash 	*head;
9876	fc_remote_port_t 	*pd;
9877
9878	ASSERT(MUTEX_HELD(&port->fp_mutex));
9879
9880	num_devices = fcio->fcio_olen / sizeof (fc_port_dev_t);
9881
9882	devlist = kmem_zalloc(sizeof (fc_port_dev_t) * num_devices, KM_SLEEP);
9883
9884	for (count = index = 0; index < pwwn_table_size; index++) {
9885		head = &port->fp_pwwn_table[index];
9886		pd = head->pwwn_head;
9887		while (pd != NULL) {
9888			mutex_enter(&pd->pd_mutex);
9889			if (pd->pd_state == PORT_DEVICE_INVALID) {
9890				mutex_exit(&pd->pd_mutex);
9891				pd = pd->pd_wwn_hnext;
9892				continue;
9893			}
9894
9895			devlist[count].dev_state = pd->pd_state;
9896			devlist[count].dev_hard_addr = pd->pd_hard_addr;
9897			devlist[count].dev_did = pd->pd_port_id;
9898			devlist[count].dev_did.priv_lilp_posit =
9899			    (uint8_t)(index & 0xff);
9900			bcopy((caddr_t)pd->pd_fc4types,
9901			    (caddr_t)devlist[count].dev_type,
9902			    sizeof (pd->pd_fc4types));
9903
9904			bcopy((caddr_t)&pd->pd_port_name,
9905			    (caddr_t)&devlist[count].dev_pwwn,
9906			    sizeof (la_wwn_t));
9907
9908			node = pd->pd_remote_nodep;
9909			mutex_exit(&pd->pd_mutex);
9910
9911			if (node) {
9912				mutex_enter(&node->fd_mutex);
9913				bcopy((caddr_t)&node->fd_node_name,
9914				    (caddr_t)&devlist[count].dev_nwwn,
9915				    sizeof (la_wwn_t));
9916				mutex_exit(&node->fd_mutex);
9917			}
9918			count++;
9919			if (count >= num_devices) {
9920				goto found;
9921			}
9922		}
9923	}
9924found:
9925	if (fp_copyout((void *)&count, (void *)fcio->fcio_abuf,
9926	    sizeof (count), mode)) {
9927		rval = FC_FAILURE;
9928	} else if (fp_copyout((void *)devlist, (void *)fcio->fcio_obuf,
9929	    sizeof (fc_port_dev_t) * num_devices, mode)) {
9930		rval = FC_FAILURE;
9931	} else {
9932		rval = FC_SUCCESS;
9933	}
9934
9935	kmem_free(devlist, sizeof (fc_port_dev_t) * num_devices);
9936
9937	return (rval);
9938}
9939
9940
9941/*
9942 * Handle Fabric ONLINE
9943 */
9944static void
9945fp_fabric_online(fc_local_port_t *port, job_request_t *job)
9946{
9947	int			index;
9948	int			rval;
9949	int			dbg_count;
9950	int			count = 0;
9951	char 			ww_name[17];
9952	uint32_t		d_id;
9953	uint32_t		listlen;
9954	fctl_ns_req_t		*ns_cmd;
9955	struct pwwn_hash	*head;
9956	fc_remote_port_t	*pd;
9957	fc_remote_port_t	*npd;
9958	fc_portmap_t		*changelist;
9959
9960	ASSERT(MUTEX_HELD(&port->fp_mutex));
9961	ASSERT(FC_IS_TOP_SWITCH(port->fp_topology));
9962	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
9963
9964	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
9965	    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
9966	    0, KM_SLEEP);
9967
9968	ASSERT(ns_cmd != NULL);
9969
9970	ns_cmd->ns_cmd_code = NS_GID_PN;
9971
9972	/*
9973	 * Check if orphans are showing up now
9974	 */
9975	if (port->fp_orphan_count) {
9976		fc_orphan_t 	*orp;
9977		fc_orphan_t	*norp = NULL;
9978		fc_orphan_t	*prev = NULL;
9979
9980		for (orp = port->fp_orphan_list; orp; orp = norp) {
9981			norp = orp->orp_next;
9982			mutex_exit(&port->fp_mutex);
9983			orp->orp_nscan++;
9984
9985			job->job_counter = 1;
9986			job->job_result = FC_SUCCESS;
9987
9988			((ns_req_gid_pn_t *)
9989			    (ns_cmd->ns_cmd_buf))->pwwn = orp->orp_pwwn;
9990			((ns_resp_gid_pn_t *)
9991			    ns_cmd->ns_data_buf)->pid.port_id = 0;
9992			((ns_resp_gid_pn_t *)
9993			    ns_cmd->ns_data_buf)->pid.priv_lilp_posit = 0;
9994
9995			rval = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
9996			if (rval == FC_SUCCESS) {
9997				d_id =
9998				    BE_32(*((uint32_t *)ns_cmd->ns_data_buf));
9999				pd = fp_create_remote_port_by_ns(port,
10000				    d_id, KM_SLEEP);
10001
10002				if (pd != NULL) {
10003					fc_wwn_to_str(&orp->orp_pwwn, ww_name);
10004
10005					fp_printf(port, CE_WARN, FP_LOG_ONLY,
10006					    0, NULL, "N_x Port with D_ID=%x,"
10007					    " PWWN=%s reappeared in fabric",
10008					    d_id, ww_name);
10009
10010					mutex_enter(&port->fp_mutex);
10011					if (prev) {
10012						prev->orp_next = orp->orp_next;
10013					} else {
10014						ASSERT(orp ==
10015						    port->fp_orphan_list);
10016						port->fp_orphan_list =
10017						    orp->orp_next;
10018					}
10019					port->fp_orphan_count--;
10020					mutex_exit(&port->fp_mutex);
10021					kmem_free(orp, sizeof (*orp));
10022					count++;
10023
10024					mutex_enter(&pd->pd_mutex);
10025					pd->pd_flags = PD_ELS_MARK;
10026
10027					mutex_exit(&pd->pd_mutex);
10028				} else {
10029					prev = orp;
10030				}
10031			} else {
10032				if (orp->orp_nscan == FC_ORPHAN_SCAN_LIMIT) {
10033					fc_wwn_to_str(&orp->orp_pwwn, ww_name);
10034
10035					fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0,
10036					    NULL,
10037					    " Port WWN %s removed from orphan"
10038					    " list after %d scans", ww_name,
10039					    orp->orp_nscan);
10040
10041					mutex_enter(&port->fp_mutex);
10042					if (prev) {
10043						prev->orp_next = orp->orp_next;
10044					} else {
10045						ASSERT(orp ==
10046						    port->fp_orphan_list);
10047						port->fp_orphan_list =
10048						    orp->orp_next;
10049					}
10050					port->fp_orphan_count--;
10051					mutex_exit(&port->fp_mutex);
10052
10053					kmem_free(orp, sizeof (*orp));
10054				} else {
10055					prev = orp;
10056				}
10057			}
10058			mutex_enter(&port->fp_mutex);
10059		}
10060	}
10061
10062	/*
10063	 * Walk the Port WWN hash table, reestablish LOGIN
10064	 * if a LOGIN is already performed on a particular
10065	 * device; Any failure to LOGIN should mark the
10066	 * port device OLD.
10067	 */
10068	for (index = 0; index < pwwn_table_size; index++) {
10069		head = &port->fp_pwwn_table[index];
10070		npd = head->pwwn_head;
10071
10072		while ((pd = npd) != NULL) {
10073			la_wwn_t 	*pwwn;
10074
10075			npd = pd->pd_wwn_hnext;
10076
10077			/*
10078			 * Don't count in the port devices that are new
10079			 * unless the total number of devices visible
10080			 * through this port is less than FP_MAX_DEVICES
10081			 */
10082			mutex_enter(&pd->pd_mutex);
10083			if (port->fp_dev_count >= FP_MAX_DEVICES ||
10084			    (port->fp_options & FP_TARGET_MODE)) {
10085				if (pd->pd_type == PORT_DEVICE_NEW ||
10086				    pd->pd_flags == PD_ELS_MARK ||
10087				    pd->pd_recepient != PD_PLOGI_INITIATOR) {
10088					mutex_exit(&pd->pd_mutex);
10089					continue;
10090				}
10091			} else {
10092				if (pd->pd_flags == PD_ELS_MARK ||
10093				    pd->pd_recepient != PD_PLOGI_INITIATOR) {
10094					mutex_exit(&pd->pd_mutex);
10095					continue;
10096				}
10097				pd->pd_type = PORT_DEVICE_OLD;
10098			}
10099			count++;
10100
10101			/*
10102			 * Consult with the name server about D_ID changes
10103			 */
10104			job->job_counter = 1;
10105			job->job_result = FC_SUCCESS;
10106
10107			((ns_req_gid_pn_t *)
10108			    (ns_cmd->ns_cmd_buf))->pwwn = pd->pd_port_name;
10109			((ns_resp_gid_pn_t *)
10110			    ns_cmd->ns_data_buf)->pid.port_id = 0;
10111
10112			((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->
10113			    pid.priv_lilp_posit = 0;
10114
10115			pwwn = &pd->pd_port_name;
10116			pd->pd_flags = PD_ELS_MARK;
10117
10118			mutex_exit(&pd->pd_mutex);
10119			mutex_exit(&port->fp_mutex);
10120
10121			rval = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
10122			if (rval != FC_SUCCESS) {
10123				fc_wwn_to_str(pwwn, ww_name);
10124
10125				mutex_enter(&pd->pd_mutex);
10126				d_id = pd->pd_port_id.port_id;
10127				pd->pd_type = PORT_DEVICE_DELETE;
10128				mutex_exit(&pd->pd_mutex);
10129
10130				FP_TRACE(FP_NHEAD1(3, 0),
10131				    "fp_fabric_online: PD "
10132				    "disappeared; d_id=%x, PWWN=%s",
10133				    d_id, ww_name);
10134
10135				FP_TRACE(FP_NHEAD2(9, 0),
10136				    "N_x Port with D_ID=%x, PWWN=%s"
10137				    " disappeared from fabric", d_id,
10138				    ww_name);
10139
10140				mutex_enter(&port->fp_mutex);
10141				continue;
10142			}
10143
10144			d_id = BE_32(*((uint32_t *)ns_cmd->ns_data_buf));
10145
10146			mutex_enter(&port->fp_mutex);
10147			mutex_enter(&pd->pd_mutex);
10148			if (d_id != pd->pd_port_id.port_id) {
10149				fctl_delist_did_table(port, pd);
10150				fc_wwn_to_str(pwwn, ww_name);
10151
10152				FP_TRACE(FP_NHEAD2(9, 0),
10153				    "D_ID of a device with PWWN %s changed."
10154				    " New D_ID = %x, OLD D_ID = %x", ww_name,
10155				    d_id, pd->pd_port_id.port_id);
10156
10157				pd->pd_port_id.port_id = BE_32(d_id);
10158				pd->pd_type = PORT_DEVICE_CHANGED;
10159				fctl_enlist_did_table(port, pd);
10160			}
10161			mutex_exit(&pd->pd_mutex);
10162
10163		}
10164	}
10165
10166	if (ns_cmd) {
10167		fctl_free_ns_cmd(ns_cmd);
10168	}
10169
10170	listlen = 0;
10171	changelist = NULL;
10172	if (count) {
10173		if (port->fp_soft_state & FP_SOFT_IN_FCA_RESET) {
10174			port->fp_soft_state &= ~FP_SOFT_IN_FCA_RESET;
10175			mutex_exit(&port->fp_mutex);
10176			delay(drv_usectohz(FLA_RR_TOV * 1000 * 1000));
10177			mutex_enter(&port->fp_mutex);
10178		}
10179
10180		dbg_count = 0;
10181
10182		job->job_counter = count;
10183
10184		for (index = 0; index < pwwn_table_size; index++) {
10185			head = &port->fp_pwwn_table[index];
10186			npd = head->pwwn_head;
10187
10188			while ((pd = npd) != NULL) {
10189				npd = pd->pd_wwn_hnext;
10190
10191				mutex_enter(&pd->pd_mutex);
10192				if (pd->pd_flags != PD_ELS_MARK) {
10193					mutex_exit(&pd->pd_mutex);
10194					continue;
10195				}
10196
10197				dbg_count++;
10198
10199				/*
10200				 * If it is already marked deletion, nothing
10201				 * else to do.
10202				 */
10203				if (pd->pd_type == PORT_DEVICE_DELETE) {
10204					pd->pd_type = PORT_DEVICE_OLD;
10205
10206					mutex_exit(&pd->pd_mutex);
10207					mutex_exit(&port->fp_mutex);
10208					fp_jobdone(job);
10209					mutex_enter(&port->fp_mutex);
10210
10211					continue;
10212				}
10213
10214				/*
10215				 * If it is freshly discovered out of
10216				 * the orphan list, nothing else to do
10217				 */
10218				if (pd->pd_type == PORT_DEVICE_NEW) {
10219					pd->pd_flags = PD_IDLE;
10220
10221					mutex_exit(&pd->pd_mutex);
10222					mutex_exit(&port->fp_mutex);
10223					fp_jobdone(job);
10224					mutex_enter(&port->fp_mutex);
10225
10226					continue;
10227				}
10228
10229				pd->pd_flags = PD_IDLE;
10230				d_id = pd->pd_port_id.port_id;
10231
10232				/*
10233				 * Explicitly mark all devices OLD; successful
10234				 * PLOGI should reset this to either NO_CHANGE
10235				 * or CHANGED.
10236				 */
10237				if (pd->pd_type != PORT_DEVICE_CHANGED) {
10238					pd->pd_type = PORT_DEVICE_OLD;
10239				}
10240
10241				mutex_exit(&pd->pd_mutex);
10242				mutex_exit(&port->fp_mutex);
10243
10244				rval = fp_port_login(port, d_id, job,
10245				    FP_CMD_PLOGI_RETAIN, KM_SLEEP, pd, NULL);
10246
10247				if (rval != FC_SUCCESS) {
10248					fp_jobdone(job);
10249				}
10250				mutex_enter(&port->fp_mutex);
10251			}
10252		}
10253		mutex_exit(&port->fp_mutex);
10254
10255		ASSERT(dbg_count == count);
10256		fp_jobwait(job);
10257
10258		mutex_enter(&port->fp_mutex);
10259
10260		ASSERT(port->fp_statec_busy > 0);
10261		if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
10262			if (port->fp_statec_busy > 1) {
10263				job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
10264			}
10265		}
10266		mutex_exit(&port->fp_mutex);
10267	} else {
10268		ASSERT(port->fp_statec_busy > 0);
10269		if (port->fp_statec_busy > 1) {
10270			job->job_flags |= JOB_CANCEL_ULP_NOTIFICATION;
10271		}
10272		mutex_exit(&port->fp_mutex);
10273	}
10274
10275	if ((job->job_flags & JOB_CANCEL_ULP_NOTIFICATION) == 0) {
10276		fctl_fillout_map(port, &changelist, &listlen, 1, 0, 0);
10277
10278		(void) fp_ulp_statec_cb(port, FC_STATE_ONLINE, changelist,
10279		    listlen, listlen, KM_SLEEP);
10280
10281		mutex_enter(&port->fp_mutex);
10282	} else {
10283		ASSERT(changelist == NULL && listlen == 0);
10284		mutex_enter(&port->fp_mutex);
10285		if (--port->fp_statec_busy == 0) {
10286			port->fp_soft_state &= ~FP_SOFT_IN_STATEC_CB;
10287		}
10288	}
10289}
10290
10291
10292/*
10293 * Fill out device list for userland ioctl in private loop
10294 */
10295static int
10296fp_fillout_loopmap(fc_local_port_t *port, fcio_t *fcio, int mode)
10297{
10298	int			rval;
10299	int			count;
10300	int			index;
10301	int			num_devices;
10302	fc_remote_node_t	*node;
10303	fc_port_dev_t 		*devlist;
10304	int			lilp_device_count;
10305	fc_lilpmap_t		*lilp_map;
10306	uchar_t			*alpa_list;
10307
10308	ASSERT(MUTEX_HELD(&port->fp_mutex));
10309
10310	num_devices = fcio->fcio_olen / sizeof (fc_port_dev_t);
10311	if (port->fp_total_devices > port->fp_dev_count &&
10312	    num_devices >= port->fp_total_devices) {
10313		job_request_t 	*job;
10314
10315		mutex_exit(&port->fp_mutex);
10316		job = fctl_alloc_job(JOB_PORT_GETMAP, 0, NULL, NULL, KM_SLEEP);
10317		job->job_counter = 1;
10318
10319		mutex_enter(&port->fp_mutex);
10320		fp_get_loopmap(port, job);
10321		mutex_exit(&port->fp_mutex);
10322
10323		fp_jobwait(job);
10324		fctl_dealloc_job(job);
10325	} else {
10326		mutex_exit(&port->fp_mutex);
10327	}
10328	devlist = kmem_zalloc(sizeof (*devlist) * num_devices, KM_SLEEP);
10329
10330	mutex_enter(&port->fp_mutex);
10331
10332	/*
10333	 * Applications are accustomed to getting the device list in
10334	 * LILP map order. The HBA firmware usually returns the device
10335	 * map in the LILP map order and diagnostic applications would
10336	 * prefer to receive in the device list in that order too
10337	 */
10338	lilp_map = &port->fp_lilp_map;
10339	alpa_list = &lilp_map->lilp_alpalist[0];
10340
10341	/*
10342	 * the length field corresponds to the offset in the LILP frame
10343	 * which begins with 1. The thing to note here is that the
10344	 * lilp_device_count is 1 more than fp->fp_total_devices since
10345	 * the host adapter's alpa also shows up in the lilp map. We
10346	 * don't however return details of the host adapter since
10347	 * fctl_get_remote_port_by_did fails for the host adapter's ALPA
10348	 * and applications are required to issue the FCIO_GET_HOST_PARAMS
10349	 * ioctl to obtain details about the host adapter port.
10350	 */
10351	lilp_device_count = lilp_map->lilp_length;
10352
10353	for (count = index = 0; index < lilp_device_count &&
10354	    count < num_devices; index++) {
10355		uint32_t d_id;
10356		fc_remote_port_t *pd;
10357
10358		d_id = alpa_list[index];
10359
10360		mutex_exit(&port->fp_mutex);
10361		pd = fctl_get_remote_port_by_did(port, d_id);
10362		mutex_enter(&port->fp_mutex);
10363
10364		if (pd != NULL) {
10365			mutex_enter(&pd->pd_mutex);
10366
10367			if (pd->pd_state == PORT_DEVICE_INVALID) {
10368				mutex_exit(&pd->pd_mutex);
10369				continue;
10370			}
10371
10372			devlist[count].dev_state = pd->pd_state;
10373			devlist[count].dev_hard_addr = pd->pd_hard_addr;
10374			devlist[count].dev_did = pd->pd_port_id;
10375			devlist[count].dev_did.priv_lilp_posit =
10376						(uint8_t)(index & 0xff);
10377			bcopy((caddr_t)pd->pd_fc4types,
10378			    (caddr_t)devlist[count].dev_type,
10379			    sizeof (pd->pd_fc4types));
10380
10381			bcopy((caddr_t)&pd->pd_port_name,
10382			    (caddr_t)&devlist[count].dev_pwwn,
10383			    sizeof (la_wwn_t));
10384
10385			node = pd->pd_remote_nodep;
10386			mutex_exit(&pd->pd_mutex);
10387
10388			if (node) {
10389				mutex_enter(&node->fd_mutex);
10390				bcopy((caddr_t)&node->fd_node_name,
10391				    (caddr_t)&devlist[count].dev_nwwn,
10392				    sizeof (la_wwn_t));
10393				mutex_exit(&node->fd_mutex);
10394			}
10395			count++;
10396		}
10397	}
10398
10399	if (fp_copyout((void *)&count, (void *)fcio->fcio_abuf,
10400	    sizeof (count), mode)) {
10401		rval = FC_FAILURE;
10402	}
10403
10404	if (fp_copyout((void *)devlist, (void *)fcio->fcio_obuf,
10405	    sizeof (fc_port_dev_t) * num_devices, mode)) {
10406		rval = FC_FAILURE;
10407	} else {
10408		rval = FC_SUCCESS;
10409	}
10410
10411	kmem_free(devlist, sizeof (*devlist) * num_devices);
10412	ASSERT(MUTEX_HELD(&port->fp_mutex));
10413
10414	return (rval);
10415}
10416
10417
10418/*
10419 * Completion function for responses to unsolicited commands
10420 */
10421static void
10422fp_unsol_intr(fc_packet_t *pkt)
10423{
10424	fp_cmd_t 	*cmd;
10425	fc_local_port_t *port;
10426
10427	cmd = pkt->pkt_ulp_private;
10428	port = cmd->cmd_port;
10429
10430	if (pkt->pkt_state != FC_PKT_SUCCESS) {
10431		fp_printf(port, CE_WARN, FP_LOG_ONLY, 0, pkt,
10432		    "couldn't post response to unsolicited request;"
10433		    " ox_id=%x rx_id=%x", pkt->pkt_cmd_fhdr.ox_id,
10434		    pkt->pkt_resp_fhdr.rx_id);
10435	}
10436
10437	if (cmd == port->fp_els_resp_pkt) {
10438		mutex_enter(&port->fp_mutex);
10439		port->fp_els_resp_pkt_busy = 0;
10440		mutex_exit(&port->fp_mutex);
10441		return;
10442	}
10443
10444	fp_free_pkt(cmd);
10445}
10446
10447
10448/*
10449 * solicited LINIT ELS completion function
10450 */
10451static void
10452fp_linit_intr(fc_packet_t *pkt)
10453{
10454	fp_cmd_t		*cmd;
10455	job_request_t		*job;
10456	fc_linit_resp_t		acc;
10457
10458	if (FP_IS_PKT_ERROR(pkt)) {
10459		(void) fp_common_intr(pkt, 1);
10460		return;
10461	}
10462
10463	cmd = pkt->pkt_ulp_private;
10464	job = cmd->cmd_job;
10465
10466	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&acc,
10467	    (uint8_t *)pkt->pkt_resp, sizeof (acc), DDI_DEV_AUTOINCR);
10468
10469	if (acc.status != FC_LINIT_SUCCESS) {
10470		job->job_result = FC_FAILURE;
10471	} else {
10472		job->job_result = FC_SUCCESS;
10473	}
10474	fp_iodone(cmd);
10475}
10476
10477
10478/*
10479 * Decode the unsolicited request; For FC-4 Device and Link data frames
10480 * notify the registered ULP of this FC-4 type right here. For Unsolicited
10481 * ELS requests, submit a request to the job_handler thread to work on it.
10482 * The intent is to act quickly on the FC-4 unsolicited link and data frames
10483 * and save much of the interrupt time processing of unsolicited ELS requests
10484 * and hand it off to the job_handler thread.
10485 */
10486static void
10487fp_unsol_cb(opaque_t port_handle, fc_unsol_buf_t *buf, uint32_t type)
10488{
10489	uchar_t		r_ctl;
10490	uchar_t		ls_code;
10491	uint32_t	s_id;
10492	uint32_t	rscn_count = FC_INVALID_RSCN_COUNT;
10493	uint32_t	cb_arg;
10494	fp_cmd_t	*cmd;
10495	fc_local_port_t *port;
10496	job_request_t	*job;
10497	fc_remote_port_t 	*pd;
10498
10499	port = port_handle;
10500
10501	FP_TRACE(FP_NHEAD1(1, 0), "fp_unsol_cb: s_id=%x,"
10502	    " d_id=%x, type=%x, r_ctl=%x, f_ctl=%x"
10503	    " seq_id=%x, df_ctl=%x, seq_cnt=%x, ox_id=%x, rx_id=%x"
10504	    " ro=%x, buffer[0]:%x", buf->ub_frame.s_id, buf->ub_frame.d_id,
10505	    buf->ub_frame.type, buf->ub_frame.r_ctl, buf->ub_frame.f_ctl,
10506	    buf->ub_frame.seq_id, buf->ub_frame.df_ctl, buf->ub_frame.seq_cnt,
10507	    buf->ub_frame.ox_id, buf->ub_frame.rx_id, buf->ub_frame.ro,
10508	    buf->ub_buffer[0]);
10509
10510	if (type & 0x80000000) {
10511		/*
10512		 * Huh ? Nothing much can be done without
10513		 * a valid buffer. So just exit.
10514		 */
10515		return;
10516	}
10517	/*
10518	 * If the unsolicited interrupts arrive while it isn't
10519	 * safe to handle unsolicited callbacks; Drop them, yes,
10520	 * drop them on the floor
10521	 */
10522	mutex_enter(&port->fp_mutex);
10523	port->fp_active_ubs++;
10524	if ((port->fp_soft_state &
10525	    (FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN)) ||
10526	    FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
10527
10528		FP_TRACE(FP_NHEAD1(3, 0), "fp_unsol_cb: port state is "
10529		    "not ONLINE. s_id=%x, d_id=%x, type=%x, "
10530		    "seq_id=%x, ox_id=%x, rx_id=%x"
10531		    "ro=%x", buf->ub_frame.s_id, buf->ub_frame.d_id,
10532		    buf->ub_frame.type, buf->ub_frame.seq_id,
10533		    buf->ub_frame.ox_id, buf->ub_frame.rx_id, buf->ub_frame.ro);
10534
10535		ASSERT(port->fp_active_ubs > 0);
10536		if (--(port->fp_active_ubs) == 0) {
10537			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
10538		}
10539
10540		mutex_exit(&port->fp_mutex);
10541
10542		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
10543		    1, &buf->ub_token);
10544
10545		return;
10546	}
10547
10548	r_ctl = buf->ub_frame.r_ctl;
10549	s_id = buf->ub_frame.s_id;
10550	if (port->fp_active_ubs == 1) {
10551		port->fp_soft_state |= FP_SOFT_IN_UNSOL_CB;
10552	}
10553
10554	if (r_ctl == R_CTL_ELS_REQ && buf->ub_buffer[0] == LA_ELS_LOGO &&
10555	    port->fp_statec_busy) {
10556		mutex_exit(&port->fp_mutex);
10557		pd = fctl_get_remote_port_by_did(port, s_id);
10558		if (pd) {
10559			mutex_enter(&pd->pd_mutex);
10560			if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
10561				FP_TRACE(FP_NHEAD1(3, 0),
10562				    "LOGO for LOGGED IN D_ID %x",
10563				    buf->ub_frame.s_id);
10564				pd->pd_state = PORT_DEVICE_VALID;
10565			}
10566			mutex_exit(&pd->pd_mutex);
10567		}
10568
10569		mutex_enter(&port->fp_mutex);
10570		ASSERT(port->fp_active_ubs > 0);
10571		if (--(port->fp_active_ubs) == 0) {
10572			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
10573		}
10574		mutex_exit(&port->fp_mutex);
10575
10576		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
10577		    1, &buf->ub_token);
10578
10579		FP_TRACE(FP_NHEAD1(3, 0),
10580		    "fp_unsol_cb() bailing out LOGO for D_ID %x",
10581		    buf->ub_frame.s_id);
10582		return;
10583	}
10584
10585	if (port->fp_els_resp_pkt_busy == 0) {
10586		if (r_ctl == R_CTL_ELS_REQ) {
10587			ls_code = buf->ub_buffer[0];
10588
10589			switch (ls_code) {
10590			case LA_ELS_PLOGI:
10591			case LA_ELS_FLOGI:
10592				port->fp_els_resp_pkt_busy = 1;
10593				mutex_exit(&port->fp_mutex);
10594				fp_i_handle_unsol_els(port, buf);
10595
10596				mutex_enter(&port->fp_mutex);
10597				ASSERT(port->fp_active_ubs > 0);
10598				if (--(port->fp_active_ubs) == 0) {
10599					port->fp_soft_state &=
10600					    ~FP_SOFT_IN_UNSOL_CB;
10601				}
10602				mutex_exit(&port->fp_mutex);
10603				port->fp_fca_tran->fca_ub_release(
10604				    port->fp_fca_handle, 1, &buf->ub_token);
10605
10606				return;
10607			case LA_ELS_RSCN:
10608				if (++(port)->fp_rscn_count ==
10609				    FC_INVALID_RSCN_COUNT) {
10610					++(port)->fp_rscn_count;
10611				}
10612				rscn_count = port->fp_rscn_count;
10613				break;
10614
10615			default:
10616				break;
10617			}
10618		}
10619	} else if ((r_ctl == R_CTL_ELS_REQ) &&
10620	    (buf->ub_buffer[0] == LA_ELS_RSCN)) {
10621		if (++port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
10622			++port->fp_rscn_count;
10623		}
10624		rscn_count = port->fp_rscn_count;
10625	}
10626
10627	mutex_exit(&port->fp_mutex);
10628
10629	switch (r_ctl & R_CTL_ROUTING) {
10630	case R_CTL_DEVICE_DATA:
10631		/*
10632		 * If the unsolicited buffer is a CT IU,
10633		 * have the job_handler thread work on it.
10634		 */
10635		if (buf->ub_frame.type == FC_TYPE_FC_SERVICES) {
10636			break;
10637		}
10638		/* FALLTHROUGH */
10639
10640	case R_CTL_FC4_SVC: {
10641		int sendup = 0;
10642
10643		/*
10644		 * If a LOGIN isn't performed before this request
10645		 * shut the door on this port with a reply that a
10646		 * LOGIN is required. We make an exception however
10647		 * for IP broadcast packets and pass them through
10648		 * to the IP ULP(s) to handle broadcast requests.
10649		 * This is not a problem for private loop devices
10650		 * but for fabric topologies we don't log into the
10651		 * remote ports during port initialization and
10652		 * the ULPs need to log into requesting ports on
10653		 * demand.
10654		 */
10655		pd = fctl_get_remote_port_by_did(port, s_id);
10656		if (pd) {
10657			mutex_enter(&pd->pd_mutex);
10658			if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
10659				sendup++;
10660			}
10661			mutex_exit(&pd->pd_mutex);
10662		} else if ((pd == NULL) &&
10663		    (buf->ub_frame.type == FC_TYPE_IS8802_SNAP) &&
10664		    (buf->ub_frame.d_id == 0xffffff ||
10665		    buf->ub_frame.d_id == 0x00)) {
10666			/* brodacst IP frame - so sendup via job thread */
10667			break;
10668		}
10669
10670		/*
10671		 * Send all FC4 services via job thread too
10672		 */
10673		if ((r_ctl & R_CTL_ROUTING) == R_CTL_FC4_SVC) {
10674			break;
10675		}
10676
10677		if (sendup || !FC_IS_REAL_DEVICE(s_id)) {
10678			fctl_ulp_unsol_cb(port, buf, buf->ub_frame.type);
10679			return;
10680		}
10681
10682		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
10683			cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
10684			    0, KM_NOSLEEP, pd);
10685			if (cmd != NULL) {
10686				fp_els_rjt_init(port, cmd, buf,
10687				    FC_ACTION_NON_RETRYABLE,
10688				    FC_REASON_LOGIN_REQUIRED, NULL);
10689
10690				if (fp_sendcmd(port, cmd,
10691				    port->fp_fca_handle) != FC_SUCCESS) {
10692					fp_free_pkt(cmd);
10693				}
10694			}
10695		}
10696
10697		mutex_enter(&port->fp_mutex);
10698		ASSERT(port->fp_active_ubs > 0);
10699		if (--(port->fp_active_ubs) == 0) {
10700			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
10701		}
10702		mutex_exit(&port->fp_mutex);
10703		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
10704		    1, &buf->ub_token);
10705
10706		return;
10707	}
10708
10709	default:
10710		break;
10711	}
10712
10713	/*
10714	 * Submit a Request to the job_handler thread to work
10715	 * on the unsolicited request. The potential side effect
10716	 * of this is that the unsolicited buffer takes a little
10717	 * longer to get released but we save interrupt time in
10718	 * the bargain.
10719	 */
10720	cb_arg = (rscn_count == FC_INVALID_RSCN_COUNT) ? NULL : rscn_count;
10721
10722	/*
10723	 * One way that the rscn_count will get used is described below :
10724	 *
10725	 * 1. fp_unsol_cb() gets an RSCN and updates fp_rscn_count.
10726	 * 2. Before mutex is released, a copy of it is stored in rscn_count.
10727	 * 3. The count is passed to job thread as JOB_UNSOL_REQUEST (below)
10728	 *    by overloading the job_cb_arg to pass the rscn_count
10729	 * 4. When one of the routines processing the RSCN picks it up (ex:
10730	 *    fp_validate_rscn_page()), it passes this count in the map
10731	 *    structure (as part of the map_rscn_info structure member) to the
10732	 *    ULPs.
10733	 * 5. When ULPs make calls back to the transport (example interfaces for
10734	 *    this are fc_ulp_transport(), fc_ulp_login(), fc_issue_els()), they
10735	 *    can now pass back this count as part of the fc_packet's
10736	 *    pkt_ulp_rscn_count member. fcp does this currently.
10737	 * 6. When transport gets a call to transport a command on the wire, it
10738	 *    will check to see if there is a valid pkt_ulp_rsvd1 field in the
10739	 *    fc_packet. If there is, it will match that info with the current
10740	 *    rscn_count on that instance of the port. If they don't match up
10741	 *    then there was a newer RSCN. The ULP gets back an error code which
10742	 *    informs it about it - FC_DEVICE_BUSY_NEW_RSCN.
10743	 * 7. At this point the ULP is free to make up its own mind as to how to
10744	 *    handle this. Currently, fcp will reset its retry counters and keep
10745	 *    retrying the operation it was doing in anticipation of getting a
10746	 *    new state change call back for the new RSCN.
10747	 */
10748	job = fctl_alloc_job(JOB_UNSOL_REQUEST, 0, NULL,
10749	    (opaque_t)(uintptr_t)cb_arg, KM_NOSLEEP);
10750	if (job == NULL) {
10751		fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL, "fp_unsol_cb() "
10752		    "couldn't submit a job to the thread, failing..");
10753
10754		mutex_enter(&port->fp_mutex);
10755
10756		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
10757			--port->fp_rscn_count;
10758		}
10759
10760		ASSERT(port->fp_active_ubs > 0);
10761		if (--(port->fp_active_ubs) == 0) {
10762			port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
10763		}
10764
10765		mutex_exit(&port->fp_mutex);
10766		port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
10767		    1, &buf->ub_token);
10768
10769		return;
10770	}
10771	job->job_private = (void *)buf;
10772	fctl_enque_job(port, job);
10773}
10774
10775
10776/*
10777 * Handle unsolicited requests
10778 */
10779static void
10780fp_handle_unsol_buf(fc_local_port_t *port, fc_unsol_buf_t *buf,
10781    job_request_t *job)
10782{
10783	uchar_t			r_ctl;
10784	uchar_t			ls_code;
10785	uint32_t		s_id;
10786	fp_cmd_t		*cmd;
10787	fc_remote_port_t 	*pd;
10788	fp_unsol_spec_t		*ub_spec;
10789
10790	r_ctl = buf->ub_frame.r_ctl;
10791	s_id = buf->ub_frame.s_id;
10792
10793	switch (r_ctl & R_CTL_ROUTING) {
10794	case R_CTL_EXTENDED_SVC:
10795		if (r_ctl != R_CTL_ELS_REQ) {
10796			break;
10797		}
10798
10799		ls_code = buf->ub_buffer[0];
10800		switch (ls_code) {
10801		case LA_ELS_LOGO:
10802		case LA_ELS_ADISC:
10803		case LA_ELS_PRLO:
10804			pd = fctl_get_remote_port_by_did(port, s_id);
10805			if (pd == NULL) {
10806				if (!FC_IS_REAL_DEVICE(s_id)) {
10807					break;
10808				}
10809				if (!FP_IS_CLASS_1_OR_2(buf->ub_class)) {
10810					break;
10811				}
10812				if ((cmd = fp_alloc_pkt(port,
10813				    sizeof (la_els_rjt_t), 0, KM_SLEEP,
10814				    NULL)) == NULL) {
10815					/*
10816					 * Can this actually fail when
10817					 * given KM_SLEEP?  (Could be used
10818					 * this way in a number of places.)
10819					 */
10820					break;
10821				}
10822
10823				fp_els_rjt_init(port, cmd, buf,
10824				    FC_ACTION_NON_RETRYABLE,
10825				    FC_REASON_INVALID_LINK_CTRL, job);
10826
10827				if (fp_sendcmd(port, cmd,
10828				    port->fp_fca_handle) != FC_SUCCESS) {
10829					fp_free_pkt(cmd);
10830				}
10831
10832				break;
10833			}
10834			if (ls_code == LA_ELS_LOGO) {
10835				fp_handle_unsol_logo(port, buf, pd, job);
10836			} else if (ls_code == LA_ELS_ADISC) {
10837				fp_handle_unsol_adisc(port, buf, pd, job);
10838			} else {
10839				fp_handle_unsol_prlo(port, buf, pd, job);
10840			}
10841			break;
10842
10843		case LA_ELS_PLOGI:
10844			fp_handle_unsol_plogi(port, buf, job, KM_SLEEP);
10845			break;
10846
10847		case LA_ELS_FLOGI:
10848			fp_handle_unsol_flogi(port, buf, job, KM_SLEEP);
10849			break;
10850
10851		case LA_ELS_RSCN:
10852			fp_handle_unsol_rscn(port, buf, job, KM_SLEEP);
10853			break;
10854
10855		default:
10856			ub_spec = kmem_zalloc(sizeof (*ub_spec), KM_SLEEP);
10857			ub_spec->port = port;
10858			ub_spec->buf = buf;
10859
10860			(void) taskq_dispatch(port->fp_taskq,
10861			    fp_ulp_unsol_cb, ub_spec, KM_SLEEP);
10862			return;
10863		}
10864		break;
10865
10866	case R_CTL_BASIC_SVC:
10867		/*
10868		 * The unsolicited basic link services could be ABTS
10869		 * and RMC (Or even a NOP). Just BA_RJT them until
10870		 * such time there arises a need to handle them more
10871		 * carefully.
10872		 */
10873		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
10874			cmd = fp_alloc_pkt(port, sizeof (la_ba_rjt_t),
10875			    0, KM_SLEEP, NULL);
10876			if (cmd != NULL) {
10877				fp_ba_rjt_init(port, cmd, buf, job);
10878				if (fp_sendcmd(port, cmd,
10879				    port->fp_fca_handle) != FC_SUCCESS) {
10880					fp_free_pkt(cmd);
10881				}
10882			}
10883		}
10884		break;
10885
10886	case R_CTL_DEVICE_DATA:
10887		if (buf->ub_frame.type == FC_TYPE_FC_SERVICES) {
10888			/*
10889			 * Mostly this is of type FC_TYPE_FC_SERVICES.
10890			 * As we don't like any Unsolicited FC services
10891			 * requests, we would do well to RJT them as
10892			 * well.
10893			 */
10894			if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
10895				cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
10896				    0, KM_SLEEP, NULL);
10897				if (cmd != NULL) {
10898					fp_els_rjt_init(port, cmd, buf,
10899					    FC_ACTION_NON_RETRYABLE,
10900					    FC_REASON_INVALID_LINK_CTRL, job);
10901
10902					if (fp_sendcmd(port, cmd,
10903					    port->fp_fca_handle) !=
10904					    FC_SUCCESS) {
10905						fp_free_pkt(cmd);
10906					}
10907				}
10908			}
10909			break;
10910		}
10911		/* FALLTHROUGH */
10912
10913	case R_CTL_FC4_SVC:
10914		ub_spec = kmem_zalloc(sizeof (*ub_spec), KM_SLEEP);
10915		ub_spec->port = port;
10916		ub_spec->buf = buf;
10917
10918		(void) taskq_dispatch(port->fp_taskq,
10919		    fp_ulp_unsol_cb, ub_spec, KM_SLEEP);
10920		return;
10921
10922	case R_CTL_LINK_CTL:
10923		/*
10924		 * Turn deaf ear on unsolicited link control frames.
10925		 * Typical unsolicited link control Frame is an LCR
10926		 * (to reset End to End credit to the default login
10927		 * value and abort current sequences for all classes)
10928		 * An intelligent microcode/firmware should handle
10929		 * this transparently at its level and not pass all
10930		 * the way up here.
10931		 *
10932		 * Possible responses to LCR are R_RDY, F_RJT, P_RJT
10933		 * or F_BSY. P_RJT is chosen to be the most appropriate
10934		 * at this time.
10935		 */
10936		/* FALLTHROUGH */
10937
10938	default:
10939		/*
10940		 * Just reject everything else as an invalid request.
10941		 */
10942		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
10943			cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
10944			    0, KM_SLEEP, NULL);
10945			if (cmd != NULL) {
10946				fp_els_rjt_init(port, cmd, buf,
10947				    FC_ACTION_NON_RETRYABLE,
10948				    FC_REASON_INVALID_LINK_CTRL, job);
10949
10950				if (fp_sendcmd(port, cmd,
10951				    port->fp_fca_handle) != FC_SUCCESS) {
10952					fp_free_pkt(cmd);
10953				}
10954			}
10955		}
10956		break;
10957	}
10958
10959	mutex_enter(&port->fp_mutex);
10960	ASSERT(port->fp_active_ubs > 0);
10961	if (--(port->fp_active_ubs) == 0) {
10962		port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
10963	}
10964	mutex_exit(&port->fp_mutex);
10965	port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
10966	    1, &buf->ub_token);
10967}
10968
10969
10970/*
10971 * Prepare a BA_RJT and send it over.
10972 */
10973static void
10974fp_ba_rjt_init(fc_local_port_t *port, fp_cmd_t *cmd, fc_unsol_buf_t *buf,
10975    job_request_t *job)
10976{
10977	fc_packet_t	*pkt;
10978	la_ba_rjt_t 	payload;
10979
10980	ASSERT(!MUTEX_HELD(&port->fp_mutex));
10981
10982	cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
10983	cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
10984	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
10985	cmd->cmd_retry_count = 1;
10986	cmd->cmd_ulp_pkt = NULL;
10987
10988	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
10989	cmd->cmd_job = job;
10990
10991	pkt = &cmd->cmd_pkt;
10992
10993	fp_unsol_resp_init(pkt, buf, R_CTL_LS_BA_RJT, FC_TYPE_BASIC_LS);
10994
10995	payload.reserved = 0;
10996	payload.reason_code = FC_REASON_CMD_UNSUPPORTED;
10997	payload.explanation = FC_EXPLN_NONE;
10998	payload.vendor = 0;
10999
11000	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
11001	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
11002}
11003
11004
11005/*
11006 * Prepare an LS_RJT and send it over
11007 */
11008static void
11009fp_els_rjt_init(fc_local_port_t *port, fp_cmd_t *cmd, fc_unsol_buf_t *buf,
11010    uchar_t action, uchar_t reason, job_request_t *job)
11011{
11012	fc_packet_t	*pkt;
11013	la_els_rjt_t 	payload;
11014
11015	ASSERT(!MUTEX_HELD(&port->fp_mutex));
11016
11017	cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
11018	cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
11019	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
11020	cmd->cmd_retry_count = 1;
11021	cmd->cmd_ulp_pkt = NULL;
11022
11023	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
11024	cmd->cmd_job = job;
11025
11026	pkt = &cmd->cmd_pkt;
11027
11028	fp_unsol_resp_init(pkt, buf, R_CTL_ELS_RSP, FC_TYPE_EXTENDED_LS);
11029
11030	payload.ls_code.ls_code = LA_ELS_RJT;
11031	payload.ls_code.mbz = 0;
11032	payload.action = action;
11033	payload.reason = reason;
11034	payload.reserved = 0;
11035	payload.vu = 0;
11036
11037	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
11038	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
11039}
11040
11041/*
11042 *     Function: fp_prlo_acc_init
11043 *
11044 *  Description: Initializes an Link Service Accept for a PRLO.
11045 *
11046 *    Arguments: *port		Local port through which the PRLO was
11047 *				received.
11048 *		 cmd		Command that will carry the accept.
11049 *		 *buf		Unsolicited buffer containing the PRLO
11050 *				request.
11051 *		 job		Job request.
11052 *		 sleep		Allocation mode.
11053 *
11054 * Return Value: *cmd		Command containing the response.
11055 *
11056 *      Context: Depends on the parameter sleep.
11057 */
11058fp_cmd_t *
11059fp_prlo_acc_init(fc_local_port_t *port, fc_remote_port_t *pd,
11060    fc_unsol_buf_t *buf, job_request_t *job, int sleep)
11061{
11062	fp_cmd_t	*cmd;
11063	fc_packet_t	*pkt;
11064	la_els_prlo_t	*req;
11065	size_t		len;
11066	uint16_t	flags;
11067
11068	req = (la_els_prlo_t *)buf->ub_buffer;
11069	len = (size_t)ntohs(req->payload_length);
11070
11071	/*
11072	 * The payload of the accept to a PRLO has to be the exact match of
11073	 * the payload of the request (at the exception of the code).
11074	 */
11075	cmd = fp_alloc_pkt(port, (int)len, 0, sleep, pd);
11076
11077	if (cmd) {
11078		/*
11079		 * The fp command was successfully allocated.
11080		 */
11081		cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
11082		cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
11083		cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
11084		cmd->cmd_retry_count = 1;
11085		cmd->cmd_ulp_pkt = NULL;
11086
11087		cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
11088		cmd->cmd_job = job;
11089
11090		pkt = &cmd->cmd_pkt;
11091
11092		fp_unsol_resp_init(pkt, buf, R_CTL_ELS_RSP,
11093		    FC_TYPE_EXTENDED_LS);
11094
11095		/* The code is overwritten for the copy. */
11096		req->ls_code = LA_ELS_ACC;
11097		/* Response code is set. */
11098		flags = ntohs(req->flags);
11099		flags &= ~SP_RESP_CODE_MASK;
11100		flags |= SP_RESP_CODE_REQ_EXECUTED;
11101		req->flags = htons(flags);
11102
11103		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)req,
11104		    (uint8_t *)pkt->pkt_cmd, len, DDI_DEV_AUTOINCR);
11105	}
11106	return (cmd);
11107}
11108
11109/*
11110 * Prepare an ACC response to an ELS request
11111 */
11112static void
11113fp_els_acc_init(fc_local_port_t *port, fp_cmd_t *cmd, fc_unsol_buf_t *buf,
11114    job_request_t *job)
11115{
11116	fc_packet_t	*pkt;
11117	ls_code_t	payload;
11118
11119	cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
11120	cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
11121	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
11122	cmd->cmd_retry_count = 1;
11123	cmd->cmd_ulp_pkt = NULL;
11124
11125	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
11126	cmd->cmd_job = job;
11127
11128	pkt = &cmd->cmd_pkt;
11129
11130	fp_unsol_resp_init(pkt, buf, R_CTL_ELS_RSP, FC_TYPE_EXTENDED_LS);
11131
11132	payload.ls_code = LA_ELS_ACC;
11133	payload.mbz = 0;
11134
11135	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
11136	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
11137}
11138
11139/*
11140 * Unsolicited PRLO handler
11141 *
11142 * A Process Logout should be handled by the ULP that established it.  However,
11143 * some devices send a PRLO to trigger a PLOGI followed by a PRLI.  This happens
11144 * when a device implicitly logs out an initiator (for whatever reason) and
11145 * tries to get that initiator to restablish the connection (PLOGI and PRLI).
11146 * The logical thing to do for the device would be to send a LOGO in response
11147 * to any FC4 frame sent by the initiator. Some devices choose, however, to send
11148 * a PRLO instead.
11149 *
11150 * From a Fibre Channel standpoint a PRLO calls for a PRLI. There's no reason to
11151 * think that the Port Login has been lost.  If we follow the Fibre Channel
11152 * protocol to the letter a PRLI should be sent after accepting the PRLO.  If
11153 * the Port Login has also been lost, the remote port will reject the PRLI
11154 * indicating that we must PLOGI first.  The initiator will then turn around and
11155 * send a PLOGI.  The way Leadville is layered and the way the ULP interface
11156 * is defined doesn't allow this scenario to be followed easily.  If FCP were to
11157 * handle the PRLO and attempt the PRLI, the reject indicating that a PLOGI is
11158 * needed would be received by FCP. FCP would have, then, to tell the transport
11159 * (fp) to PLOGI.  The problem is, the transport would still think the Port
11160 * Login is valid and there is no way for FCP to tell the transport: "PLOGI even
11161 * if you think it's not necessary".  To work around that difficulty, the PRLO
11162 * is treated by the transport as a LOGO.  The downside to it is a Port Login
11163 * may be disrupted (if a PLOGI wasn't actually needed) and another ULP (that
11164 * has nothing to do with the PRLO) may be impacted.  However, this is a
11165 * scenario very unlikely to happen.  As of today the only ULP in Leadville
11166 * using PRLI/PRLOs is FCP.  For a PRLO to disrupt another ULP (that would be
11167 * FCIP), a SCSI target would have to be running FCP and FCIP (which is very
11168 * unlikely).
11169 */
11170static void
11171fp_handle_unsol_prlo(fc_local_port_t *port, fc_unsol_buf_t *buf,
11172    fc_remote_port_t *pd, job_request_t *job)
11173{
11174	int		busy;
11175	int		rval;
11176	int		retain;
11177	fp_cmd_t	*cmd;
11178	fc_portmap_t 	*listptr;
11179	boolean_t	tolerance;
11180	la_els_prlo_t	*req;
11181
11182	req = (la_els_prlo_t *)buf->ub_buffer;
11183
11184	if ((ntohs(req->payload_length) !=
11185	    (sizeof (service_parameter_page_t) + sizeof (ls_code_t))) ||
11186	    (req->page_length != sizeof (service_parameter_page_t))) {
11187		/*
11188		 * We are being very restrictive.  Only on page per
11189		 * payload.  If it is not the case we reject the ELS although
11190		 * we should reply indicating we handle only single page
11191		 * per PRLO.
11192		 */
11193		goto fp_reject_prlo;
11194	}
11195
11196	if (ntohs(req->payload_length) > buf->ub_bufsize) {
11197		/*
11198		 * This is in case the payload advertizes a size bigger than
11199		 * what it really is.
11200		 */
11201		goto fp_reject_prlo;
11202	}
11203
11204	mutex_enter(&port->fp_mutex);
11205	busy = port->fp_statec_busy;
11206	mutex_exit(&port->fp_mutex);
11207
11208	mutex_enter(&pd->pd_mutex);
11209	tolerance = fctl_tc_increment(&pd->pd_logo_tc);
11210	if (!busy) {
11211		if (pd->pd_state != PORT_DEVICE_LOGGED_IN ||
11212		    pd->pd_state == PORT_DEVICE_INVALID ||
11213		    pd->pd_flags == PD_ELS_IN_PROGRESS ||
11214		    pd->pd_type == PORT_DEVICE_OLD) {
11215			busy++;
11216		}
11217	}
11218
11219	if (busy) {
11220		mutex_exit(&pd->pd_mutex);
11221
11222		FP_TRACE(FP_NHEAD1(5, 0), "Logout; D_ID=%x,"
11223		    "pd=%p - busy",
11224		    pd->pd_port_id.port_id, pd);
11225
11226		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11227			goto fp_reject_prlo;
11228		}
11229	} else {
11230		retain = (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
11231
11232		if (tolerance) {
11233			fctl_tc_reset(&pd->pd_logo_tc);
11234			retain = 0;
11235			pd->pd_state = PORT_DEVICE_INVALID;
11236		}
11237
11238		FP_TRACE(FP_NHEAD1(5, 0), "Accepting LOGO; d_id=%x, pd=%p,"
11239		    " tolerance=%d retain=%d", pd->pd_port_id.port_id, pd,
11240		    tolerance, retain);
11241
11242		pd->pd_aux_flags |= PD_LOGGED_OUT;
11243		mutex_exit(&pd->pd_mutex);
11244
11245		cmd = fp_prlo_acc_init(port, pd, buf, job, KM_SLEEP);
11246		if (cmd == NULL) {
11247			return;
11248		}
11249
11250		rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
11251		if (rval != FC_SUCCESS) {
11252			fp_free_pkt(cmd);
11253			return;
11254		}
11255
11256		listptr = kmem_zalloc(sizeof (fc_portmap_t), KM_SLEEP);
11257
11258		if (retain) {
11259			fp_unregister_login(pd);
11260			fctl_copy_portmap(listptr, pd);
11261		} else {
11262			uint32_t 	d_id;
11263			char 		ww_name[17];
11264
11265			mutex_enter(&pd->pd_mutex);
11266			d_id = pd->pd_port_id.port_id;
11267			fc_wwn_to_str(&pd->pd_port_name, ww_name);
11268			mutex_exit(&pd->pd_mutex);
11269
11270			FP_TRACE(FP_NHEAD2(9, 0),
11271			    "N_x Port with D_ID=%x, PWWN=%s logged out"
11272			    " %d times in %d us; Giving up", d_id, ww_name,
11273			    FC_LOGO_TOLERANCE_LIMIT,
11274			    FC_LOGO_TOLERANCE_TIME_LIMIT);
11275
11276			fp_fillout_old_map(listptr, pd, 0);
11277			listptr->map_type = PORT_DEVICE_OLD;
11278		}
11279
11280		(void) fp_ulp_devc_cb(port, listptr, 1, 1, KM_SLEEP, 0);
11281		return;
11282	}
11283
11284fp_reject_prlo:
11285
11286	cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t), 0, KM_SLEEP, pd);
11287	if (cmd != NULL) {
11288		fp_els_rjt_init(port, cmd, buf, FC_ACTION_NON_RETRYABLE,
11289		    FC_REASON_INVALID_LINK_CTRL, job);
11290
11291		if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
11292			fp_free_pkt(cmd);
11293		}
11294	}
11295}
11296
11297/*
11298 * Unsolicited LOGO handler
11299 */
11300static void
11301fp_handle_unsol_logo(fc_local_port_t *port, fc_unsol_buf_t *buf,
11302    fc_remote_port_t *pd, job_request_t *job)
11303{
11304	int		busy;
11305	int		rval;
11306	int		retain;
11307	fp_cmd_t	*cmd;
11308	fc_portmap_t 	*listptr;
11309	boolean_t	tolerance;
11310
11311	mutex_enter(&port->fp_mutex);
11312	busy = port->fp_statec_busy;
11313	mutex_exit(&port->fp_mutex);
11314
11315	mutex_enter(&pd->pd_mutex);
11316	tolerance = fctl_tc_increment(&pd->pd_logo_tc);
11317	if (!busy) {
11318		if (pd->pd_state != PORT_DEVICE_LOGGED_IN ||
11319		    pd->pd_state == PORT_DEVICE_INVALID ||
11320		    pd->pd_flags == PD_ELS_IN_PROGRESS ||
11321		    pd->pd_type == PORT_DEVICE_OLD) {
11322			busy++;
11323		}
11324	}
11325
11326	if (busy) {
11327		mutex_exit(&pd->pd_mutex);
11328
11329		FP_TRACE(FP_NHEAD1(5, 0), "Logout; D_ID=%x,"
11330		    "pd=%p - busy",
11331		    pd->pd_port_id.port_id, pd);
11332
11333		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11334			cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
11335			    0, KM_SLEEP, pd);
11336			if (cmd != NULL) {
11337				fp_els_rjt_init(port, cmd, buf,
11338				    FC_ACTION_NON_RETRYABLE,
11339				    FC_REASON_INVALID_LINK_CTRL, job);
11340
11341				if (fp_sendcmd(port, cmd,
11342				    port->fp_fca_handle) != FC_SUCCESS) {
11343					fp_free_pkt(cmd);
11344				}
11345			}
11346		}
11347	} else {
11348		retain = (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
11349
11350		if (tolerance) {
11351			fctl_tc_reset(&pd->pd_logo_tc);
11352			retain = 0;
11353			pd->pd_state = PORT_DEVICE_INVALID;
11354		}
11355
11356		FP_TRACE(FP_NHEAD1(5, 0), "Accepting LOGO; d_id=%x, pd=%p,"
11357		    " tolerance=%d retain=%d", pd->pd_port_id.port_id, pd,
11358		    tolerance, retain);
11359
11360		pd->pd_aux_flags |= PD_LOGGED_OUT;
11361		mutex_exit(&pd->pd_mutex);
11362
11363		cmd = fp_alloc_pkt(port, FP_PORT_IDENTIFIER_LEN, 0,
11364		    KM_SLEEP, pd);
11365		if (cmd == NULL) {
11366			return;
11367		}
11368
11369		fp_els_acc_init(port, cmd, buf, job);
11370
11371		rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
11372		if (rval != FC_SUCCESS) {
11373			fp_free_pkt(cmd);
11374			return;
11375		}
11376
11377		listptr = kmem_zalloc(sizeof (fc_portmap_t), KM_SLEEP);
11378
11379		if (retain) {
11380			job_request_t	*job;
11381			fctl_ns_req_t	*ns_cmd;
11382
11383			/*
11384			 * when get LOGO, first try to get PID from nameserver
11385			 * if failed, then we do not need
11386			 * send PLOGI to that remote port
11387			 */
11388			job = fctl_alloc_job(
11389			    JOB_NS_CMD, 0, NULL, (opaque_t)port, KM_SLEEP);
11390
11391			if (job != NULL) {
11392				ns_cmd = fctl_alloc_ns_cmd(
11393				    sizeof (ns_req_gid_pn_t),
11394				    sizeof (ns_resp_gid_pn_t),
11395				    sizeof (ns_resp_gid_pn_t),
11396				    0, KM_SLEEP);
11397				if (ns_cmd != NULL) {
11398					int ret;
11399					job->job_result = FC_SUCCESS;
11400					ns_cmd->ns_cmd_code = NS_GID_PN;
11401					((ns_req_gid_pn_t *)
11402					    (ns_cmd->ns_cmd_buf))->pwwn =
11403					    pd->pd_port_name;
11404					ret = fp_ns_query(
11405					    port, ns_cmd, job, 1, KM_SLEEP);
11406					if ((ret != FC_SUCCESS) ||
11407					    (job->job_result != FC_SUCCESS)) {
11408						fctl_free_ns_cmd(ns_cmd);
11409						fctl_dealloc_job(job);
11410						FP_TRACE(FP_NHEAD2(9, 0),
11411						    "NS query failed,",
11412						    " delete pd");
11413						goto delete_pd;
11414					}
11415					fctl_free_ns_cmd(ns_cmd);
11416				}
11417				fctl_dealloc_job(job);
11418			}
11419			fp_unregister_login(pd);
11420			fctl_copy_portmap(listptr, pd);
11421		} else {
11422			uint32_t 	d_id;
11423			char 		ww_name[17];
11424
11425delete_pd:
11426			mutex_enter(&pd->pd_mutex);
11427			d_id = pd->pd_port_id.port_id;
11428			fc_wwn_to_str(&pd->pd_port_name, ww_name);
11429			mutex_exit(&pd->pd_mutex);
11430
11431			FP_TRACE(FP_NHEAD2(9, 0),
11432			    "N_x Port with D_ID=%x, PWWN=%s logged out"
11433			    " %d times in %d us; Giving up", d_id, ww_name,
11434			    FC_LOGO_TOLERANCE_LIMIT,
11435			    FC_LOGO_TOLERANCE_TIME_LIMIT);
11436
11437			fp_fillout_old_map(listptr, pd, 0);
11438			listptr->map_type = PORT_DEVICE_OLD;
11439		}
11440
11441		(void) fp_ulp_devc_cb(port, listptr, 1, 1, KM_SLEEP, 0);
11442	}
11443}
11444
11445
11446/*
11447 * Perform general purpose preparation of a response to an unsolicited request
11448 */
11449static void
11450fp_unsol_resp_init(fc_packet_t *pkt, fc_unsol_buf_t *buf,
11451    uchar_t r_ctl, uchar_t type)
11452{
11453	pkt->pkt_cmd_fhdr.r_ctl = r_ctl;
11454	pkt->pkt_cmd_fhdr.d_id = buf->ub_frame.s_id;
11455	pkt->pkt_cmd_fhdr.s_id = buf->ub_frame.d_id;
11456	pkt->pkt_cmd_fhdr.type = type;
11457	pkt->pkt_cmd_fhdr.f_ctl = F_CTL_LAST_SEQ | F_CTL_XCHG_CONTEXT;
11458	pkt->pkt_cmd_fhdr.seq_id = buf->ub_frame.seq_id;
11459	pkt->pkt_cmd_fhdr.df_ctl  = buf->ub_frame.df_ctl;
11460	pkt->pkt_cmd_fhdr.seq_cnt = buf->ub_frame.seq_cnt;
11461	pkt->pkt_cmd_fhdr.ox_id = buf->ub_frame.ox_id;
11462	pkt->pkt_cmd_fhdr.rx_id = buf->ub_frame.rx_id;
11463	pkt->pkt_cmd_fhdr.ro = 0;
11464	pkt->pkt_cmd_fhdr.rsvd = 0;
11465	pkt->pkt_comp = fp_unsol_intr;
11466	pkt->pkt_timeout = FP_ELS_TIMEOUT;
11467}
11468
11469/*
11470 * Immediate handling of unsolicited FLOGI and PLOGI requests. In the
11471 * early development days of public loop soc+ firmware, numerous problems
11472 * were encountered (the details are undocumented and history now) which
11473 * led to the birth of this function.
11474 *
11475 * If a pre-allocated unsolicited response packet is free, send out an
11476 * immediate response, otherwise submit the request to the port thread
11477 * to do the deferred processing.
11478 */
11479static void
11480fp_i_handle_unsol_els(fc_local_port_t *port, fc_unsol_buf_t *buf)
11481{
11482	int			sent;
11483	int 			f_port;
11484	int			do_acc;
11485	fp_cmd_t 		*cmd;
11486	la_els_logi_t 		*payload;
11487	fc_remote_port_t	*pd;
11488	char			dww_name[17];
11489
11490	ASSERT(!MUTEX_HELD(&port->fp_mutex));
11491
11492	cmd = port->fp_els_resp_pkt;
11493
11494	mutex_enter(&port->fp_mutex);
11495	do_acc = (port->fp_statec_busy == 0) ? 1 : 0;
11496	mutex_exit(&port->fp_mutex);
11497
11498	switch (buf->ub_buffer[0]) {
11499	case LA_ELS_PLOGI: {
11500		int small;
11501
11502		payload = (la_els_logi_t *)buf->ub_buffer;
11503
11504		f_port = FP_IS_F_PORT(payload->
11505		    common_service.cmn_features) ? 1 : 0;
11506
11507		small = fctl_wwn_cmp(&port->fp_service_params.nport_ww_name,
11508		    &payload->nport_ww_name);
11509		pd = fctl_get_remote_port_by_pwwn(port,
11510		    &payload->nport_ww_name);
11511		if (pd) {
11512			mutex_enter(&pd->pd_mutex);
11513			sent = (pd->pd_flags == PD_ELS_IN_PROGRESS) ? 1 : 0;
11514			/*
11515			 * Most likely this means a cross login is in
11516			 * progress or a device about to be yanked out.
11517			 * Only accept the plogi if my wwn is smaller.
11518			 */
11519			if (pd->pd_type == PORT_DEVICE_OLD) {
11520				sent = 1;
11521			}
11522			/*
11523			 * Stop plogi request (if any)
11524			 * attempt from local side to speedup
11525			 * the discovery progress.
11526			 * Mark the pd as PD_PLOGI_RECEPIENT.
11527			 */
11528			if (f_port == 0 && small < 0) {
11529				pd->pd_recepient = PD_PLOGI_RECEPIENT;
11530			}
11531			fc_wwn_to_str(&pd->pd_port_name, dww_name);
11532
11533			mutex_exit(&pd->pd_mutex);
11534
11535			FP_TRACE(FP_NHEAD1(3, 0), "fp_i_handle_unsol_els: "
11536			    "Unsol PLOGI received. PD still exists in the "
11537			    "PWWN list. pd=%p PWWN=%s, sent=%x",
11538			    pd, dww_name, sent);
11539
11540			if (f_port == 0 && small < 0) {
11541				FP_TRACE(FP_NHEAD1(3, 0),
11542				    "fp_i_handle_unsol_els: Mark the pd"
11543				    " as plogi recipient, pd=%p, PWWN=%s"
11544				    ", sent=%x",
11545				    pd, dww_name, sent);
11546			}
11547		} else {
11548			sent = 0;
11549		}
11550
11551		/*
11552		 * To avoid Login collisions, accept only if my WWN
11553		 * is smaller than the requester (A curious side note
11554		 * would be that this rule may not satisfy the PLOGIs
11555		 * initiated by the switch from not-so-well known
11556		 * ports such as 0xFFFC41)
11557		 */
11558		if ((f_port == 0 && small < 0) ||
11559		    (((small > 0 && do_acc) ||
11560		    FC_MUST_ACCEPT_D_ID(buf->ub_frame.s_id)) && sent == 0)) {
11561			if (fp_is_class_supported(port->fp_cos,
11562			    buf->ub_class) == FC_FAILURE) {
11563				if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11564					cmd->cmd_pkt.pkt_cmdlen =
11565					    sizeof (la_els_rjt_t);
11566					cmd->cmd_pkt.pkt_rsplen = 0;
11567					fp_els_rjt_init(port, cmd, buf,
11568					    FC_ACTION_NON_RETRYABLE,
11569					    FC_REASON_CLASS_NOT_SUPP, NULL);
11570					FP_TRACE(FP_NHEAD1(3, 0),
11571					    "fp_i_handle_unsol_els: "
11572					    "Unsupported class. "
11573					    "Rejecting PLOGI");
11574
11575				} else {
11576					mutex_enter(&port->fp_mutex);
11577					port->fp_els_resp_pkt_busy = 0;
11578					mutex_exit(&port->fp_mutex);
11579					return;
11580				}
11581			} else {
11582				cmd->cmd_pkt.pkt_cmdlen =
11583				    sizeof (la_els_logi_t);
11584				cmd->cmd_pkt.pkt_rsplen = 0;
11585
11586				/*
11587				 * Sometime later, we should validate
11588				 * the service parameters instead of
11589				 * just accepting it.
11590				 */
11591				fp_login_acc_init(port, cmd, buf, NULL,
11592				    KM_NOSLEEP);
11593				FP_TRACE(FP_NHEAD1(3, 0),
11594				    "fp_i_handle_unsol_els: Accepting PLOGI,"
11595				    " f_port=%d, small=%d, do_acc=%d,"
11596				    " sent=%d.", f_port, small, do_acc,
11597				    sent);
11598				/*
11599				 * If fp_port_id is zero and topology is
11600				 * Point-to-Point, get the local port id from
11601				 * the d_id in the PLOGI request.
11602				 * If the outgoing FLOGI hasn't been accepted,
11603				 * the topology will be unknown here. But it's
11604				 * still safe to save the d_id to fp_port_id,
11605				 * just because it will be overwritten later
11606				 * if the topology is not Point-to-Point.
11607				 */
11608				mutex_enter(&port->fp_mutex);
11609				if ((port->fp_port_id.port_id == 0) &&
11610				    (port->fp_topology == FC_TOP_PT_PT ||
11611				    port->fp_topology == FC_TOP_UNKNOWN)) {
11612					port->fp_port_id.port_id =
11613					    buf->ub_frame.d_id;
11614				}
11615				mutex_exit(&port->fp_mutex);
11616			}
11617		} else {
11618			if (FP_IS_CLASS_1_OR_2(buf->ub_class) ||
11619			    port->fp_options & FP_SEND_RJT) {
11620				cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_rjt_t);
11621				cmd->cmd_pkt.pkt_rsplen = 0;
11622				fp_els_rjt_init(port, cmd, buf,
11623				    FC_ACTION_NON_RETRYABLE,
11624				    FC_REASON_LOGICAL_BSY, NULL);
11625				FP_TRACE(FP_NHEAD1(3, 0),
11626				    "fp_i_handle_unsol_els: "
11627				    "Rejecting PLOGI with Logical Busy."
11628				    "Possible Login collision.");
11629			} else {
11630				mutex_enter(&port->fp_mutex);
11631				port->fp_els_resp_pkt_busy = 0;
11632				mutex_exit(&port->fp_mutex);
11633				return;
11634			}
11635		}
11636		break;
11637	}
11638
11639	case LA_ELS_FLOGI:
11640		if (fp_is_class_supported(port->fp_cos,
11641		    buf->ub_class) == FC_FAILURE) {
11642			if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11643				cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_rjt_t);
11644				cmd->cmd_pkt.pkt_rsplen = 0;
11645				fp_els_rjt_init(port, cmd, buf,
11646				    FC_ACTION_NON_RETRYABLE,
11647				    FC_REASON_CLASS_NOT_SUPP, NULL);
11648				FP_TRACE(FP_NHEAD1(3, 0),
11649				    "fp_i_handle_unsol_els: "
11650				    "Unsupported Class. Rejecting FLOGI.");
11651			} else {
11652				mutex_enter(&port->fp_mutex);
11653				port->fp_els_resp_pkt_busy = 0;
11654				mutex_exit(&port->fp_mutex);
11655				return;
11656			}
11657		} else {
11658			mutex_enter(&port->fp_mutex);
11659			if (FC_PORT_STATE_MASK(port->fp_state) !=
11660			    FC_STATE_ONLINE || (port->fp_port_id.port_id &&
11661			    buf->ub_frame.s_id == port->fp_port_id.port_id)) {
11662				mutex_exit(&port->fp_mutex);
11663				if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11664					cmd->cmd_pkt.pkt_cmdlen =
11665					    sizeof (la_els_rjt_t);
11666					cmd->cmd_pkt.pkt_rsplen = 0;
11667					fp_els_rjt_init(port, cmd, buf,
11668					    FC_ACTION_NON_RETRYABLE,
11669					    FC_REASON_INVALID_LINK_CTRL,
11670					    NULL);
11671					FP_TRACE(FP_NHEAD1(3, 0),
11672					    "fp_i_handle_unsol_els: "
11673					    "Invalid Link Ctrl. "
11674					    "Rejecting FLOGI.");
11675				} else {
11676					mutex_enter(&port->fp_mutex);
11677					port->fp_els_resp_pkt_busy = 0;
11678					mutex_exit(&port->fp_mutex);
11679					return;
11680				}
11681			} else {
11682				mutex_exit(&port->fp_mutex);
11683				cmd->cmd_pkt.pkt_cmdlen =
11684				    sizeof (la_els_logi_t);
11685				cmd->cmd_pkt.pkt_rsplen = 0;
11686				/*
11687				 * Let's not aggressively validate the N_Port's
11688				 * service parameters until PLOGI. Suffice it
11689				 * to give a hint that we are an N_Port and we
11690				 * are game to some serious stuff here.
11691				 */
11692				fp_login_acc_init(port, cmd, buf,
11693				    NULL, KM_NOSLEEP);
11694				FP_TRACE(FP_NHEAD1(3, 0),
11695				    "fp_i_handle_unsol_els: "
11696				    "Accepting FLOGI.");
11697			}
11698		}
11699		break;
11700
11701	default:
11702		return;
11703	}
11704
11705	if ((fp_sendcmd(port, cmd, port->fp_fca_handle)) != FC_SUCCESS) {
11706		mutex_enter(&port->fp_mutex);
11707		port->fp_els_resp_pkt_busy = 0;
11708		mutex_exit(&port->fp_mutex);
11709	}
11710}
11711
11712
11713/*
11714 * Handle unsolicited PLOGI request
11715 */
11716static void
11717fp_handle_unsol_plogi(fc_local_port_t *port, fc_unsol_buf_t *buf,
11718    job_request_t *job, int sleep)
11719{
11720	int			sent;
11721	int			small;
11722	int 			f_port;
11723	int			do_acc;
11724	fp_cmd_t		*cmd;
11725	la_wwn_t 		*swwn;
11726	la_wwn_t		*dwwn;
11727	la_els_logi_t 		*payload;
11728	fc_remote_port_t	*pd;
11729	char			dww_name[17];
11730
11731	payload = (la_els_logi_t *)buf->ub_buffer;
11732	f_port = FP_IS_F_PORT(payload->common_service.cmn_features) ? 1 : 0;
11733
11734	mutex_enter(&port->fp_mutex);
11735	do_acc = (port->fp_statec_busy == 0) ? 1 : 0;
11736	mutex_exit(&port->fp_mutex);
11737
11738	FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_plogi: s_id=%x, d_id=%x,"
11739	    "type=%x, f_ctl=%x"
11740	    " seq_id=%x, ox_id=%x, rx_id=%x"
11741	    " ro=%x", buf->ub_frame.s_id, buf->ub_frame.d_id,
11742	    buf->ub_frame.type, buf->ub_frame.f_ctl, buf->ub_frame.seq_id,
11743	    buf->ub_frame.ox_id, buf->ub_frame.rx_id, buf->ub_frame.ro);
11744
11745	swwn = &port->fp_service_params.nport_ww_name;
11746	dwwn = &payload->nport_ww_name;
11747	small = fctl_wwn_cmp(swwn, dwwn);
11748	pd = fctl_get_remote_port_by_pwwn(port, dwwn);
11749	if (pd) {
11750		mutex_enter(&pd->pd_mutex);
11751		sent = (pd->pd_flags == PD_ELS_IN_PROGRESS) ? 1 : 0;
11752		/*
11753		 * Most likely this means a cross login is in
11754		 * progress or a device about to be yanked out.
11755		 * Only accept the plogi if my wwn is smaller.
11756		 */
11757
11758		if (pd->pd_type == PORT_DEVICE_OLD) {
11759			sent = 1;
11760		}
11761		/*
11762		 * Stop plogi request (if any)
11763		 * attempt from local side to speedup
11764		 * the discovery progress.
11765		 * Mark the pd as PD_PLOGI_RECEPIENT.
11766		 */
11767		if (f_port == 0 && small < 0) {
11768			pd->pd_recepient = PD_PLOGI_RECEPIENT;
11769		}
11770		fc_wwn_to_str(&pd->pd_port_name, dww_name);
11771
11772		mutex_exit(&pd->pd_mutex);
11773
11774		FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_plogi: Unsol PLOGI"
11775		    " received. PD still exists in the PWWN list. pd=%p "
11776		    "PWWN=%s, sent=%x", pd, dww_name, sent);
11777
11778		if (f_port == 0 && small < 0) {
11779			FP_TRACE(FP_NHEAD1(3, 0),
11780			    "fp_handle_unsol_plogi: Mark the pd"
11781			    " as plogi recipient, pd=%p, PWWN=%s"
11782			    ", sent=%x",
11783			    pd, dww_name, sent);
11784		}
11785	} else {
11786		sent = 0;
11787	}
11788
11789	/*
11790	 * Avoid Login collisions by accepting only if my WWN is smaller.
11791	 *
11792	 * A side note: There is no need to start a PLOGI from this end in
11793	 *	this context if login isn't going to be accepted for the
11794	 * 	above reason as either a LIP (in private loop), RSCN (in
11795	 *	fabric topology), or an FLOGI (in point to point - Huh ?
11796	 *	check FC-PH) would normally drive the PLOGI from this end.
11797	 *	At this point of time there is no need for an inbound PLOGI
11798	 *	to kick an outbound PLOGI when it is going to be rejected
11799	 *	for the reason of WWN being smaller. However it isn't hard
11800	 *	to do that either (when such a need arises, start a timer
11801	 *	for a duration that extends beyond a normal device discovery
11802	 *	time and check if an outbound PLOGI did go before that, if
11803	 *	none fire one)
11804	 *
11805	 *	Unfortunately, as it turned out, during booting, it is possible
11806	 *	to miss another initiator in the same loop as port driver
11807	 * 	instances are serially attached. While preserving the above
11808	 * 	comments for belly laughs, please kick an outbound PLOGI in
11809	 *	a non-switch environment (which is a pt pt between N_Ports or
11810	 *	a private loop)
11811	 *
11812	 *	While preserving the above comments for amusement, send an
11813	 *	ACC if the PLOGI is going to be rejected for WWN being smaller
11814	 *	when no discovery is in progress at this end. Turn around
11815	 *	and make the port device as the PLOGI initiator, so that
11816	 *	during subsequent link/loop initialization, this end drives
11817	 *	the PLOGI (In fact both ends do in this particular case, but
11818	 *	only one wins)
11819	 *
11820	 * Make sure the PLOGIs initiated by the switch from not-so-well-known
11821	 * ports (such as 0xFFFC41) are accepted too.
11822	 */
11823	if ((f_port == 0 && small < 0) || (((small > 0 && do_acc) ||
11824	    FC_MUST_ACCEPT_D_ID(buf->ub_frame.s_id)) && sent == 0)) {
11825		if (fp_is_class_supported(port->fp_cos,
11826		    buf->ub_class) == FC_FAILURE) {
11827			if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11828				cmd = fp_alloc_pkt(port,
11829				    sizeof (la_els_logi_t), 0, sleep, pd);
11830				if (cmd == NULL) {
11831					return;
11832				}
11833				cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_rjt_t);
11834				cmd->cmd_pkt.pkt_rsplen = 0;
11835				fp_els_rjt_init(port, cmd, buf,
11836				    FC_ACTION_NON_RETRYABLE,
11837				    FC_REASON_CLASS_NOT_SUPP, job);
11838				FP_TRACE(FP_NHEAD1(3, 0),
11839				    "fp_handle_unsol_plogi: "
11840				    "Unsupported class. rejecting PLOGI");
11841			}
11842		} else {
11843			cmd = fp_alloc_pkt(port, sizeof (la_els_logi_t),
11844			    0, sleep, pd);
11845			if (cmd == NULL) {
11846				return;
11847			}
11848			cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_logi_t);
11849			cmd->cmd_pkt.pkt_rsplen = 0;
11850
11851			/*
11852			 * Sometime later, we should validate the service
11853			 * parameters instead of just accepting it.
11854			 */
11855			fp_login_acc_init(port, cmd, buf, job, KM_SLEEP);
11856			FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_plogi: "
11857			    "Accepting PLOGI, f_port=%d, small=%d, "
11858			    "do_acc=%d, sent=%d.", f_port, small, do_acc,
11859			    sent);
11860
11861			/*
11862			 * If fp_port_id is zero and topology is
11863			 * Point-to-Point, get the local port id from
11864			 * the d_id in the PLOGI request.
11865			 * If the outgoing FLOGI hasn't been accepted,
11866			 * the topology will be unknown here. But it's
11867			 * still safe to save the d_id to fp_port_id,
11868			 * just because it will be overwritten later
11869			 * if the topology is not Point-to-Point.
11870			 */
11871			mutex_enter(&port->fp_mutex);
11872			if ((port->fp_port_id.port_id == 0) &&
11873			    (port->fp_topology == FC_TOP_PT_PT ||
11874			    port->fp_topology == FC_TOP_UNKNOWN)) {
11875				port->fp_port_id.port_id =
11876				    buf->ub_frame.d_id;
11877			}
11878			mutex_exit(&port->fp_mutex);
11879		}
11880	} else {
11881		if (FP_IS_CLASS_1_OR_2(buf->ub_class) ||
11882		    port->fp_options & FP_SEND_RJT) {
11883			cmd = fp_alloc_pkt(port, sizeof (la_els_logi_t),
11884			    0, sleep, pd);
11885			if (cmd == NULL) {
11886				return;
11887			}
11888			cmd->cmd_pkt.pkt_cmdlen = sizeof (la_els_rjt_t);
11889			cmd->cmd_pkt.pkt_rsplen = 0;
11890			/*
11891			 * Send out Logical busy to indicate
11892			 * the detection of PLOGI collision
11893			 */
11894			fp_els_rjt_init(port, cmd, buf,
11895			    FC_ACTION_NON_RETRYABLE,
11896			    FC_REASON_LOGICAL_BSY, job);
11897
11898			fc_wwn_to_str(dwwn, dww_name);
11899			FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_plogi: "
11900			    "Rejecting Unsol PLOGI with Logical Busy."
11901			    "possible PLOGI collision. PWWN=%s, sent=%x",
11902			    dww_name, sent);
11903		} else {
11904			return;
11905		}
11906	}
11907
11908	if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
11909		fp_free_pkt(cmd);
11910	}
11911}
11912
11913
11914/*
11915 * Handle mischievous turning over of our own FLOGI requests back to
11916 * us by the SOC+ microcode. In other words, look at the class of such
11917 * bone headed requests, if 1 or 2, bluntly P_RJT them, if 3 drop them
11918 * on the floor
11919 */
11920static void
11921fp_handle_unsol_flogi(fc_local_port_t *port, fc_unsol_buf_t *buf,
11922	job_request_t *job, int sleep)
11923{
11924	uint32_t	state;
11925	uint32_t	s_id;
11926	fp_cmd_t 	*cmd;
11927
11928	if (fp_is_class_supported(port->fp_cos, buf->ub_class) == FC_FAILURE) {
11929		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11930			cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
11931			    0, sleep, NULL);
11932			if (cmd == NULL) {
11933				return;
11934			}
11935			fp_els_rjt_init(port, cmd, buf,
11936			    FC_ACTION_NON_RETRYABLE,
11937			    FC_REASON_CLASS_NOT_SUPP, job);
11938		} else {
11939			return;
11940		}
11941	} else {
11942
11943		FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_flogi:"
11944		    " s_id=%x, d_id=%x, type=%x, f_ctl=%x"
11945		    " seq_id=%x, ox_id=%x, rx_id=%x, ro=%x",
11946		    buf->ub_frame.s_id, buf->ub_frame.d_id,
11947		    buf->ub_frame.type, buf->ub_frame.f_ctl,
11948		    buf->ub_frame.seq_id, buf->ub_frame.ox_id,
11949		    buf->ub_frame.rx_id, buf->ub_frame.ro);
11950
11951		mutex_enter(&port->fp_mutex);
11952		state = FC_PORT_STATE_MASK(port->fp_state);
11953		s_id = port->fp_port_id.port_id;
11954		mutex_exit(&port->fp_mutex);
11955
11956		if (state != FC_STATE_ONLINE ||
11957		    (s_id && buf->ub_frame.s_id == s_id)) {
11958			if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
11959				cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
11960				    0, sleep, NULL);
11961				if (cmd == NULL) {
11962					return;
11963				}
11964				fp_els_rjt_init(port, cmd, buf,
11965				    FC_ACTION_NON_RETRYABLE,
11966				    FC_REASON_INVALID_LINK_CTRL, job);
11967				FP_TRACE(FP_NHEAD1(3, 0),
11968				    "fp_handle_unsol_flogi: "
11969				    "Rejecting PLOGI. Invalid Link CTRL");
11970			} else {
11971				return;
11972			}
11973		} else {
11974			cmd = fp_alloc_pkt(port, sizeof (la_els_logi_t),
11975			    0, sleep, NULL);
11976			if (cmd == NULL) {
11977				return;
11978			}
11979			/*
11980			 * Let's not aggressively validate the N_Port's
11981			 * service parameters until PLOGI. Suffice it
11982			 * to give a hint that we are an N_Port and we
11983			 * are game to some serious stuff here.
11984			 */
11985			fp_login_acc_init(port, cmd, buf, job, KM_SLEEP);
11986			FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_flogi: "
11987			    "Accepting PLOGI");
11988		}
11989	}
11990
11991	if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
11992		fp_free_pkt(cmd);
11993	}
11994}
11995
11996
11997/*
11998 * Perform PLOGI accept
11999 */
12000static void
12001fp_login_acc_init(fc_local_port_t *port, fp_cmd_t *cmd, fc_unsol_buf_t *buf,
12002    job_request_t *job, int sleep)
12003{
12004	fc_packet_t	*pkt;
12005	fc_portmap_t 	*listptr;
12006	la_els_logi_t	payload;
12007
12008	ASSERT(buf != NULL);
12009
12010	/*
12011	 * If we are sending ACC to PLOGI and we haven't already
12012	 * create port and node device handles, let's create them
12013	 * here.
12014	 */
12015	if (buf->ub_buffer[0] == LA_ELS_PLOGI &&
12016	    FC_IS_REAL_DEVICE(buf->ub_frame.s_id)) {
12017		int			small;
12018		int			do_acc;
12019		fc_remote_port_t	*pd;
12020		la_els_logi_t 		*req;
12021
12022		req = (la_els_logi_t *)buf->ub_buffer;
12023		small = fctl_wwn_cmp(&port->fp_service_params.nport_ww_name,
12024		    &req->nport_ww_name);
12025
12026		mutex_enter(&port->fp_mutex);
12027		do_acc = (port->fp_statec_busy == 0) ? 1 : 0;
12028		mutex_exit(&port->fp_mutex);
12029
12030		pd = fctl_create_remote_port(port, &req->node_ww_name,
12031		    &req->nport_ww_name, buf->ub_frame.s_id,
12032		    PD_PLOGI_RECEPIENT, sleep);
12033		if (pd == NULL) {
12034			FP_TRACE(FP_NHEAD1(3, 0), "login_acc_init: "
12035			    "Couldn't create port device for d_id:0x%x",
12036			    buf->ub_frame.s_id);
12037
12038			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
12039			    "couldn't create port device d_id=%x",
12040			    buf->ub_frame.s_id);
12041		} else {
12042			/*
12043			 * usoc currently returns PLOGIs inline and
12044			 * the maximum buffer size is 60 bytes or so.
12045			 * So attempt not to look beyond what is in
12046			 * the unsolicited buffer
12047			 *
12048			 * JNI also traverses this path sometimes
12049			 */
12050			if (buf->ub_bufsize >= sizeof (la_els_logi_t)) {
12051				fp_register_login(NULL, pd, req, buf->ub_class);
12052			} else {
12053				mutex_enter(&pd->pd_mutex);
12054				if (pd->pd_login_count == 0) {
12055					pd->pd_login_count++;
12056				}
12057				pd->pd_state = PORT_DEVICE_LOGGED_IN;
12058				pd->pd_login_class = buf->ub_class;
12059				mutex_exit(&pd->pd_mutex);
12060			}
12061
12062			listptr = kmem_zalloc(sizeof (fc_portmap_t), sleep);
12063			if (listptr != NULL) {
12064				fctl_copy_portmap(listptr, pd);
12065				(void) fp_ulp_devc_cb(port, listptr,
12066				    1, 1, sleep, 0);
12067			}
12068
12069			if (small > 0 && do_acc) {
12070				mutex_enter(&pd->pd_mutex);
12071				pd->pd_recepient = PD_PLOGI_INITIATOR;
12072				mutex_exit(&pd->pd_mutex);
12073			}
12074		}
12075	}
12076
12077	cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
12078	cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
12079	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
12080	cmd->cmd_retry_count = 1;
12081	cmd->cmd_ulp_pkt = NULL;
12082
12083	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
12084	cmd->cmd_job = job;
12085
12086	pkt = &cmd->cmd_pkt;
12087
12088	fp_unsol_resp_init(pkt, buf, R_CTL_ELS_RSP, FC_TYPE_EXTENDED_LS);
12089
12090	payload = port->fp_service_params;
12091	payload.ls_code.ls_code = LA_ELS_ACC;
12092
12093	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
12094	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
12095
12096	FP_TRACE(FP_NHEAD1(3, 0), "login_acc_init: ELS:0x%x d_id:0x%x "
12097	    "bufsize:0x%x sizeof(la_els_logi):0x%x "
12098	    "port's wwn:0x%01x%03x%04x%08x requestor's wwn:0x%01x%03x%04x%08x "
12099	    "statec_busy:0x%x", buf->ub_buffer[0], buf->ub_frame.s_id,
12100	    buf->ub_bufsize, sizeof (la_els_logi_t),
12101	    port->fp_service_params.nport_ww_name.w.naa_id,
12102	    port->fp_service_params.nport_ww_name.w.nport_id,
12103	    port->fp_service_params.nport_ww_name.w.wwn_hi,
12104	    port->fp_service_params.nport_ww_name.w.wwn_lo,
12105	    ((la_els_logi_t *)buf->ub_buffer)->nport_ww_name.w.naa_id,
12106	    ((la_els_logi_t *)buf->ub_buffer)->nport_ww_name.w.nport_id,
12107	    ((la_els_logi_t *)buf->ub_buffer)->nport_ww_name.w.wwn_hi,
12108	    ((la_els_logi_t *)buf->ub_buffer)->nport_ww_name.w.wwn_lo,
12109	    port->fp_statec_busy);
12110}
12111
12112
12113#define	RSCN_EVENT_NAME_LEN	256
12114
12115/*
12116 * Handle RSCNs
12117 */
12118static void
12119fp_handle_unsol_rscn(fc_local_port_t *port, fc_unsol_buf_t *buf,
12120    job_request_t *job, int sleep)
12121{
12122	uint32_t		mask;
12123	fp_cmd_t		*cmd;
12124	uint32_t		count;
12125	int			listindex;
12126	int16_t			len;
12127	fc_rscn_t		*payload;
12128	fc_portmap_t 		*listptr;
12129	fctl_ns_req_t		*ns_cmd;
12130	fc_affected_id_t 	*page;
12131	caddr_t			nvname;
12132	nvlist_t		*attr_list = NULL;
12133
12134	mutex_enter(&port->fp_mutex);
12135	if (!FC_IS_TOP_SWITCH(port->fp_topology)) {
12136		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
12137			--port->fp_rscn_count;
12138		}
12139		mutex_exit(&port->fp_mutex);
12140		return;
12141	}
12142	mutex_exit(&port->fp_mutex);
12143
12144	cmd = fp_alloc_pkt(port, FP_PORT_IDENTIFIER_LEN, 0, sleep, NULL);
12145	if (cmd != NULL) {
12146		fp_els_acc_init(port, cmd, buf, job);
12147		if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
12148			fp_free_pkt(cmd);
12149		}
12150	}
12151
12152	payload = (fc_rscn_t *)buf->ub_buffer;
12153	ASSERT(payload->rscn_code == LA_ELS_RSCN);
12154	ASSERT(payload->rscn_len == FP_PORT_IDENTIFIER_LEN);
12155
12156	len = payload->rscn_payload_len - FP_PORT_IDENTIFIER_LEN;
12157
12158	if (len <= 0) {
12159		mutex_enter(&port->fp_mutex);
12160		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
12161			--port->fp_rscn_count;
12162		}
12163		mutex_exit(&port->fp_mutex);
12164
12165		return;
12166	}
12167
12168	ASSERT((len & 0x3) == 0);	/* Must be power of 4 */
12169	count = (len >> 2) << 1;	/* number of pages multiplied by 2 */
12170
12171	listptr = kmem_zalloc(sizeof (fc_portmap_t) * count, sleep);
12172	page = (fc_affected_id_t *)(buf->ub_buffer + sizeof (fc_rscn_t));
12173
12174	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
12175
12176	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gpn_id_t),
12177	    sizeof (ns_resp_gpn_id_t), sizeof (ns_resp_gpn_id_t),
12178	    0, sleep);
12179	if (ns_cmd == NULL) {
12180		kmem_free(listptr, sizeof (fc_portmap_t) * count);
12181
12182		mutex_enter(&port->fp_mutex);
12183		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
12184			--port->fp_rscn_count;
12185		}
12186		mutex_exit(&port->fp_mutex);
12187
12188		return;
12189	}
12190
12191	ns_cmd->ns_cmd_code = NS_GPN_ID;
12192
12193	FP_TRACE(FP_NHEAD1(3, 0), "fp_handle_unsol_rscn: s_id=%x, d_id=%x,"
12194	    "type=%x, f_ctl=%x seq_id=%x, ox_id=%x, rx_id=%x"
12195	    " ro=%x", buf->ub_frame.s_id, buf->ub_frame.d_id,
12196	    buf->ub_frame.type, buf->ub_frame.f_ctl, buf->ub_frame.seq_id,
12197	    buf->ub_frame.ox_id, buf->ub_frame.rx_id, buf->ub_frame.ro);
12198
12199	/* Only proceed if we can allocate nvname and the nvlist */
12200	if ((nvname = kmem_zalloc(RSCN_EVENT_NAME_LEN, KM_NOSLEEP)) != NULL &&
12201		nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
12202		KM_NOSLEEP) == DDI_SUCCESS) {
12203	    if (!(attr_list && nvlist_add_uint32(attr_list, "instance",
12204		    port->fp_instance) == DDI_SUCCESS &&
12205		    nvlist_add_byte_array(attr_list, "port-wwn",
12206		    port->fp_service_params.nport_ww_name.raw_wwn,
12207		    sizeof (la_wwn_t)) == DDI_SUCCESS)) {
12208		nvlist_free(attr_list);
12209		attr_list = NULL;
12210	    }
12211	}
12212
12213	for (listindex = 0; len; len -= FP_PORT_IDENTIFIER_LEN, page++) {
12214		/* Add affected page to the event payload */
12215		if (attr_list != NULL) {
12216		    (void) snprintf(nvname, RSCN_EVENT_NAME_LEN,
12217			    "affected_page_%d", listindex);
12218		    if (attr_list && nvlist_add_uint32(attr_list, nvname,
12219			    ntohl(*(uint32_t *)page)) != DDI_SUCCESS) {
12220			/* We don't want to send a partial event, so dump it */
12221			nvlist_free(attr_list);
12222			attr_list = NULL;
12223		    }
12224		}
12225		/*
12226		 * Query the NS to get the Port WWN for this
12227		 * affected D_ID.
12228		 */
12229		mask = 0;
12230		switch (page->aff_format & FC_RSCN_ADDRESS_MASK) {
12231		case FC_RSCN_PORT_ADDRESS:
12232			fp_validate_rscn_page(port, page, job, ns_cmd,
12233			    listptr, &listindex, sleep);
12234
12235			if (listindex == 0) {
12236				/*
12237				 * We essentially did not process this RSCN. So,
12238				 * ULPs are not going to be called and so we
12239				 * decrement the rscn_count
12240				 */
12241				mutex_enter(&port->fp_mutex);
12242				if (--port->fp_rscn_count ==
12243				    FC_INVALID_RSCN_COUNT) {
12244					--port->fp_rscn_count;
12245				}
12246				mutex_exit(&port->fp_mutex);
12247			}
12248			break;
12249
12250		case FC_RSCN_AREA_ADDRESS:
12251			mask = 0xFFFF00;
12252			/* FALLTHROUGH */
12253
12254		case FC_RSCN_DOMAIN_ADDRESS:
12255			if (!mask) {
12256				mask = 0xFF0000;
12257			}
12258			fp_validate_area_domain(port, page->aff_d_id, mask,
12259			    job, sleep);
12260			break;
12261
12262		case FC_RSCN_FABRIC_ADDRESS:
12263			/*
12264			 * We need to discover all the devices on this
12265			 * port.
12266			 */
12267			fp_validate_area_domain(port, 0, 0, job, sleep);
12268			break;
12269
12270		default:
12271			break;
12272		}
12273	}
12274	if (attr_list != NULL) {
12275	    (void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW,
12276		    EC_SUNFC, ESC_SUNFC_PORT_RSCN, attr_list,
12277		    NULL, DDI_SLEEP);
12278	    nvlist_free(attr_list);
12279	} else {
12280	    FP_TRACE(FP_NHEAD1(9, 0),
12281		"RSCN handled, but event not sent to userland");
12282	}
12283	if (nvname != NULL) {
12284	    kmem_free(nvname, RSCN_EVENT_NAME_LEN);
12285	}
12286
12287	if (ns_cmd) {
12288		fctl_free_ns_cmd(ns_cmd);
12289	}
12290
12291	if (listindex) {
12292#ifdef	DEBUG
12293		page = (fc_affected_id_t *)(buf->ub_buffer +
12294		    sizeof (fc_rscn_t));
12295
12296		if (listptr->map_did.port_id != page->aff_d_id) {
12297			FP_TRACE(FP_NHEAD1(9, 0),
12298			    "PORT RSCN: processed=%x, reporting=%x",
12299			    listptr->map_did.port_id, page->aff_d_id);
12300		}
12301#endif
12302
12303		(void) fp_ulp_devc_cb(port, listptr, listindex, count,
12304		    sleep, 0);
12305	} else {
12306		kmem_free(listptr, sizeof (fc_portmap_t) * count);
12307	}
12308}
12309
12310
12311/*
12312 * Fill out old map for ULPs with fp_mutex, fd_mutex and pd_mutex held
12313 */
12314static void
12315fp_fillout_old_map_held(fc_portmap_t *map, fc_remote_port_t *pd, uchar_t flag)
12316{
12317	int		is_switch;
12318	int		initiator;
12319	fc_local_port_t	*port;
12320
12321	port = pd->pd_port;
12322
12323	/* This function has the following bunch of assumptions */
12324	ASSERT(port != NULL);
12325	ASSERT(MUTEX_HELD(&port->fp_mutex));
12326	ASSERT(MUTEX_HELD(&pd->pd_remote_nodep->fd_mutex));
12327	ASSERT(MUTEX_HELD(&pd->pd_mutex));
12328
12329	pd->pd_state = PORT_DEVICE_INVALID;
12330	pd->pd_type = PORT_DEVICE_OLD;
12331	initiator = (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
12332	is_switch = FC_IS_TOP_SWITCH(port->fp_topology);
12333
12334	fctl_delist_did_table(port, pd);
12335	fctl_delist_pwwn_table(port, pd);
12336
12337	FP_TRACE(FP_NHEAD1(6, 0), "fp_fillout_old_map_held: port=%p, d_id=%x"
12338	    " removed the PD=%p from DID and PWWN tables",
12339	    port, pd->pd_port_id.port_id, pd);
12340
12341	if ((!flag) && port && initiator && is_switch) {
12342		(void) fctl_add_orphan_held(port, pd);
12343	}
12344	fctl_copy_portmap_held(map, pd);
12345	map->map_pd = pd;
12346}
12347
12348/*
12349 * Fill out old map for ULPs
12350 */
12351static void
12352fp_fillout_old_map(fc_portmap_t *map, fc_remote_port_t *pd, uchar_t flag)
12353{
12354	int		is_switch;
12355	int		initiator;
12356	fc_local_port_t	*port;
12357
12358	mutex_enter(&pd->pd_mutex);
12359	port = pd->pd_port;
12360	mutex_exit(&pd->pd_mutex);
12361
12362	mutex_enter(&port->fp_mutex);
12363	mutex_enter(&pd->pd_mutex);
12364
12365	pd->pd_state = PORT_DEVICE_INVALID;
12366	pd->pd_type = PORT_DEVICE_OLD;
12367	initiator = (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
12368	is_switch = FC_IS_TOP_SWITCH(port->fp_topology);
12369
12370	fctl_delist_did_table(port, pd);
12371	fctl_delist_pwwn_table(port, pd);
12372
12373	FP_TRACE(FP_NHEAD1(6, 0), "fp_fillout_old_map: port=%p, d_id=%x"
12374	    " removed the PD=%p from DID and PWWN tables",
12375	    port, pd->pd_port_id.port_id, pd);
12376
12377	mutex_exit(&pd->pd_mutex);
12378	mutex_exit(&port->fp_mutex);
12379
12380	ASSERT(port != NULL);
12381	if ((!flag) && port && initiator && is_switch) {
12382		(void) fctl_add_orphan(port, pd, KM_NOSLEEP);
12383	}
12384	fctl_copy_portmap(map, pd);
12385	map->map_pd = pd;
12386}
12387
12388
12389/*
12390 * Fillout Changed Map for ULPs
12391 */
12392static void
12393fp_fillout_changed_map(fc_portmap_t *map, fc_remote_port_t *pd,
12394    uint32_t *new_did, la_wwn_t *new_pwwn)
12395{
12396	ASSERT(MUTEX_HELD(&pd->pd_mutex));
12397
12398	pd->pd_type = PORT_DEVICE_CHANGED;
12399	if (new_did) {
12400		pd->pd_port_id.port_id = *new_did;
12401	}
12402	if (new_pwwn) {
12403		pd->pd_port_name = *new_pwwn;
12404	}
12405	mutex_exit(&pd->pd_mutex);
12406
12407	fctl_copy_portmap(map, pd);
12408
12409	mutex_enter(&pd->pd_mutex);
12410	pd->pd_type = PORT_DEVICE_NOCHANGE;
12411}
12412
12413
12414/*
12415 * Fillout New Name Server map
12416 */
12417static void
12418fp_fillout_new_nsmap(fc_local_port_t *port, ddi_acc_handle_t *handle,
12419    fc_portmap_t *port_map, ns_resp_gan_t *gan_resp, uint32_t d_id)
12420{
12421	ASSERT(!MUTEX_HELD(&port->fp_mutex));
12422
12423	if (handle) {
12424		ddi_rep_get8(*handle, (uint8_t *)&port_map->map_pwwn,
12425		    (uint8_t *)&gan_resp->gan_pwwn, sizeof (gan_resp->gan_pwwn),
12426		    DDI_DEV_AUTOINCR);
12427		ddi_rep_get8(*handle, (uint8_t *)&port_map->map_nwwn,
12428		    (uint8_t *)&gan_resp->gan_nwwn, sizeof (gan_resp->gan_nwwn),
12429		    DDI_DEV_AUTOINCR);
12430		ddi_rep_get8(*handle, (uint8_t *)port_map->map_fc4_types,
12431		    (uint8_t *)gan_resp->gan_fc4types,
12432		    sizeof (gan_resp->gan_fc4types), DDI_DEV_AUTOINCR);
12433	} else {
12434		bcopy(&gan_resp->gan_pwwn, &port_map->map_pwwn,
12435		    sizeof (gan_resp->gan_pwwn));
12436		bcopy(&gan_resp->gan_nwwn, &port_map->map_nwwn,
12437		    sizeof (gan_resp->gan_nwwn));
12438		bcopy(gan_resp->gan_fc4types, port_map->map_fc4_types,
12439		    sizeof (gan_resp->gan_fc4types));
12440	}
12441	port_map->map_did.port_id = d_id;
12442	port_map->map_did.priv_lilp_posit = 0;
12443	port_map->map_hard_addr.hard_addr = 0;
12444	port_map->map_hard_addr.rsvd = 0;
12445	port_map->map_state = PORT_DEVICE_INVALID;
12446	port_map->map_type = PORT_DEVICE_NEW;
12447	port_map->map_flags = 0;
12448	port_map->map_pd = NULL;
12449
12450	(void) fctl_remove_if_orphan(port, &port_map->map_pwwn);
12451
12452	ASSERT(port != NULL);
12453}
12454
12455
12456/*
12457 * Perform LINIT ELS
12458 */
12459static int
12460fp_remote_lip(fc_local_port_t *port, la_wwn_t *pwwn, int sleep,
12461    job_request_t *job)
12462{
12463	int			rval;
12464	uint32_t		d_id;
12465	uint32_t		s_id;
12466	uint32_t		lfa;
12467	uchar_t			class;
12468	uint32_t		ret;
12469	fp_cmd_t		*cmd;
12470	fc_porttype_t		ptype;
12471	fc_packet_t		*pkt;
12472	fc_linit_req_t		payload;
12473	fc_remote_port_t 	*pd;
12474
12475	rval = 0;
12476
12477	ASSERT(job != NULL);
12478	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
12479
12480	pd = fctl_get_remote_port_by_pwwn(port, pwwn);
12481	if (pd == NULL) {
12482		fctl_ns_req_t *ns_cmd;
12483
12484		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
12485		    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
12486		    0, sleep);
12487
12488		if (ns_cmd == NULL) {
12489			return (FC_NOMEM);
12490		}
12491		job->job_result = FC_SUCCESS;
12492		ns_cmd->ns_cmd_code = NS_GID_PN;
12493		((ns_req_gid_pn_t *)(ns_cmd->ns_cmd_buf))->pwwn = *pwwn;
12494
12495		ret = fp_ns_query(port, ns_cmd, job, 1, sleep);
12496		if (ret != FC_SUCCESS || job->job_result != FC_SUCCESS) {
12497			fctl_free_ns_cmd(ns_cmd);
12498			return (FC_FAILURE);
12499		}
12500		bcopy(ns_cmd->ns_data_buf, (caddr_t)&d_id, sizeof (d_id));
12501		d_id = BE_32(*((uint32_t *)ns_cmd->ns_data_buf));
12502
12503		fctl_free_ns_cmd(ns_cmd);
12504		lfa = d_id & 0xFFFF00;
12505
12506		/*
12507		 * Given this D_ID, get the port type to see if
12508		 * we can do LINIT on the LFA
12509		 */
12510		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gpt_id_t),
12511		    sizeof (ns_resp_gpt_id_t), sizeof (ns_resp_gpt_id_t),
12512		    0, sleep);
12513
12514		if (ns_cmd == NULL) {
12515			return (FC_NOMEM);
12516		}
12517
12518		job->job_result = FC_SUCCESS;
12519		ns_cmd->ns_cmd_code = NS_GPT_ID;
12520
12521		((ns_req_gpt_id_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = d_id;
12522		((ns_req_gpt_id_t *)
12523		    (ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
12524
12525		ret = fp_ns_query(port, ns_cmd, job, 1, sleep);
12526		if (ret != FC_SUCCESS || job->job_result != FC_SUCCESS) {
12527			fctl_free_ns_cmd(ns_cmd);
12528			return (FC_FAILURE);
12529		}
12530		bcopy(ns_cmd->ns_data_buf, (caddr_t)&ptype, sizeof (ptype));
12531
12532		fctl_free_ns_cmd(ns_cmd);
12533
12534		switch (ptype.port_type) {
12535		case FC_NS_PORT_NL:
12536		case FC_NS_PORT_F_NL:
12537		case FC_NS_PORT_FL:
12538			break;
12539
12540		default:
12541			return (FC_FAILURE);
12542		}
12543	} else {
12544		mutex_enter(&pd->pd_mutex);
12545		ptype = pd->pd_porttype;
12546
12547		switch (pd->pd_porttype.port_type) {
12548		case FC_NS_PORT_NL:
12549		case FC_NS_PORT_F_NL:
12550		case FC_NS_PORT_FL:
12551			lfa = pd->pd_port_id.port_id & 0xFFFF00;
12552			break;
12553
12554		default:
12555			mutex_exit(&pd->pd_mutex);
12556			return (FC_FAILURE);
12557		}
12558		mutex_exit(&pd->pd_mutex);
12559	}
12560
12561	mutex_enter(&port->fp_mutex);
12562	s_id = port->fp_port_id.port_id;
12563	class = port->fp_ns_login_class;
12564	mutex_exit(&port->fp_mutex);
12565
12566	cmd = fp_alloc_pkt(port, sizeof (fc_linit_req_t),
12567	    sizeof (fc_linit_resp_t), sleep, pd);
12568	if (cmd == NULL) {
12569		return (FC_NOMEM);
12570	}
12571
12572	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
12573	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
12574	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
12575	cmd->cmd_retry_count = fp_retry_count;
12576	cmd->cmd_ulp_pkt = NULL;
12577
12578	pkt = &cmd->cmd_pkt;
12579	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
12580
12581	fp_els_init(cmd, s_id, lfa, fp_linit_intr, job);
12582
12583	/*
12584	 * How does LIP work by the way ?
12585	 * 	If the L_Port receives three consecutive identical ordered
12586	 *	sets whose first two characters (fully decoded) are equal to
12587	 *	the values shown in Table 3 of FC-AL-2 then the L_Port shall
12588	 *	recognize a Loop Initialization Primitive sequence. The
12589	 *	character 3 determines the type of lip:
12590	 *		LIP(F7)		Normal LIP
12591	 *		LIP(F8)		Loop Failure LIP
12592	 *
12593	 * The possible combination for the 3rd and 4th bytes are:
12594	 *	F7, 	F7	Normal Lip 	- No valid AL_PA
12595	 *	F8, 	F8	Loop Failure	- No valid AL_PA
12596	 *	F7, 	AL_PS	Normal Lip	- Valid source AL_PA
12597	 *	F8, 	AL_PS	Loop Failure	- Valid source AL_PA
12598	 *	AL_PD	AL_PS	Loop reset of AL_PD originated by AL_PS
12599	 *			And Normal Lip for all other loop members
12600	 *	0xFF	AL_PS	Vendor specific reset of all loop members
12601	 *
12602	 * Now, it may not always be that we, at the source, may have an
12603	 * AL_PS (AL_PA of source) for 4th character slot, so we decide
12604	 * to do (Normal Lip, No Valid AL_PA), that means, in the LINIT
12605	 * payload we are going to set:
12606	 *	lip_b3 = 0xF7;		Normal LIP
12607	 *	lip_b4 = 0xF7;		No valid source AL_PA
12608	 */
12609	payload.ls_code.ls_code = LA_ELS_LINIT;
12610	payload.ls_code.mbz = 0;
12611	payload.rsvd = 0;
12612	payload.func = 0;		/* Let Fabric determine the best way */
12613	payload.lip_b3 = 0xF7;		/* Normal LIP */
12614	payload.lip_b4 = 0xF7;		/* No valid source AL_PA */
12615
12616	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
12617	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
12618
12619	job->job_counter = 1;
12620
12621	ret = fp_sendcmd(port, cmd, port->fp_fca_handle);
12622	if (ret == FC_SUCCESS) {
12623		fp_jobwait(job);
12624		rval = job->job_result;
12625	} else {
12626		rval = FC_FAILURE;
12627		fp_free_pkt(cmd);
12628	}
12629
12630	return (rval);
12631}
12632
12633
12634/*
12635 * Fill out the device handles with GAN response
12636 */
12637static void
12638fp_stuff_device_with_gan(ddi_acc_handle_t *handle, fc_remote_port_t *pd,
12639    ns_resp_gan_t *gan_resp)
12640{
12641	fc_remote_node_t 	*node;
12642	fc_porttype_t		type;
12643	fc_local_port_t		*port;
12644
12645	ASSERT(pd != NULL);
12646	ASSERT(handle != NULL);
12647
12648	port = pd->pd_port;
12649
12650	FP_TRACE(FP_NHEAD1(1, 0), "GAN PD stuffing; pd=%p,"
12651	    " port_id=%x, sym_len=%d fc4-type=%x",
12652	    pd, gan_resp->gan_type_id.rsvd,
12653	    gan_resp->gan_spnlen, gan_resp->gan_fc4types[0]);
12654
12655	mutex_enter(&pd->pd_mutex);
12656
12657	ddi_rep_get8(*handle, (uint8_t *)&type,
12658	    (uint8_t *)&gan_resp->gan_type_id, sizeof (type), DDI_DEV_AUTOINCR);
12659
12660	pd->pd_porttype.port_type = type.port_type;
12661	pd->pd_porttype.rsvd = 0;
12662
12663	pd->pd_spn_len = gan_resp->gan_spnlen;
12664	if (pd->pd_spn_len) {
12665		ddi_rep_get8(*handle, (uint8_t *)pd->pd_spn,
12666		    (uint8_t *)gan_resp->gan_spname, pd->pd_spn_len,
12667		    DDI_DEV_AUTOINCR);
12668	}
12669
12670	ddi_rep_get8(*handle, (uint8_t *)pd->pd_ip_addr,
12671	    (uint8_t *)gan_resp->gan_ip, sizeof (pd->pd_ip_addr),
12672	    DDI_DEV_AUTOINCR);
12673	ddi_rep_get8(*handle, (uint8_t *)&pd->pd_cos,
12674	    (uint8_t *)&gan_resp->gan_cos, sizeof (pd->pd_cos),
12675	    DDI_DEV_AUTOINCR);
12676	ddi_rep_get8(*handle, (uint8_t *)pd->pd_fc4types,
12677	    (uint8_t *)gan_resp->gan_fc4types, sizeof (pd->pd_fc4types),
12678	    DDI_DEV_AUTOINCR);
12679
12680	node = pd->pd_remote_nodep;
12681	mutex_exit(&pd->pd_mutex);
12682
12683	mutex_enter(&node->fd_mutex);
12684
12685	ddi_rep_get8(*handle, (uint8_t *)node->fd_ipa,
12686	    (uint8_t *)gan_resp->gan_ipa, sizeof (node->fd_ipa),
12687	    DDI_DEV_AUTOINCR);
12688
12689	node->fd_snn_len = gan_resp->gan_snnlen;
12690	if (node->fd_snn_len) {
12691		ddi_rep_get8(*handle, (uint8_t *)node->fd_snn,
12692		    (uint8_t *)gan_resp->gan_snname, node->fd_snn_len,
12693		    DDI_DEV_AUTOINCR);
12694	}
12695
12696	mutex_exit(&node->fd_mutex);
12697}
12698
12699
12700/*
12701 * Handles all NS Queries (also means that this function
12702 * doesn't handle NS object registration)
12703 */
12704static int
12705fp_ns_query(fc_local_port_t *port, fctl_ns_req_t *ns_cmd, job_request_t *job,
12706    int polled, int sleep)
12707{
12708	int 		rval;
12709	fp_cmd_t 	*cmd;
12710
12711	ASSERT(!MUTEX_HELD(&port->fp_mutex));
12712
12713	if (ns_cmd->ns_cmd_size == 0) {
12714		return (FC_FAILURE);
12715	}
12716
12717	cmd = fp_alloc_pkt(port, sizeof (fc_ct_header_t) +
12718	    ns_cmd->ns_cmd_size, sizeof (fc_ct_header_t) +
12719	    ns_cmd->ns_resp_size, sleep, NULL);
12720	if (cmd == NULL) {
12721		return (FC_NOMEM);
12722	}
12723
12724	fp_ct_init(port, cmd, ns_cmd, ns_cmd->ns_cmd_code, ns_cmd->ns_cmd_buf,
12725	    ns_cmd->ns_cmd_size, ns_cmd->ns_resp_size, job);
12726
12727	if (polled) {
12728		job->job_counter = 1;
12729		ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
12730	}
12731	rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
12732	if (rval != FC_SUCCESS) {
12733		job->job_result = rval;
12734		fp_iodone(cmd);
12735		if (polled == 0) {
12736			/*
12737			 * Return FC_SUCCESS to indicate that
12738			 * fp_iodone is performed already.
12739			 */
12740			rval = FC_SUCCESS;
12741		}
12742	}
12743
12744	if (polled) {
12745		fp_jobwait(job);
12746		rval = job->job_result;
12747	}
12748
12749	return (rval);
12750}
12751
12752
12753/*
12754 * Initialize Common Transport request
12755 */
12756static void
12757fp_ct_init(fc_local_port_t *port, fp_cmd_t *cmd, fctl_ns_req_t *ns_cmd,
12758    uint16_t cmd_code, caddr_t cmd_buf, uint16_t cmd_len,
12759    uint16_t resp_len, job_request_t *job)
12760{
12761	uint32_t	s_id;
12762	uchar_t		class;
12763	fc_packet_t 	*pkt;
12764	fc_ct_header_t 	ct;
12765
12766	ASSERT(!MUTEX_HELD(&port->fp_mutex));
12767
12768	mutex_enter(&port->fp_mutex);
12769	s_id = port->fp_port_id.port_id;
12770	class = port->fp_ns_login_class;
12771	mutex_exit(&port->fp_mutex);
12772
12773	cmd->cmd_job = job;
12774	cmd->cmd_private = ns_cmd;
12775	pkt = &cmd->cmd_pkt;
12776
12777	ct.ct_rev = CT_REV;
12778	ct.ct_inid = 0;
12779	ct.ct_fcstype = FCSTYPE_DIRECTORY;
12780	ct.ct_fcssubtype = FCSSUB_DS_NAME_SERVER;
12781	ct.ct_options = 0;
12782	ct.ct_reserved1 = 0;
12783	ct.ct_cmdrsp = cmd_code;
12784	ct.ct_aiusize = resp_len >> 2;
12785	ct.ct_reserved2 = 0;
12786	ct.ct_reason = 0;
12787	ct.ct_expln = 0;
12788	ct.ct_vendor = 0;
12789
12790	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&ct, (uint8_t *)pkt->pkt_cmd,
12791	    sizeof (ct), DDI_DEV_AUTOINCR);
12792
12793	pkt->pkt_cmd_fhdr.r_ctl = R_CTL_UNSOL_CONTROL;
12794	pkt->pkt_cmd_fhdr.d_id = 0xFFFFFC;
12795	pkt->pkt_cmd_fhdr.s_id = s_id;
12796	pkt->pkt_cmd_fhdr.type = FC_TYPE_FC_SERVICES;
12797	pkt->pkt_cmd_fhdr.f_ctl = F_CTL_SEQ_INITIATIVE |
12798	    F_CTL_FIRST_SEQ | F_CTL_END_SEQ;
12799	pkt->pkt_cmd_fhdr.seq_id = 0;
12800	pkt->pkt_cmd_fhdr.df_ctl  = 0;
12801	pkt->pkt_cmd_fhdr.seq_cnt = 0;
12802	pkt->pkt_cmd_fhdr.ox_id = 0xffff;
12803	pkt->pkt_cmd_fhdr.rx_id = 0xffff;
12804	pkt->pkt_cmd_fhdr.ro = 0;
12805	pkt->pkt_cmd_fhdr.rsvd = 0;
12806
12807	pkt->pkt_comp = fp_ns_intr;
12808	pkt->pkt_ulp_private = (opaque_t)cmd;
12809	pkt->pkt_timeout = FP_NS_TIMEOUT;
12810
12811	if (cmd_buf) {
12812		ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)cmd_buf,
12813		    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
12814		    cmd_len, DDI_DEV_AUTOINCR);
12815	}
12816
12817	cmd->cmd_transport = port->fp_fca_tran->fca_transport;
12818
12819	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | class;
12820	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
12821	cmd->cmd_flags = FP_CMD_PLOGI_DONT_CARE;
12822	cmd->cmd_retry_count = fp_retry_count;
12823	cmd->cmd_ulp_pkt = NULL;
12824}
12825
12826
12827/*
12828 * Name Server request interrupt routine
12829 */
12830static void
12831fp_ns_intr(fc_packet_t *pkt)
12832{
12833	fp_cmd_t	*cmd;
12834	fc_local_port_t	*port;
12835	fc_ct_header_t 	resp_hdr;
12836	fc_ct_header_t 	cmd_hdr;
12837	fctl_ns_req_t	*ns_cmd;
12838
12839	cmd = pkt->pkt_ulp_private;
12840	port = cmd->cmd_port;
12841
12842	ddi_rep_get8(pkt->pkt_cmd_acc, (uint8_t *)&cmd_hdr,
12843	    (uint8_t *)pkt->pkt_cmd, sizeof (cmd_hdr), DDI_DEV_AUTOINCR);
12844
12845	ns_cmd = (fctl_ns_req_t *)
12846	    (((fp_cmd_t *)(pkt->pkt_ulp_private))->cmd_private);
12847
12848	if (!FP_IS_PKT_ERROR(pkt)) {
12849		ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&resp_hdr,
12850		    (uint8_t *)pkt->pkt_resp, sizeof (resp_hdr),
12851		    DDI_DEV_AUTOINCR);
12852
12853		/*
12854		 * On x86 architectures, make sure the resp_hdr is big endian.
12855		 * This macro is a NOP on sparc architectures mainly because
12856		 * we don't want to end up wasting time since the end result
12857		 * is going to be the same.
12858		 */
12859		MAKE_BE_32(&resp_hdr);
12860
12861		if (ns_cmd) {
12862			/*
12863			 * Always copy out the response CT_HDR
12864			 */
12865			bcopy(&resp_hdr, &ns_cmd->ns_resp_hdr,
12866			    sizeof (resp_hdr));
12867		}
12868
12869		if (resp_hdr.ct_cmdrsp == FS_RJT_IU) {
12870			pkt->pkt_state = FC_PKT_FS_RJT;
12871			pkt->pkt_reason = resp_hdr.ct_reason;
12872			pkt->pkt_expln = resp_hdr.ct_expln;
12873		}
12874	}
12875
12876	if (FP_IS_PKT_ERROR(pkt)) {
12877		if (ns_cmd) {
12878			if (ns_cmd->ns_flags & FCTL_NS_VALIDATE_PD) {
12879				ASSERT(ns_cmd->ns_pd != NULL);
12880
12881				/* Mark it OLD if not already done */
12882				mutex_enter(&ns_cmd->ns_pd->pd_mutex);
12883				ns_cmd->ns_pd->pd_type = PORT_DEVICE_OLD;
12884				mutex_exit(&ns_cmd->ns_pd->pd_mutex);
12885			}
12886
12887			if (ns_cmd->ns_flags & FCTL_NS_ASYNC_REQUEST) {
12888				fctl_free_ns_cmd(ns_cmd);
12889				((fp_cmd_t *)
12890				    (pkt->pkt_ulp_private))->cmd_private = NULL;
12891			}
12892
12893		}
12894
12895		FP_TRACE(FP_NHEAD1(4, 0), "NS failure; pkt state=%x reason=%x",
12896		    pkt->pkt_state, pkt->pkt_reason);
12897
12898		(void) fp_common_intr(pkt, 1);
12899
12900		return;
12901	}
12902
12903	if (resp_hdr.ct_cmdrsp != FS_ACC_IU) {
12904		uint32_t	d_id;
12905		fc_local_port_t	*port;
12906		fp_cmd_t	*cmd;
12907
12908		d_id = pkt->pkt_cmd_fhdr.d_id;
12909		cmd = pkt->pkt_ulp_private;
12910		port = cmd->cmd_port;
12911		FP_TRACE(FP_NHEAD2(9, 0),
12912		    "Bogus NS response received for D_ID=%x", d_id);
12913	}
12914
12915	if (cmd_hdr.ct_cmdrsp == NS_GA_NXT) {
12916		fp_gan_handler(pkt, ns_cmd);
12917		return;
12918	}
12919
12920	if (cmd_hdr.ct_cmdrsp >= NS_GPN_ID &&
12921	    cmd_hdr.ct_cmdrsp <= NS_GID_PT) {
12922		if (ns_cmd) {
12923			if ((ns_cmd->ns_flags & FCTL_NS_NO_DATA_BUF) == 0) {
12924				fp_ns_query_handler(pkt, ns_cmd);
12925				return;
12926			}
12927		}
12928	}
12929
12930	fp_iodone(pkt->pkt_ulp_private);
12931}
12932
12933
12934/*
12935 * Process NS_GAN response
12936 */
12937static void
12938fp_gan_handler(fc_packet_t *pkt, fctl_ns_req_t *ns_cmd)
12939{
12940	int			my_did;
12941	fc_portid_t		d_id;
12942	fp_cmd_t		*cmd;
12943	fc_local_port_t		*port;
12944	fc_remote_port_t	*pd;
12945	ns_req_gan_t		gan_req;
12946	ns_resp_gan_t		*gan_resp;
12947
12948	ASSERT(ns_cmd != NULL);
12949
12950	cmd = pkt->pkt_ulp_private;
12951	port = cmd->cmd_port;
12952
12953	gan_resp = (ns_resp_gan_t *)(pkt->pkt_resp + sizeof (fc_ct_header_t));
12954
12955	ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&d_id,
12956	    (uint8_t *)&gan_resp->gan_type_id, sizeof (d_id), DDI_DEV_AUTOINCR);
12957
12958	*(uint32_t *)&d_id = BE_32(*(uint32_t *)&d_id);
12959
12960	/*
12961	 * In this case the priv_lilp_posit field  in reality
12962	 * is actually represents the relative position on a private loop.
12963	 * So zero it while dealing with Port Identifiers.
12964	 */
12965	d_id.priv_lilp_posit = 0;
12966	pd = fctl_get_remote_port_by_did(port, d_id.port_id);
12967	if (ns_cmd->ns_gan_sid == d_id.port_id) {
12968		/*
12969		 * We've come a full circle; time to get out.
12970		 */
12971		fp_iodone(cmd);
12972		return;
12973	}
12974
12975	if (ns_cmd->ns_gan_sid == FCTL_GAN_START_ID) {
12976		ns_cmd->ns_gan_sid = d_id.port_id;
12977	}
12978
12979	mutex_enter(&port->fp_mutex);
12980	my_did = (d_id.port_id == port->fp_port_id.port_id) ? 1 : 0;
12981	mutex_exit(&port->fp_mutex);
12982
12983	FP_TRACE(FP_NHEAD1(1, 0), "GAN response; port=%p, d_id=%x", port,
12984	    d_id.port_id);
12985
12986	if (my_did == 0) {
12987		la_wwn_t pwwn;
12988		la_wwn_t nwwn;
12989
12990		FP_TRACE(FP_NHEAD1(1, 0), "GAN response details; "
12991		    "port=%p, d_id=%x, type_id=%x, "
12992		    "pwwn=%x %x %x %x %x %x %x %x, "
12993		    "nwwn=%x %x %x %x %x %x %x %x",
12994		    port, d_id.port_id, gan_resp->gan_type_id,
12995
12996		    gan_resp->gan_pwwn.raw_wwn[0],
12997		    gan_resp->gan_pwwn.raw_wwn[1],
12998		    gan_resp->gan_pwwn.raw_wwn[2],
12999		    gan_resp->gan_pwwn.raw_wwn[3],
13000		    gan_resp->gan_pwwn.raw_wwn[4],
13001		    gan_resp->gan_pwwn.raw_wwn[5],
13002		    gan_resp->gan_pwwn.raw_wwn[6],
13003		    gan_resp->gan_pwwn.raw_wwn[7],
13004
13005		    gan_resp->gan_nwwn.raw_wwn[0],
13006		    gan_resp->gan_nwwn.raw_wwn[1],
13007		    gan_resp->gan_nwwn.raw_wwn[2],
13008		    gan_resp->gan_nwwn.raw_wwn[3],
13009		    gan_resp->gan_nwwn.raw_wwn[4],
13010		    gan_resp->gan_nwwn.raw_wwn[5],
13011		    gan_resp->gan_nwwn.raw_wwn[6],
13012		    gan_resp->gan_nwwn.raw_wwn[7]);
13013
13014		ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&nwwn,
13015		    (uint8_t *)&gan_resp->gan_nwwn, sizeof (nwwn),
13016		    DDI_DEV_AUTOINCR);
13017
13018		ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)&pwwn,
13019		    (uint8_t *)&gan_resp->gan_pwwn, sizeof (pwwn),
13020		    DDI_DEV_AUTOINCR);
13021
13022		if (ns_cmd->ns_flags & FCTL_NS_CREATE_DEVICE && pd == NULL) {
13023			pd = fctl_create_remote_port(port, &nwwn, &pwwn,
13024			    d_id.port_id, PD_PLOGI_INITIATOR, KM_NOSLEEP);
13025		}
13026		if (pd != NULL) {
13027			fp_stuff_device_with_gan(&pkt->pkt_resp_acc,
13028			    pd, gan_resp);
13029		}
13030
13031		if (ns_cmd->ns_flags & FCTL_NS_GET_DEV_COUNT) {
13032			*((int *)ns_cmd->ns_data_buf) += 1;
13033		}
13034
13035		if (ns_cmd->ns_flags & FCTL_NS_FILL_NS_MAP) {
13036			ASSERT((ns_cmd->ns_flags & FCTL_NS_NO_DATA_BUF) == 0);
13037
13038			if (ns_cmd->ns_flags & FCTL_NS_BUF_IS_USERLAND) {
13039				fc_port_dev_t *userbuf;
13040
13041				userbuf = ((fc_port_dev_t *)
13042				    ns_cmd->ns_data_buf) +
13043				    ns_cmd->ns_gan_index++;
13044
13045				userbuf->dev_did = d_id;
13046
13047				ddi_rep_get8(pkt->pkt_resp_acc,
13048				    (uint8_t *)userbuf->dev_type,
13049				    (uint8_t *)gan_resp->gan_fc4types,
13050				    sizeof (userbuf->dev_type),
13051				    DDI_DEV_AUTOINCR);
13052
13053				userbuf->dev_nwwn = nwwn;
13054				userbuf->dev_pwwn = pwwn;
13055
13056				if (pd != NULL) {
13057					mutex_enter(&pd->pd_mutex);
13058					userbuf->dev_state = pd->pd_state;
13059					userbuf->dev_hard_addr =
13060					    pd->pd_hard_addr;
13061					mutex_exit(&pd->pd_mutex);
13062				} else {
13063					userbuf->dev_state =
13064					    PORT_DEVICE_INVALID;
13065				}
13066			} else if (ns_cmd->ns_flags &
13067			    FCTL_NS_BUF_IS_FC_PORTMAP) {
13068				fc_portmap_t *map;
13069
13070				map = ((fc_portmap_t *)
13071				    ns_cmd->ns_data_buf) +
13072				    ns_cmd->ns_gan_index++;
13073
13074				/*
13075				 * First fill it like any new map
13076				 * and update the port device info
13077				 * below.
13078				 */
13079				fp_fillout_new_nsmap(port, &pkt->pkt_resp_acc,
13080				    map, gan_resp, d_id.port_id);
13081				if (pd != NULL) {
13082					fctl_copy_portmap(map, pd);
13083				} else {
13084					map->map_state = PORT_DEVICE_INVALID;
13085					map->map_type = PORT_DEVICE_NOCHANGE;
13086				}
13087			} else {
13088				caddr_t dst_ptr;
13089
13090				dst_ptr = ns_cmd->ns_data_buf +
13091				    (NS_GAN_RESP_LEN) * ns_cmd->ns_gan_index++;
13092
13093				ddi_rep_get8(pkt->pkt_resp_acc,
13094				    (uint8_t *)dst_ptr, (uint8_t *)gan_resp,
13095				    NS_GAN_RESP_LEN, DDI_DEV_AUTOINCR);
13096			}
13097		} else {
13098			ns_cmd->ns_gan_index++;
13099		}
13100		if (ns_cmd->ns_gan_index >= ns_cmd->ns_gan_max) {
13101			fp_iodone(cmd);
13102			return;
13103		}
13104	}
13105
13106	gan_req.pid = d_id;
13107
13108	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&gan_req,
13109	    (uint8_t *)(pkt->pkt_cmd + sizeof (fc_ct_header_t)),
13110	    sizeof (gan_req), DDI_DEV_AUTOINCR);
13111
13112	if (cmd->cmd_transport(port->fp_fca_handle, pkt) != FC_SUCCESS) {
13113		pkt->pkt_state = FC_PKT_TRAN_ERROR;
13114		fp_iodone(cmd);
13115	}
13116}
13117
13118
13119/*
13120 * Handle NS Query interrupt
13121 */
13122static void
13123fp_ns_query_handler(fc_packet_t *pkt, fctl_ns_req_t *ns_cmd)
13124{
13125	fp_cmd_t 	*cmd;
13126	fc_local_port_t	*port;
13127	caddr_t 	src_ptr;
13128	uint32_t 	xfer_len;
13129
13130	cmd = pkt->pkt_ulp_private;
13131	port = cmd->cmd_port;
13132
13133	xfer_len = ns_cmd->ns_resp_size;
13134
13135	FP_TRACE(FP_NHEAD1(1, 0), "NS Query response, cmd_code=%x, xfer_len=%x",
13136	    ns_cmd->ns_cmd_code, xfer_len);
13137
13138	if (ns_cmd->ns_cmd_code == NS_GPN_ID) {
13139		src_ptr = (caddr_t)pkt->pkt_resp + sizeof (fc_ct_header_t);
13140
13141		FP_TRACE(FP_NHEAD1(6, 0), "GPN_ID results; %x %x %x %x %x",
13142		    src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3], src_ptr[4]);
13143	}
13144
13145	if (xfer_len <= ns_cmd->ns_data_len) {
13146		src_ptr = (caddr_t)pkt->pkt_resp + sizeof (fc_ct_header_t);
13147		ddi_rep_get8(pkt->pkt_resp_acc, (uint8_t *)ns_cmd->ns_data_buf,
13148		    (uint8_t *)src_ptr, xfer_len, DDI_DEV_AUTOINCR);
13149	}
13150
13151	if (ns_cmd->ns_flags & FCTL_NS_VALIDATE_PD) {
13152		ASSERT(ns_cmd->ns_pd != NULL);
13153
13154		mutex_enter(&ns_cmd->ns_pd->pd_mutex);
13155		if (ns_cmd->ns_pd->pd_type == PORT_DEVICE_OLD) {
13156			ns_cmd->ns_pd->pd_type = PORT_DEVICE_NOCHANGE;
13157		}
13158		mutex_exit(&ns_cmd->ns_pd->pd_mutex);
13159	}
13160
13161	if (ns_cmd->ns_flags & FCTL_NS_ASYNC_REQUEST) {
13162		fctl_free_ns_cmd(ns_cmd);
13163		((fp_cmd_t *)(pkt->pkt_ulp_private))->cmd_private = NULL;
13164	}
13165	fp_iodone(cmd);
13166}
13167
13168
13169/*
13170 * Handle unsolicited ADISC ELS request
13171 */
13172static void
13173fp_handle_unsol_adisc(fc_local_port_t *port, fc_unsol_buf_t *buf,
13174    fc_remote_port_t *pd, job_request_t *job)
13175{
13176	int		rval;
13177	fp_cmd_t	*cmd;
13178
13179	FP_TRACE(FP_NHEAD1(5, 0), "ADISC; port=%p, D_ID=%x state=%x, pd=%p",
13180	    port, pd->pd_port_id.port_id, pd->pd_state, pd);
13181	mutex_enter(&pd->pd_mutex);
13182	if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
13183		mutex_exit(&pd->pd_mutex);
13184		if (FP_IS_CLASS_1_OR_2(buf->ub_class)) {
13185			cmd = fp_alloc_pkt(port, sizeof (la_els_rjt_t),
13186			    0, KM_SLEEP, pd);
13187			if (cmd != NULL) {
13188				fp_els_rjt_init(port, cmd, buf,
13189				    FC_ACTION_NON_RETRYABLE,
13190				    FC_REASON_INVALID_LINK_CTRL, job);
13191
13192				if (fp_sendcmd(port, cmd,
13193				    port->fp_fca_handle) != FC_SUCCESS) {
13194					fp_free_pkt(cmd);
13195				}
13196			}
13197		}
13198	} else {
13199		mutex_exit(&pd->pd_mutex);
13200		/*
13201		 * Yes, yes, we don't have a hard address. But we
13202		 * we should still respond. Huh ? Visit 21.19.2
13203		 * of FC-PH-2 which essentially says that if an
13204		 * NL_Port doesn't have a hard address, or if a port
13205		 * does not have FC-AL capability, it shall report
13206		 * zeroes in this field.
13207		 */
13208		cmd = fp_alloc_pkt(port, sizeof (la_els_adisc_t),
13209		    0, KM_SLEEP, pd);
13210		if (cmd == NULL) {
13211			return;
13212		}
13213		fp_adisc_acc_init(port, cmd, buf, job);
13214		rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
13215		if (rval != FC_SUCCESS) {
13216			fp_free_pkt(cmd);
13217		}
13218	}
13219}
13220
13221
13222/*
13223 * Initialize ADISC response.
13224 */
13225static void
13226fp_adisc_acc_init(fc_local_port_t *port, fp_cmd_t *cmd, fc_unsol_buf_t *buf,
13227    job_request_t *job)
13228{
13229	fc_packet_t	*pkt;
13230	la_els_adisc_t	payload;
13231
13232	cmd->cmd_pkt.pkt_tran_flags = buf->ub_class;
13233	cmd->cmd_pkt.pkt_tran_type = FC_PKT_OUTBOUND;
13234	cmd->cmd_flags = FP_CMD_CFLAG_UNDEFINED;
13235	cmd->cmd_retry_count = 1;
13236	cmd->cmd_ulp_pkt = NULL;
13237
13238	cmd->cmd_transport = port->fp_fca_tran->fca_els_send;
13239	cmd->cmd_job = job;
13240
13241	pkt = &cmd->cmd_pkt;
13242
13243	fp_unsol_resp_init(pkt, buf, R_CTL_ELS_RSP, FC_TYPE_EXTENDED_LS);
13244
13245	payload.ls_code.ls_code = LA_ELS_ACC;
13246	payload.ls_code.mbz = 0;
13247
13248	mutex_enter(&port->fp_mutex);
13249	payload.nport_id = port->fp_port_id;
13250	payload.hard_addr = port->fp_hard_addr;
13251	mutex_exit(&port->fp_mutex);
13252
13253	payload.port_wwn = port->fp_service_params.nport_ww_name;
13254	payload.node_wwn = port->fp_service_params.node_ww_name;
13255
13256	ddi_rep_put8(pkt->pkt_cmd_acc, (uint8_t *)&payload,
13257	    (uint8_t *)pkt->pkt_cmd, sizeof (payload), DDI_DEV_AUTOINCR);
13258}
13259
13260
13261/*
13262 * Hold and Install the requested ULP drivers
13263 */
13264static void
13265fp_load_ulp_modules(dev_info_t *dip, fc_local_port_t *port)
13266{
13267	int		len;
13268	int		count;
13269	int		data_len;
13270	major_t		ulp_major;
13271	caddr_t		ulp_name;
13272	caddr_t		data_ptr;
13273	caddr_t		data_buf;
13274
13275	ASSERT(!MUTEX_HELD(&port->fp_mutex));
13276
13277	data_buf = NULL;
13278	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
13279	    DDI_PROP_DONTPASS, "load-ulp-list",
13280	    (caddr_t)&data_buf, &data_len) != DDI_PROP_SUCCESS) {
13281		return;
13282	}
13283
13284	len = strlen(data_buf);
13285	port->fp_ulp_nload = fctl_atoi(data_buf, 10);
13286
13287	data_ptr = data_buf + len + 1;
13288	for (count = 0; count < port->fp_ulp_nload; count++) {
13289		len = strlen(data_ptr) + 1;
13290		ulp_name = kmem_zalloc(len, KM_SLEEP);
13291		bcopy(data_ptr, ulp_name, len);
13292
13293		ulp_major = ddi_name_to_major(ulp_name);
13294
13295		if (ulp_major != (major_t)-1) {
13296			if (modload("drv", ulp_name) < 0) {
13297				fp_printf(port, CE_NOTE, FP_LOG_ONLY,
13298				    0, NULL, "failed to load %s",
13299				    ulp_name);
13300			}
13301		} else {
13302			fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
13303			    "%s isn't a valid driver", ulp_name);
13304		}
13305
13306		kmem_free(ulp_name, len);
13307		data_ptr += len;	/* Skip to next field */
13308	}
13309
13310	/*
13311	 * Free the memory allocated by DDI
13312	 */
13313	if (data_buf != NULL) {
13314		kmem_free(data_buf, data_len);
13315	}
13316}
13317
13318
13319/*
13320 * Perform LOGO operation
13321 */
13322static int
13323fp_logout(fc_local_port_t *port, fc_remote_port_t *pd, job_request_t *job)
13324{
13325	int		rval;
13326	fp_cmd_t 	*cmd;
13327
13328	ASSERT(!MUTEX_HELD(&port->fp_mutex));
13329	ASSERT(!MUTEX_HELD(&pd->pd_mutex));
13330
13331	cmd = fp_alloc_pkt(port, sizeof (la_els_logo_t),
13332	    FP_PORT_IDENTIFIER_LEN, KM_SLEEP, pd);
13333
13334	mutex_enter(&port->fp_mutex);
13335	mutex_enter(&pd->pd_mutex);
13336
13337	ASSERT(pd->pd_state == PORT_DEVICE_LOGGED_IN);
13338	ASSERT(pd->pd_login_count == 1);
13339
13340	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
13341	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
13342	cmd->cmd_flags = 0;
13343	cmd->cmd_retry_count = 1;
13344	cmd->cmd_ulp_pkt = NULL;
13345
13346	fp_logo_init(pd, cmd, job);
13347
13348	mutex_exit(&pd->pd_mutex);
13349	mutex_exit(&port->fp_mutex);
13350
13351	rval = fp_sendcmd(port, cmd, port->fp_fca_handle);
13352	if (rval != FC_SUCCESS) {
13353		fp_iodone(cmd);
13354	}
13355
13356	return (rval);
13357}
13358
13359
13360/*
13361 * Perform Port attach callbacks to registered ULPs
13362 */
13363static void
13364fp_attach_ulps(fc_local_port_t *port, fc_attach_cmd_t cmd)
13365{
13366	fp_soft_attach_t *att;
13367
13368	att = kmem_zalloc(sizeof (*att), KM_SLEEP);
13369	att->att_cmd = cmd;
13370	att->att_port = port;
13371
13372	/*
13373	 * We need to remember whether or not fctl_busy_port
13374	 * succeeded so we know whether or not to call
13375	 * fctl_idle_port when the task is complete.
13376	 */
13377
13378	if (fctl_busy_port(port) == 0) {
13379		att->att_need_pm_idle = B_TRUE;
13380	} else {
13381		att->att_need_pm_idle = B_FALSE;
13382	}
13383
13384	(void) taskq_dispatch(port->fp_taskq, fp_ulp_port_attach,
13385	    att, KM_SLEEP);
13386}
13387
13388
13389/*
13390 * Forward state change notifications on to interested ULPs.
13391 * Spawns a call to fctl_ulp_statec_cb() in a taskq thread to do all the
13392 * real work.
13393 */
13394static int
13395fp_ulp_notify(fc_local_port_t *port, uint32_t statec, int sleep)
13396{
13397	fc_port_clist_t *clist;
13398
13399	clist = kmem_zalloc(sizeof (*clist), sleep);
13400	if (clist == NULL) {
13401		return (FC_NOMEM);
13402	}
13403
13404	clist->clist_state = statec;
13405
13406	mutex_enter(&port->fp_mutex);
13407	clist->clist_flags = port->fp_topology;
13408	mutex_exit(&port->fp_mutex);
13409
13410	clist->clist_port = (opaque_t)port;
13411	clist->clist_len = 0;
13412	clist->clist_size = 0;
13413	clist->clist_map = NULL;
13414
13415	(void) taskq_dispatch(port->fp_taskq, fctl_ulp_statec_cb,
13416	    clist, KM_SLEEP);
13417
13418	return (FC_SUCCESS);
13419}
13420
13421
13422/*
13423 * Get name server map
13424 */
13425static int
13426fp_ns_getmap(fc_local_port_t *port, job_request_t *job, fc_portmap_t **map,
13427    uint32_t *len, uint32_t sid)
13428{
13429	int ret;
13430	fctl_ns_req_t *ns_cmd;
13431
13432	/*
13433	 * Don't let the allocator do anything for response;
13434	 * we have have buffer ready to fillout.
13435	 */
13436	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
13437	    sizeof (ns_resp_gan_t), 0, (FCTL_NS_FILL_NS_MAP |
13438	    FCTL_NS_BUF_IS_FC_PORTMAP), KM_SLEEP);
13439
13440	ns_cmd->ns_data_len = sizeof (**map) * (*len);
13441	ns_cmd->ns_data_buf = (caddr_t)*map;
13442
13443	ASSERT(ns_cmd != NULL);
13444
13445	ns_cmd->ns_gan_index = 0;
13446	ns_cmd->ns_gan_sid = sid;
13447	ns_cmd->ns_cmd_code = NS_GA_NXT;
13448	ns_cmd->ns_gan_max = *len;
13449
13450	ret = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
13451
13452	if (ns_cmd->ns_gan_index != *len) {
13453		*len = ns_cmd->ns_gan_index;
13454	}
13455	ns_cmd->ns_data_len = 0;
13456	ns_cmd->ns_data_buf = NULL;
13457	fctl_free_ns_cmd(ns_cmd);
13458
13459	return (ret);
13460}
13461
13462
13463/*
13464 * Create a remote port in Fabric topology by using NS services
13465 */
13466static fc_remote_port_t *
13467fp_create_remote_port_by_ns(fc_local_port_t *port, uint32_t d_id, int sleep)
13468{
13469	int			rval;
13470	job_request_t 		*job;
13471	fctl_ns_req_t 		*ns_cmd;
13472	fc_remote_port_t	*pd;
13473
13474	ASSERT(!MUTEX_HELD(&port->fp_mutex));
13475
13476	FP_TRACE(FP_NHEAD1(1, 0), "PD creation begin; port=%p, d_id=%x",
13477	    port, d_id);
13478
13479#ifdef	DEBUG
13480	mutex_enter(&port->fp_mutex);
13481	ASSERT(FC_IS_TOP_SWITCH(port->fp_topology));
13482	mutex_exit(&port->fp_mutex);
13483#endif
13484
13485	job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, (opaque_t)port, sleep);
13486	if (job == NULL) {
13487		return (NULL);
13488	}
13489
13490	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
13491	    sizeof (ns_resp_gan_t), 0, (FCTL_NS_CREATE_DEVICE |
13492	    FCTL_NS_NO_DATA_BUF), sleep);
13493	if (ns_cmd == NULL) {
13494		return (NULL);
13495	}
13496
13497	job->job_result = FC_SUCCESS;
13498	ns_cmd->ns_gan_max = 1;
13499	ns_cmd->ns_cmd_code = NS_GA_NXT;
13500	ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
13501	((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = d_id - 1;
13502	((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
13503
13504	ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
13505	rval = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
13506	fctl_free_ns_cmd(ns_cmd);
13507
13508	if (rval != FC_SUCCESS || job->job_result != FC_SUCCESS) {
13509		fctl_dealloc_job(job);
13510		return (NULL);
13511	}
13512	fctl_dealloc_job(job);
13513
13514	pd = fctl_get_remote_port_by_did(port, d_id);
13515
13516	FP_TRACE(FP_NHEAD1(1, 0), "PD creation end; port=%p, d_id=%x, pd=%p",
13517	    port, d_id, pd);
13518
13519	return (pd);
13520}
13521
13522
13523/*
13524 * Check for the permissions on an ioctl command. If it is required to have an
13525 * EXCLUSIVE open performed, return a FAILURE to just shut the door on it. If
13526 * the ioctl command isn't in one of the list built, shut the door on that too.
13527 *
13528 * 	Certain ioctls perform hardware accesses in FCA drivers, and it needs
13529 *	to be made sure that users open the port for an exclusive access while
13530 *	performing those operations.
13531 *
13532 * 	This can prevent a casual user from inflicting damage on the port by
13533 *	sending these ioctls from multiple processes/threads (there is no good
13534 *	reason why one would need to do that) without actually realizing how
13535 *	expensive such commands could turn out to be.
13536 *
13537 *	It is also important to note that, even with an exclusive access,
13538 *	multiple threads can share the same file descriptor and fire down
13539 *	commands in parallel. To prevent that the driver needs to make sure
13540 *	that such commands aren't in progress already. This is taken care of
13541 *	in the FP_EXCL_BUSY bit of fp_flag.
13542 */
13543static int
13544fp_check_perms(uchar_t open_flag, uint16_t ioctl_cmd)
13545{
13546	int ret = FC_FAILURE;
13547	int count;
13548
13549	for (count = 0;
13550	    count < sizeof (fp_perm_list) / sizeof (fp_perm_list[0]);
13551	    count++) {
13552		if (fp_perm_list[count].fp_ioctl_cmd == ioctl_cmd) {
13553			if (fp_perm_list[count].fp_open_flag & open_flag) {
13554				ret = FC_SUCCESS;
13555			}
13556			break;
13557		}
13558	}
13559
13560	return (ret);
13561}
13562
13563
13564/*
13565 * Bind Port driver's unsolicited, state change callbacks
13566 */
13567static int
13568fp_bind_callbacks(fc_local_port_t *port)
13569{
13570	fc_fca_bind_info_t	bind_info = {0};
13571	fc_fca_port_info_t	*port_info;
13572	int		rval =  DDI_SUCCESS;
13573	uint16_t	class;
13574	int		node_namelen, port_namelen;
13575	char		*nname = NULL, *pname = NULL;
13576
13577	ASSERT(!MUTEX_HELD(&port->fp_mutex));
13578
13579	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, port->fp_port_dip,
13580	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
13581	    "node-name", &nname) != DDI_PROP_SUCCESS) {
13582		FP_TRACE(FP_NHEAD1(1, 0),
13583		    "fp_bind_callback fail to get node-name");
13584	}
13585	if (nname) {
13586		fc_str_to_wwn(nname, &(bind_info.port_nwwn));
13587	}
13588
13589	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, port->fp_port_dip,
13590	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
13591	    "port-name", &pname) != DDI_PROP_SUCCESS) {
13592		FP_TRACE(FP_NHEAD1(1, 0),
13593		    "fp_bind_callback fail to get port-name");
13594	}
13595	if (pname) {
13596		fc_str_to_wwn(pname, &(bind_info.port_pwwn));
13597	}
13598
13599	if (port->fp_npiv_type == FC_NPIV_PORT) {
13600		bind_info.port_npiv = 1;
13601	}
13602
13603	/*
13604	 * fca_bind_port returns the FCA driver's handle for the local
13605	 * port instance. If the port number isn't supported it returns NULL.
13606	 * It also sets up callback in the FCA for various
13607	 * things like state change, ELS etc..
13608	 */
13609	bind_info.port_statec_cb = fp_statec_cb;
13610	bind_info.port_unsol_cb = fp_unsol_cb;
13611	bind_info.port_num = port->fp_port_num;
13612	bind_info.port_handle = (opaque_t)port;
13613
13614	port_info = kmem_zalloc(sizeof (*port_info), KM_SLEEP);
13615
13616	/*
13617	 * Hold the port driver mutex as the callbacks are bound until the
13618	 * service parameters are properly filled in (in order to be able to
13619	 * properly respond to unsolicited ELS requests)
13620	 */
13621	mutex_enter(&port->fp_mutex);
13622
13623	port->fp_fca_handle = port->fp_fca_tran->fca_bind_port(
13624	    port->fp_fca_dip, port_info, &bind_info);
13625
13626	if (port->fp_fca_handle == NULL) {
13627		rval = DDI_FAILURE;
13628		goto exit;
13629	}
13630
13631	port->fp_bind_state = port->fp_state = port_info->pi_port_state;
13632	port->fp_service_params = port_info->pi_login_params;
13633	port->fp_hard_addr = port_info->pi_hard_addr;
13634
13635	/* Copy from the FCA structure to the FP structure */
13636	port->fp_hba_port_attrs = port_info->pi_attrs;
13637
13638	if (port_info->pi_rnid_params.status == FC_SUCCESS) {
13639		port->fp_rnid_init = 1;
13640		bcopy(&port_info->pi_rnid_params.params,
13641		    &port->fp_rnid_params,
13642		    sizeof (port->fp_rnid_params));
13643	} else {
13644		port->fp_rnid_init = 0;
13645	}
13646
13647	node_namelen = strlen((char *)&port_info->pi_attrs.sym_node_name);
13648	if (node_namelen) {
13649		bcopy(&port_info->pi_attrs.sym_node_name,
13650		    &port->fp_sym_node_name,
13651		    node_namelen);
13652		port->fp_sym_node_namelen = node_namelen;
13653	}
13654	port_namelen = strlen((char *)&port_info->pi_attrs.sym_port_name);
13655	if (port_namelen) {
13656		bcopy(&port_info->pi_attrs.sym_port_name,
13657		    &port->fp_sym_port_name,
13658		    port_namelen);
13659		port->fp_sym_port_namelen = port_namelen;
13660	}
13661
13662	/* zero out the normally unused fields right away */
13663	port->fp_service_params.ls_code.mbz = 0;
13664	port->fp_service_params.ls_code.ls_code = 0;
13665	bzero(&port->fp_service_params.reserved,
13666	    sizeof (port->fp_service_params.reserved));
13667
13668	class = port_info->pi_login_params.class_1.class_opt;
13669	port->fp_cos |= (class & 0x8000) ? FC_NS_CLASS1 : 0;
13670
13671	class = port_info->pi_login_params.class_2.class_opt;
13672	port->fp_cos |= (class & 0x8000) ? FC_NS_CLASS2 : 0;
13673
13674	class = port_info->pi_login_params.class_3.class_opt;
13675	port->fp_cos |= (class & 0x8000) ? FC_NS_CLASS3 : 0;
13676
13677exit:
13678	if (nname) {
13679		ddi_prop_free(nname);
13680	}
13681	if (pname) {
13682		ddi_prop_free(pname);
13683	}
13684	mutex_exit(&port->fp_mutex);
13685	kmem_free(port_info, sizeof (*port_info));
13686
13687	return (rval);
13688}
13689
13690
13691/*
13692 * Retrieve FCA capabilities
13693 */
13694static void
13695fp_retrieve_caps(fc_local_port_t *port)
13696{
13697	int			rval;
13698	int 			ub_count;
13699	fc_fcp_dma_t		fcp_dma;
13700	fc_reset_action_t	action;
13701	fc_dma_behavior_t	dma_behavior;
13702
13703	ASSERT(!MUTEX_HELD(&port->fp_mutex));
13704
13705	rval = port->fp_fca_tran->fca_get_cap(port->fp_fca_handle,
13706	    FC_CAP_UNSOL_BUF, &ub_count);
13707
13708	switch (rval) {
13709	case FC_CAP_FOUND:
13710	case FC_CAP_SETTABLE:
13711		switch (ub_count) {
13712		case 0:
13713			break;
13714
13715		case -1:
13716			ub_count = fp_unsol_buf_count;
13717			break;
13718
13719		default:
13720			/* 1/4th of total buffers is my share */
13721			ub_count =
13722			    (ub_count / port->fp_fca_tran->fca_numports) >> 2;
13723			break;
13724		}
13725		break;
13726
13727	default:
13728		ub_count = 0;
13729		break;
13730	}
13731
13732	mutex_enter(&port->fp_mutex);
13733	port->fp_ub_count = ub_count;
13734	mutex_exit(&port->fp_mutex);
13735
13736	rval = port->fp_fca_tran->fca_get_cap(port->fp_fca_handle,
13737	    FC_CAP_POST_RESET_BEHAVIOR, &action);
13738
13739	switch (rval) {
13740	case FC_CAP_FOUND:
13741	case FC_CAP_SETTABLE:
13742		switch (action) {
13743		case FC_RESET_RETURN_NONE:
13744		case FC_RESET_RETURN_ALL:
13745		case FC_RESET_RETURN_OUTSTANDING:
13746			break;
13747
13748		default:
13749			action = FC_RESET_RETURN_NONE;
13750			break;
13751		}
13752		break;
13753
13754	default:
13755		action = FC_RESET_RETURN_NONE;
13756		break;
13757	}
13758	mutex_enter(&port->fp_mutex);
13759	port->fp_reset_action = action;
13760	mutex_exit(&port->fp_mutex);
13761
13762	rval = port->fp_fca_tran->fca_get_cap(port->fp_fca_handle,
13763	    FC_CAP_NOSTREAM_ON_UNALIGN_BUF, &dma_behavior);
13764
13765	switch (rval) {
13766	case FC_CAP_FOUND:
13767		switch (dma_behavior) {
13768		case FC_ALLOW_STREAMING:
13769			/* FALLTHROUGH */
13770		case FC_NO_STREAMING:
13771			break;
13772
13773		default:
13774			/*
13775			 * If capability was found and the value
13776			 * was incorrect assume the worst
13777			 */
13778			dma_behavior = FC_NO_STREAMING;
13779			break;
13780		}
13781		break;
13782
13783	default:
13784		/*
13785		 * If capability was not defined - allow streaming; existing
13786		 * FCAs should not be affected.
13787		 */
13788		dma_behavior = FC_ALLOW_STREAMING;
13789		break;
13790	}
13791	mutex_enter(&port->fp_mutex);
13792	port->fp_dma_behavior = dma_behavior;
13793	mutex_exit(&port->fp_mutex);
13794
13795	rval = port->fp_fca_tran->fca_get_cap(port->fp_fca_handle,
13796	    FC_CAP_FCP_DMA, &fcp_dma);
13797
13798	if (rval != FC_CAP_FOUND || (fcp_dma != FC_NO_DVMA_SPACE &&
13799	    fcp_dma != FC_DVMA_SPACE)) {
13800		fcp_dma = FC_DVMA_SPACE;
13801	}
13802
13803	mutex_enter(&port->fp_mutex);
13804	port->fp_fcp_dma = fcp_dma;
13805	mutex_exit(&port->fp_mutex);
13806}
13807
13808
13809/*
13810 * Handle Domain, Area changes in the Fabric.
13811 */
13812static void
13813fp_validate_area_domain(fc_local_port_t *port, uint32_t id, uint32_t mask,
13814    job_request_t *job, int sleep)
13815{
13816#ifdef	DEBUG
13817	uint32_t		dcnt;
13818#endif
13819	int			rval;
13820	int			send;
13821	int			index;
13822	int			listindex;
13823	int			login;
13824	int			job_flags;
13825	char 			ww_name[17];
13826	uint32_t		d_id;
13827	uint32_t		count;
13828	fctl_ns_req_t		*ns_cmd;
13829	fc_portmap_t		*list;
13830	fc_orphan_t 		*orp;
13831	fc_orphan_t		*norp;
13832	fc_orphan_t		*prev;
13833	fc_remote_port_t 	*pd;
13834	fc_remote_port_t 	*npd;
13835	struct pwwn_hash 	*head;
13836
13837	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
13838	    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
13839	    0, sleep);
13840	if (ns_cmd == NULL) {
13841		mutex_enter(&port->fp_mutex);
13842		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
13843			--port->fp_rscn_count;
13844		}
13845		mutex_exit(&port->fp_mutex);
13846
13847		return;
13848	}
13849	ns_cmd->ns_cmd_code = NS_GID_PN;
13850
13851	/*
13852	 * We need to get a new count of devices from the
13853	 * name server, which will also create any new devices
13854	 * as needed.
13855	 */
13856
13857	(void) fp_ns_get_devcount(port, job, 1, sleep);
13858
13859	FP_TRACE(FP_NHEAD1(3, 0),
13860	    "fp_validate_area_domain: get_devcount found %d devices",
13861	    port->fp_total_devices);
13862
13863	mutex_enter(&port->fp_mutex);
13864
13865	for (count = index = 0; index < pwwn_table_size; index++) {
13866		head = &port->fp_pwwn_table[index];
13867		pd = head->pwwn_head;
13868		while (pd != NULL) {
13869			mutex_enter(&pd->pd_mutex);
13870			if (pd->pd_flags != PD_ELS_IN_PROGRESS) {
13871				if ((pd->pd_port_id.port_id & mask) == id &&
13872				    pd->pd_recepient == PD_PLOGI_INITIATOR) {
13873					count++;
13874					pd->pd_type = PORT_DEVICE_OLD;
13875					pd->pd_flags = PD_ELS_MARK;
13876				}
13877			}
13878			mutex_exit(&pd->pd_mutex);
13879			pd = pd->pd_wwn_hnext;
13880		}
13881	}
13882
13883#ifdef	DEBUG
13884	dcnt = count;
13885#endif /* DEBUG */
13886
13887	/*
13888	 * Since port->fp_orphan_count is declared an 'int' it is
13889	 * theoretically possible that the count could go negative.
13890	 *
13891	 * This would be bad and if that happens we really do want
13892	 * to know.
13893	 */
13894
13895	ASSERT(port->fp_orphan_count >= 0);
13896
13897	count += port->fp_orphan_count;
13898
13899	/*
13900	 * We add the port->fp_total_devices value to the count
13901	 * in the case where our port is newly attached. This is
13902	 * because we haven't done any discovery and we don't have
13903	 * any orphans in the port's orphan list. If we do not do
13904	 * this addition to count then we won't alloc enough kmem
13905	 * to do discovery with.
13906	 */
13907
13908	if (count == 0) {
13909		count += port->fp_total_devices;
13910		FP_TRACE(FP_NHEAD1(3, 0), "fp_validate_area_domain: "
13911		    "0x%x orphans found, using 0x%x",
13912		    port->fp_orphan_count, count);
13913	}
13914
13915	mutex_exit(&port->fp_mutex);
13916
13917	/*
13918	 * Allocate the change list
13919	 */
13920
13921	list = kmem_zalloc(sizeof (fc_portmap_t) * count, sleep);
13922	if (list == NULL) {
13923		fp_printf(port, CE_NOTE, FP_LOG_ONLY, 0, NULL,
13924		    " Not enough memory to service RSCNs"
13925		    " for %d ports, continuing...", count);
13926
13927		fctl_free_ns_cmd(ns_cmd);
13928
13929		mutex_enter(&port->fp_mutex);
13930		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
13931			--port->fp_rscn_count;
13932		}
13933		mutex_exit(&port->fp_mutex);
13934
13935		return;
13936	}
13937
13938	/*
13939	 * Attempt to validate or invalidate the devices that were
13940	 * already in the pwwn hash table.
13941	 */
13942
13943	mutex_enter(&port->fp_mutex);
13944	for (listindex = 0, index = 0; index < pwwn_table_size; index++) {
13945		head = &port->fp_pwwn_table[index];
13946		npd = head->pwwn_head;
13947
13948		while ((pd = npd) != NULL) {
13949			npd = pd->pd_wwn_hnext;
13950
13951			mutex_enter(&pd->pd_mutex);
13952			if ((pd->pd_port_id.port_id & mask) == id &&
13953			    pd->pd_flags == PD_ELS_MARK) {
13954				la_wwn_t *pwwn;
13955
13956				job->job_result = FC_SUCCESS;
13957
13958				((ns_req_gid_pn_t *)
13959				    (ns_cmd->ns_cmd_buf))->pwwn =
13960				    pd->pd_port_name;
13961
13962				pwwn = &pd->pd_port_name;
13963				d_id = pd->pd_port_id.port_id;
13964
13965				mutex_exit(&pd->pd_mutex);
13966				mutex_exit(&port->fp_mutex);
13967
13968				rval = fp_ns_query(port, ns_cmd, job, 1,
13969				    sleep);
13970				if (rval != FC_SUCCESS) {
13971					fc_wwn_to_str(pwwn, ww_name);
13972
13973					FP_TRACE(FP_NHEAD1(3, 0),
13974					    "AREA RSCN: PD disappeared; "
13975					    "d_id=%x, PWWN=%s", d_id, ww_name);
13976
13977					FP_TRACE(FP_NHEAD2(9, 0),
13978					    "N_x Port with D_ID=%x,"
13979					    " PWWN=%s disappeared from fabric",
13980					    d_id, ww_name);
13981
13982					fp_fillout_old_map(list + listindex++,
13983					    pd, 1);
13984				} else {
13985					fctl_copy_portmap(list + listindex++,
13986					    pd);
13987
13988					mutex_enter(&pd->pd_mutex);
13989					pd->pd_flags = PD_ELS_IN_PROGRESS;
13990					mutex_exit(&pd->pd_mutex);
13991				}
13992
13993				mutex_enter(&port->fp_mutex);
13994			} else {
13995				mutex_exit(&pd->pd_mutex);
13996			}
13997		}
13998	}
13999
14000	mutex_exit(&port->fp_mutex);
14001
14002	ASSERT(listindex == dcnt);
14003
14004	job->job_counter = listindex;
14005	job_flags = job->job_flags;
14006	job->job_flags |= JOB_TYPE_FP_ASYNC;
14007
14008	/*
14009	 * Login (if we were the initiator) or validate devices in the
14010	 * port map.
14011	 */
14012
14013	for (index = 0; index < listindex; index++) {
14014		pd = list[index].map_pd;
14015
14016		mutex_enter(&pd->pd_mutex);
14017		ASSERT((pd->pd_port_id.port_id & mask) == id);
14018
14019		if (pd->pd_flags != PD_ELS_IN_PROGRESS) {
14020			ASSERT(pd->pd_type == PORT_DEVICE_OLD);
14021			mutex_exit(&pd->pd_mutex);
14022			fp_jobdone(job);
14023			continue;
14024		}
14025
14026		login = (pd->pd_state == PORT_DEVICE_LOGGED_IN) ? 1 : 0;
14027		send = (pd->pd_recepient == PD_PLOGI_INITIATOR) ? 1 : 0;
14028		d_id = pd->pd_port_id.port_id;
14029		mutex_exit(&pd->pd_mutex);
14030
14031		if ((d_id & mask) == id && send) {
14032			if (login) {
14033				FP_TRACE(FP_NHEAD1(6, 0),
14034				    "RSCN and PLOGI request;"
14035				    " pd=%p, job=%p d_id=%x, index=%d", pd,
14036				    job, d_id, index);
14037
14038				rval = fp_port_login(port, d_id, job,
14039				    FP_CMD_PLOGI_RETAIN, sleep, pd, NULL);
14040				if (rval != FC_SUCCESS) {
14041					mutex_enter(&pd->pd_mutex);
14042					pd->pd_flags = PD_IDLE;
14043					mutex_exit(&pd->pd_mutex);
14044
14045					job->job_result = rval;
14046					fp_jobdone(job);
14047				}
14048
14049				FP_TRACE(FP_NHEAD2(4, 0),
14050				    "PLOGI succeeded:no skip(1) for "
14051				    "D_ID %x", d_id);
14052				list[index].map_flags |=
14053				    PORT_DEVICE_NO_SKIP_DEVICE_DISCOVERY;
14054			} else {
14055				FP_TRACE(FP_NHEAD1(6, 0), "RSCN and NS request;"
14056				    " pd=%p, job=%p d_id=%x, index=%d", pd,
14057				    job, d_id, index);
14058
14059				rval = fp_ns_validate_device(port, pd, job,
14060				    0, sleep);
14061				if (rval != FC_SUCCESS) {
14062					fp_jobdone(job);
14063				}
14064				mutex_enter(&pd->pd_mutex);
14065				pd->pd_flags = PD_IDLE;
14066				mutex_exit(&pd->pd_mutex);
14067			}
14068		} else {
14069			FP_TRACE(FP_NHEAD1(6, 0),
14070			    "RSCN and NO request sent; pd=%p,"
14071			    " d_id=%x, index=%d", pd, d_id, index);
14072
14073			mutex_enter(&pd->pd_mutex);
14074			pd->pd_flags = PD_IDLE;
14075			mutex_exit(&pd->pd_mutex);
14076
14077			fp_jobdone(job);
14078		}
14079	}
14080
14081	if (listindex) {
14082		fctl_jobwait(job);
14083	}
14084	job->job_flags = job_flags;
14085
14086	/*
14087	 * Orphan list validation.
14088	 */
14089	mutex_enter(&port->fp_mutex);
14090	for (prev = NULL, orp = port->fp_orphan_list; port->fp_orphan_count &&
14091	    orp != NULL; orp = norp) {
14092		norp = orp->orp_next;
14093		mutex_exit(&port->fp_mutex);
14094
14095		job->job_counter = 1;
14096		job->job_result = FC_SUCCESS;
14097		ASSERT((job->job_flags & JOB_TYPE_FP_ASYNC) == 0);
14098
14099		((ns_req_gid_pn_t *)ns_cmd->ns_cmd_buf)->pwwn = orp->orp_pwwn;
14100
14101		((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.port_id = 0;
14102		((ns_resp_gid_pn_t *)
14103		    ns_cmd->ns_data_buf)->pid.priv_lilp_posit = 0;
14104
14105		rval = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
14106		if (rval == FC_SUCCESS) {
14107			d_id = BE_32(*((uint32_t *)ns_cmd->ns_data_buf));
14108			pd = fp_create_remote_port_by_ns(port, d_id, KM_SLEEP);
14109			if (pd != NULL) {
14110				fc_wwn_to_str(&orp->orp_pwwn, ww_name);
14111
14112				FP_TRACE(FP_NHEAD1(6, 0),
14113				    "RSCN and ORPHAN list "
14114				    "success; d_id=%x, PWWN=%s", d_id, ww_name);
14115
14116				FP_TRACE(FP_NHEAD2(6, 0),
14117				    "N_x Port with D_ID=%x, PWWN=%s reappeared"
14118				    " in fabric", d_id, ww_name);
14119
14120				mutex_enter(&port->fp_mutex);
14121				if (prev) {
14122					prev->orp_next = orp->orp_next;
14123				} else {
14124					ASSERT(orp == port->fp_orphan_list);
14125					port->fp_orphan_list = orp->orp_next;
14126				}
14127				port->fp_orphan_count--;
14128				mutex_exit(&port->fp_mutex);
14129
14130				kmem_free(orp, sizeof (*orp));
14131				fctl_copy_portmap(list + listindex++, pd);
14132			} else {
14133				prev = orp;
14134			}
14135		} else {
14136			prev = orp;
14137		}
14138		mutex_enter(&port->fp_mutex);
14139	}
14140	mutex_exit(&port->fp_mutex);
14141
14142	/*
14143	 * One more pass through the list to delist old devices from
14144	 * the d_id and pwwn tables and possibly add to the orphan list.
14145	 */
14146
14147	for (index = 0; index < listindex; index++) {
14148		pd = list[index].map_pd;
14149		ASSERT(pd != NULL);
14150
14151		/*
14152		 * Update PLOGI results; For NS validation
14153		 * of orphan list, it is redundant
14154		 *
14155		 * Take care to preserve PORT_DEVICE_NO_SKIP_DEVICE_DISCOVERY if
14156		 * appropriate as fctl_copy_portmap() will clear map_flags.
14157		 */
14158		if (list[index].map_flags &
14159		    PORT_DEVICE_NO_SKIP_DEVICE_DISCOVERY) {
14160			fctl_copy_portmap(list + index, pd);
14161			list[index].map_flags |=
14162			    PORT_DEVICE_NO_SKIP_DEVICE_DISCOVERY;
14163		} else {
14164			fctl_copy_portmap(list + index, pd);
14165		}
14166
14167		FP_TRACE(FP_NHEAD1(6, 0), "RSCN with Area DOMAIN "
14168		    "results; pd=%p, d_id=%x pwwn=%x %x %x %x %x %x %x %x",
14169		    pd, pd->pd_port_id.port_id,
14170		    pd->pd_port_name.raw_wwn[0],
14171		    pd->pd_port_name.raw_wwn[1],
14172		    pd->pd_port_name.raw_wwn[2],
14173		    pd->pd_port_name.raw_wwn[3],
14174		    pd->pd_port_name.raw_wwn[4],
14175		    pd->pd_port_name.raw_wwn[5],
14176		    pd->pd_port_name.raw_wwn[6],
14177		    pd->pd_port_name.raw_wwn[7]);
14178
14179		FP_TRACE(FP_NHEAD1(6, 0), "RSCN with Area DOMAIN "
14180		    "results continued, pd=%p type=%x, flags=%x, state=%x",
14181		    pd, pd->pd_type, pd->pd_flags, pd->pd_state);
14182
14183		mutex_enter(&pd->pd_mutex);
14184		if (pd->pd_type == PORT_DEVICE_OLD) {
14185			int initiator;
14186
14187			pd->pd_flags = PD_IDLE;
14188			initiator = (pd->pd_recepient ==
14189			    PD_PLOGI_INITIATOR) ? 1 : 0;
14190
14191			mutex_exit(&pd->pd_mutex);
14192
14193			mutex_enter(&port->fp_mutex);
14194			mutex_enter(&pd->pd_mutex);
14195
14196			pd->pd_state = PORT_DEVICE_INVALID;
14197			fctl_delist_did_table(port, pd);
14198			fctl_delist_pwwn_table(port, pd);
14199
14200			mutex_exit(&pd->pd_mutex);
14201			mutex_exit(&port->fp_mutex);
14202
14203			if (initiator) {
14204				(void) fctl_add_orphan(port, pd, sleep);
14205			}
14206			list[index].map_pd = pd;
14207		} else {
14208			ASSERT(pd->pd_flags == PD_IDLE);
14209			if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
14210				/*
14211				 * Reset LOGO tolerance to zero
14212				 */
14213				fctl_tc_reset(&pd->pd_logo_tc);
14214			}
14215			mutex_exit(&pd->pd_mutex);
14216		}
14217	}
14218
14219	if (ns_cmd) {
14220		fctl_free_ns_cmd(ns_cmd);
14221	}
14222	if (listindex) {
14223		(void) fp_ulp_devc_cb(port, list, listindex, count,
14224		    sleep, 0);
14225	} else {
14226		kmem_free(list, sizeof (*list) * count);
14227
14228		mutex_enter(&port->fp_mutex);
14229		if (--port->fp_rscn_count == FC_INVALID_RSCN_COUNT) {
14230			--port->fp_rscn_count;
14231		}
14232		mutex_exit(&port->fp_mutex);
14233	}
14234}
14235
14236
14237/*
14238 * Work hard to make sense out of an RSCN page.
14239 */
14240static void
14241fp_validate_rscn_page(fc_local_port_t *port, fc_affected_id_t *page,
14242    job_request_t *job, fctl_ns_req_t *ns_cmd, fc_portmap_t *listptr,
14243    int *listindex, int sleep)
14244{
14245	int			rval;
14246	char 			ww_name[17];
14247	la_wwn_t		*pwwn;
14248	fc_remote_port_t	*pwwn_pd;
14249	fc_remote_port_t	*did_pd;
14250
14251	did_pd = fctl_get_remote_port_by_did(port, page->aff_d_id);
14252
14253	FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page; "
14254	    "port=%p, d_id=%x, pd=%p, rscn_count:0x%x", port, page->aff_d_id,
14255	    did_pd, (uint32_t)(uintptr_t)job->job_cb_arg);
14256
14257	if (did_pd != NULL) {
14258		mutex_enter(&did_pd->pd_mutex);
14259		if (did_pd->pd_flags != PD_IDLE) {
14260			mutex_exit(&did_pd->pd_mutex);
14261			FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page: "
14262			    "PD is BUSY; port=%p, d_id=%x, pd=%p",
14263			    port, page->aff_d_id, did_pd);
14264			return;
14265		}
14266		did_pd->pd_flags = PD_ELS_IN_PROGRESS;
14267		mutex_exit(&did_pd->pd_mutex);
14268	}
14269
14270	job->job_counter = 1;
14271
14272	pwwn = &((ns_resp_gpn_id_t *)ns_cmd->ns_data_buf)->pwwn;
14273
14274	((ns_req_gpn_id_t *)ns_cmd->ns_cmd_buf)->pid.port_id = page->aff_d_id;
14275	((ns_req_gpn_id_t *)ns_cmd->ns_cmd_buf)->pid.priv_lilp_posit = 0;
14276
14277	bzero(ns_cmd->ns_data_buf, sizeof (la_wwn_t));
14278	rval = fp_ns_query(port, ns_cmd, job, 1, sleep);
14279
14280	FP_TRACE(FP_NHEAD1(1, 0), "NS Query Response for D_ID page; rev=%x,"
14281	    " in_id=%x, cmdrsp=%x, reason=%x, expln=%x",
14282	    ns_cmd->ns_resp_hdr.ct_rev, ns_cmd->ns_resp_hdr.ct_inid,
14283	    ns_cmd->ns_resp_hdr.ct_cmdrsp, ns_cmd->ns_resp_hdr.ct_reason,
14284	    ns_cmd->ns_resp_hdr.ct_expln);
14285
14286	job->job_counter = 1;
14287
14288	if (rval != FC_SUCCESS || fctl_is_wwn_zero(pwwn) == FC_SUCCESS) {
14289		/*
14290		 * What this means is that the D_ID
14291		 * disappeared from the Fabric.
14292		 */
14293		if (did_pd == NULL) {
14294			FP_TRACE(FP_NHEAD1(1, 0), "RSCN with D_ID page;"
14295			    " NULL PD disappeared, rval=%x", rval);
14296			return;
14297		}
14298
14299		fc_wwn_to_str(&did_pd->pd_port_name, ww_name);
14300
14301		(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14302		    (uint32_t)(uintptr_t)job->job_cb_arg;
14303
14304		fp_fillout_old_map(listptr + (*listindex)++, did_pd, 0);
14305
14306		FP_TRACE(FP_NHEAD1(3, 0), "RSCN: PD disappeared; "
14307		    "d_id=%x, PWWN=%s", page->aff_d_id, ww_name);
14308
14309		FP_TRACE(FP_NHEAD2(9, 0),
14310		    "GPN_ID for D_ID=%x failed", page->aff_d_id);
14311
14312		FP_TRACE(FP_NHEAD2(9, 0),
14313		    "N_x Port with D_ID=%x, PWWN=%s disappeared from"
14314		    " fabric", page->aff_d_id, ww_name);
14315
14316		mutex_enter(&did_pd->pd_mutex);
14317		did_pd->pd_flags = PD_IDLE;
14318		mutex_exit(&did_pd->pd_mutex);
14319
14320		FP_TRACE(FP_NHEAD1(3, 0), "RSCN with D_ID (%x) page; "
14321		    "PD disappeared, pd=%p", page->aff_d_id, did_pd);
14322
14323		return;
14324	}
14325
14326	pwwn_pd = fctl_get_remote_port_by_pwwn(port, pwwn);
14327
14328	if (did_pd != NULL && pwwn_pd != NULL && did_pd == pwwn_pd) {
14329		/*
14330		 * There is no change. Do PLOGI again and add it to
14331		 * ULP portmap baggage and return. Note: When RSCNs
14332		 * arrive with per page states, the need for PLOGI
14333		 * can be determined correctly.
14334		 */
14335		mutex_enter(&pwwn_pd->pd_mutex);
14336		pwwn_pd->pd_type = PORT_DEVICE_NOCHANGE;
14337		mutex_exit(&pwwn_pd->pd_mutex);
14338
14339		(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14340		    (uint32_t)(uintptr_t)job->job_cb_arg;
14341
14342		fctl_copy_portmap(listptr + (*listindex)++, pwwn_pd);
14343
14344		mutex_enter(&pwwn_pd->pd_mutex);
14345		if ((pwwn_pd->pd_state == PORT_DEVICE_LOGGED_IN) ||
14346			(pwwn_pd->pd_aux_flags & PD_LOGGED_OUT)) {
14347			fc_wwn_to_str(&pwwn_pd->pd_port_name, ww_name);
14348			mutex_exit(&pwwn_pd->pd_mutex);
14349
14350			rval = fp_port_login(port, page->aff_d_id, job,
14351			    FP_CMD_PLOGI_RETAIN, sleep, pwwn_pd, NULL);
14352			if (rval == FC_SUCCESS) {
14353				fp_jobwait(job);
14354				rval = job->job_result;
14355
14356				/*
14357				 * Reset LOGO tolerance to zero
14358				 * Also we are the PLOGI initiator now.
14359				 */
14360				mutex_enter(&pwwn_pd->pd_mutex);
14361				fctl_tc_reset(&pwwn_pd->pd_logo_tc);
14362				pwwn_pd->pd_recepient = PD_PLOGI_INITIATOR;
14363				mutex_exit(&pwwn_pd->pd_mutex);
14364			}
14365
14366			if (rval == FC_SUCCESS) {
14367				struct fc_portmap *map =
14368				    listptr + *listindex - 1;
14369
14370				FP_TRACE(FP_NHEAD2(4, 0),
14371				    "PLOGI succeeded: no skip(2)"
14372				    " for D_ID %x", page->aff_d_id);
14373				map->map_flags |=
14374				    PORT_DEVICE_NO_SKIP_DEVICE_DISCOVERY;
14375			} else {
14376				FP_TRACE(FP_NHEAD2(9, rval),
14377				    "PLOGI to D_ID=%x failed", page->aff_d_id);
14378
14379				FP_TRACE(FP_NHEAD2(9, 0),
14380				    "N_x Port with D_ID=%x, PWWN=%s"
14381				    " disappeared from fabric",
14382				    page->aff_d_id, ww_name);
14383
14384				fp_fillout_old_map(listptr +
14385				    *listindex - 1, pwwn_pd, 0);
14386			}
14387		} else {
14388			mutex_exit(&pwwn_pd->pd_mutex);
14389		}
14390
14391		mutex_enter(&did_pd->pd_mutex);
14392		did_pd->pd_flags = PD_IDLE;
14393		mutex_exit(&did_pd->pd_mutex);
14394
14395		FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID (0x%x) page; "
14396		    "Case ONE, rval=%x, result=%x pd=%p", page->aff_d_id, rval,
14397		    job->job_result, pwwn_pd);
14398
14399		return;
14400	}
14401
14402	if (did_pd == NULL && pwwn_pd == NULL) {
14403
14404		fc_orphan_t	*orp  = NULL;
14405		fc_orphan_t	*norp = NULL;
14406		fc_orphan_t	*prev = NULL;
14407
14408		/*
14409		 * Hunt down the orphan list before giving up.
14410		 */
14411
14412		mutex_enter(&port->fp_mutex);
14413		if (port->fp_orphan_count) {
14414
14415			for (orp = port->fp_orphan_list; orp; orp = norp) {
14416				norp = orp->orp_next;
14417
14418				if (fctl_wwn_cmp(&orp->orp_pwwn, pwwn) != 0) {
14419					prev = orp;
14420					continue;
14421				}
14422
14423				if (prev) {
14424					prev->orp_next = orp->orp_next;
14425				} else {
14426					ASSERT(orp ==
14427					    port->fp_orphan_list);
14428					port->fp_orphan_list =
14429					    orp->orp_next;
14430				}
14431				port->fp_orphan_count--;
14432				break;
14433			}
14434		}
14435
14436		mutex_exit(&port->fp_mutex);
14437		pwwn_pd = fp_create_remote_port_by_ns(port,
14438		    page->aff_d_id, sleep);
14439
14440		if (pwwn_pd != NULL) {
14441
14442			if (orp) {
14443				fc_wwn_to_str(&orp->orp_pwwn,
14444				    ww_name);
14445
14446				FP_TRACE(FP_NHEAD2(9, 0),
14447				    "N_x Port with D_ID=%x,"
14448				    " PWWN=%s reappeared in fabric",
14449				    page->aff_d_id, ww_name);
14450
14451				kmem_free(orp, sizeof (*orp));
14452			}
14453
14454			(listptr + *listindex)->
14455			    map_rscn_info.ulp_rscn_count =
14456			    (uint32_t)(uintptr_t)job->job_cb_arg;
14457
14458			fctl_copy_portmap(listptr +
14459			    (*listindex)++, pwwn_pd);
14460		}
14461
14462		FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID (0x%x) page; "
14463		    "Case TWO", page->aff_d_id);
14464
14465		return;
14466	}
14467
14468	if (pwwn_pd != NULL && did_pd == NULL) {
14469		uint32_t old_d_id;
14470		uint32_t d_id = page->aff_d_id;
14471
14472		/*
14473		 * What this means is there is a new D_ID for this
14474		 * Port WWN. Take out the port device off D_ID
14475		 * list and put it back with a new D_ID. Perform
14476		 * PLOGI if already logged in.
14477		 */
14478		mutex_enter(&port->fp_mutex);
14479		mutex_enter(&pwwn_pd->pd_mutex);
14480
14481		old_d_id = pwwn_pd->pd_port_id.port_id;
14482
14483		fctl_delist_did_table(port, pwwn_pd);
14484
14485		(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14486		    (uint32_t)(uintptr_t)job->job_cb_arg;
14487
14488		fp_fillout_changed_map(listptr + (*listindex)++, pwwn_pd,
14489		    &d_id, NULL);
14490		fctl_enlist_did_table(port, pwwn_pd);
14491
14492		FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page;"
14493		    " Case THREE, pd=%p,"
14494		    " state=%x", pwwn_pd, pwwn_pd->pd_state);
14495
14496		if ((pwwn_pd->pd_state == PORT_DEVICE_LOGGED_IN) ||
14497			(pwwn_pd->pd_aux_flags & PD_LOGGED_OUT)) {
14498			fc_wwn_to_str(&pwwn_pd->pd_port_name, ww_name);
14499
14500			mutex_exit(&pwwn_pd->pd_mutex);
14501			mutex_exit(&port->fp_mutex);
14502
14503			FP_TRACE(FP_NHEAD2(9, 0),
14504			    "N_x Port with D_ID=%x, PWWN=%s has a new"
14505			    " D_ID=%x now", old_d_id, ww_name, d_id);
14506
14507			rval = fp_port_login(port, page->aff_d_id, job,
14508			    FP_CMD_PLOGI_RETAIN, sleep, pwwn_pd, NULL);
14509			if (rval == FC_SUCCESS) {
14510				fp_jobwait(job);
14511				rval = job->job_result;
14512			}
14513
14514			if (rval != FC_SUCCESS) {
14515				fp_fillout_old_map(listptr +
14516				    *listindex - 1, pwwn_pd, 0);
14517			}
14518		} else {
14519			mutex_exit(&pwwn_pd->pd_mutex);
14520			mutex_exit(&port->fp_mutex);
14521		}
14522
14523		return;
14524	}
14525
14526	if (pwwn_pd == NULL && did_pd != NULL) {
14527		fc_portmap_t	*ptr;
14528		uint32_t 	len = 1;
14529		char 		old_ww_name[17];
14530
14531		mutex_enter(&did_pd->pd_mutex);
14532		fc_wwn_to_str(&did_pd->pd_port_name, old_ww_name);
14533		mutex_exit(&did_pd->pd_mutex);
14534
14535		fc_wwn_to_str(pwwn, ww_name);
14536
14537		(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14538		    (uint32_t)(uintptr_t)job->job_cb_arg;
14539
14540		/*
14541		 * What this means is that there is a new Port WWN for
14542		 * this D_ID; Mark the Port device as old and provide
14543		 * the new PWWN and D_ID combination as new.
14544		 */
14545		fp_fillout_old_map(listptr + (*listindex)++, did_pd, 0);
14546
14547		FP_TRACE(FP_NHEAD2(9, 0),
14548		    "N_x Port with D_ID=%x, PWWN=%s has a new PWWN=%s now",
14549		    page->aff_d_id, old_ww_name, ww_name);
14550
14551		(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14552		    (uint32_t)(uintptr_t)job->job_cb_arg;
14553
14554		ptr = listptr + (*listindex)++;
14555
14556		job->job_counter = 1;
14557
14558		if (fp_ns_getmap(port, job, &ptr, &len,
14559		    page->aff_d_id - 1) != FC_SUCCESS) {
14560			(*listindex)--;
14561		}
14562
14563		mutex_enter(&did_pd->pd_mutex);
14564		did_pd->pd_flags = PD_IDLE;
14565		mutex_exit(&did_pd->pd_mutex);
14566
14567		return;
14568	}
14569
14570	/*
14571	 * A weird case of Port WWN and D_ID existence but not matching up
14572	 * between them. Trust your instincts - Take the port device handle
14573	 * off Port WWN list, fix it with new Port WWN and put it back, In
14574	 * the mean time mark the port device corresponding to the old port
14575	 * WWN as OLD.
14576	 */
14577	FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page; Case WEIRD, pwwn_pd=%p,"
14578	    " did_pd=%p", pwwn_pd, did_pd);
14579
14580	mutex_enter(&port->fp_mutex);
14581	mutex_enter(&pwwn_pd->pd_mutex);
14582
14583	pwwn_pd->pd_type = PORT_DEVICE_OLD;
14584	pwwn_pd->pd_state = PORT_DEVICE_INVALID;
14585	fctl_delist_did_table(port, pwwn_pd);
14586	fctl_delist_pwwn_table(port, pwwn_pd);
14587
14588	FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page; case WEIRD continued,"
14589	    " pwwn-d_id=%x pwwn-wwn=%x %x %x %x %x %x %x %x",
14590	    pwwn_pd->pd_port_id.port_id,
14591
14592	    pwwn_pd->pd_port_name.raw_wwn[0],
14593	    pwwn_pd->pd_port_name.raw_wwn[1],
14594	    pwwn_pd->pd_port_name.raw_wwn[2],
14595	    pwwn_pd->pd_port_name.raw_wwn[3],
14596	    pwwn_pd->pd_port_name.raw_wwn[4],
14597	    pwwn_pd->pd_port_name.raw_wwn[5],
14598	    pwwn_pd->pd_port_name.raw_wwn[6],
14599	    pwwn_pd->pd_port_name.raw_wwn[7]);
14600
14601	mutex_exit(&pwwn_pd->pd_mutex);
14602	mutex_exit(&port->fp_mutex);
14603
14604	(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14605	    (uint32_t)(uintptr_t)job->job_cb_arg;
14606
14607	fctl_copy_portmap(listptr + (*listindex)++, pwwn_pd);
14608
14609	mutex_enter(&port->fp_mutex);
14610	mutex_enter(&did_pd->pd_mutex);
14611
14612	fctl_delist_pwwn_table(port, did_pd);
14613
14614	(listptr + *listindex)->map_rscn_info.ulp_rscn_count =
14615	    (uint32_t)(uintptr_t)job->job_cb_arg;
14616
14617	fp_fillout_changed_map(listptr + (*listindex)++, did_pd, NULL, pwwn);
14618	fctl_enlist_pwwn_table(port, did_pd);
14619
14620	FP_TRACE(FP_NHEAD1(6, 0), "RSCN with D_ID page; case WEIRD continued,"
14621	    " d_id=%x, state=%x, did-wwn=%x %x %x %x %x %x %x %x",
14622	    did_pd->pd_port_id.port_id, did_pd->pd_state,
14623
14624	    did_pd->pd_port_name.raw_wwn[0],
14625	    did_pd->pd_port_name.raw_wwn[1],
14626	    did_pd->pd_port_name.raw_wwn[2],
14627	    did_pd->pd_port_name.raw_wwn[3],
14628	    did_pd->pd_port_name.raw_wwn[4],
14629	    did_pd->pd_port_name.raw_wwn[5],
14630	    did_pd->pd_port_name.raw_wwn[6],
14631	    did_pd->pd_port_name.raw_wwn[7]);
14632
14633	if ((did_pd->pd_state == PORT_DEVICE_LOGGED_IN) ||
14634		(did_pd->pd_aux_flags & PD_LOGGED_OUT)) {
14635		mutex_exit(&did_pd->pd_mutex);
14636		mutex_exit(&port->fp_mutex);
14637
14638		rval = fp_port_login(port, page->aff_d_id, job,
14639		    FP_CMD_PLOGI_RETAIN, sleep, did_pd, NULL);
14640		if (rval == FC_SUCCESS) {
14641			fp_jobwait(job);
14642			if (job->job_result != FC_SUCCESS) {
14643				fp_fillout_old_map(listptr +
14644				    *listindex - 1, did_pd, 0);
14645			}
14646		} else {
14647			fp_fillout_old_map(listptr + *listindex - 1, did_pd, 0);
14648		}
14649	} else {
14650		mutex_exit(&did_pd->pd_mutex);
14651		mutex_exit(&port->fp_mutex);
14652	}
14653
14654	mutex_enter(&did_pd->pd_mutex);
14655	did_pd->pd_flags = PD_IDLE;
14656	mutex_exit(&did_pd->pd_mutex);
14657}
14658
14659
14660/*
14661 * Check with NS for the presence of this port WWN
14662 */
14663static int
14664fp_ns_validate_device(fc_local_port_t *port, fc_remote_port_t *pd,
14665    job_request_t *job, int polled, int sleep)
14666{
14667	la_wwn_t	pwwn;
14668	uint32_t	flags;
14669	fctl_ns_req_t	*ns_cmd;
14670
14671	flags = FCTL_NS_VALIDATE_PD | ((polled) ? 0: FCTL_NS_ASYNC_REQUEST);
14672	ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
14673	    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
14674	    flags, sleep);
14675	if (ns_cmd == NULL) {
14676		return (FC_NOMEM);
14677	}
14678
14679	mutex_enter(&pd->pd_mutex);
14680	pwwn = pd->pd_port_name;
14681	mutex_exit(&pd->pd_mutex);
14682
14683	ns_cmd->ns_cmd_code = NS_GID_PN;
14684	ns_cmd->ns_pd = pd;
14685	((ns_req_gid_pn_t *)ns_cmd->ns_cmd_buf)->pwwn = pwwn;
14686	((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.port_id = 0;
14687	((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.priv_lilp_posit = 0;
14688
14689	return (fp_ns_query(port, ns_cmd, job, polled, sleep));
14690}
14691
14692
14693/*
14694 * Sanity check the LILP map returned by FCA
14695 */
14696static int
14697fp_validate_lilp_map(fc_lilpmap_t *lilp_map)
14698{
14699	int	count;
14700
14701	if (lilp_map->lilp_length == 0) {
14702		return (FC_FAILURE);
14703	}
14704
14705	for (count = 0; count < lilp_map->lilp_length; count++) {
14706		if (fp_is_valid_alpa(lilp_map->lilp_alpalist[count]) !=
14707		    FC_SUCCESS) {
14708			return (FC_FAILURE);
14709		}
14710	}
14711
14712	return (FC_SUCCESS);
14713}
14714
14715
14716/*
14717 * Sanity check if the AL_PA is a valid address
14718 */
14719static int
14720fp_is_valid_alpa(uchar_t al_pa)
14721{
14722	int 	count;
14723
14724	for (count = 0; count < sizeof (fp_valid_alpas); count++) {
14725		if (al_pa == fp_valid_alpas[count] || al_pa == 0) {
14726			return (FC_SUCCESS);
14727		}
14728	}
14729
14730	return (FC_FAILURE);
14731}
14732
14733
14734/*
14735 * Post unsolicited callbacks to ULPs
14736 */
14737static void
14738fp_ulp_unsol_cb(void *arg)
14739{
14740	fp_unsol_spec_t	*ub_spec = (fp_unsol_spec_t *)arg;
14741
14742	fctl_ulp_unsol_cb(ub_spec->port, ub_spec->buf,
14743	    ub_spec->buf->ub_frame.type);
14744	kmem_free(ub_spec, sizeof (*ub_spec));
14745}
14746
14747
14748/*
14749 * Perform message reporting in a consistent manner. Unless there is
14750 * a strong reason NOT to use this function (which is very very rare)
14751 * all message reporting should go through this.
14752 */
14753static void
14754fp_printf(fc_local_port_t *port, int level, fp_mesg_dest_t dest, int fc_errno,
14755    fc_packet_t *pkt, const char *fmt, ...)
14756{
14757	caddr_t		buf;
14758	va_list		ap;
14759
14760	switch (level) {
14761	case CE_NOTE:
14762		if ((port->fp_verbose & FP_WARNING_MESSAGES) == 0) {
14763			return;
14764		}
14765		break;
14766
14767	case CE_WARN:
14768		if ((port->fp_verbose & FP_FATAL_MESSAGES) == 0) {
14769			return;
14770		}
14771		break;
14772	}
14773
14774	buf = kmem_zalloc(256, KM_NOSLEEP);
14775	if (buf == NULL) {
14776		return;
14777	}
14778
14779	(void) sprintf(buf, "fp(%d): ", port->fp_instance);
14780
14781	va_start(ap, fmt);
14782	(void) vsprintf(buf + strlen(buf), fmt, ap);
14783	va_end(ap);
14784
14785	if (fc_errno) {
14786		char *errmsg;
14787
14788		(void) fc_ulp_error(fc_errno, &errmsg);
14789		(void) sprintf(buf + strlen(buf), " FC Error=%s", errmsg);
14790	} else {
14791		if (pkt) {
14792			caddr_t	state, reason, action, expln;
14793
14794			(void) fc_ulp_pkt_error(pkt, &state, &reason,
14795			    &action, &expln);
14796
14797			(void) sprintf(buf + strlen(buf),
14798			    " state=%s, reason=%s", state, reason);
14799
14800			if (pkt->pkt_resp_resid) {
14801				(void) sprintf(buf + strlen(buf),
14802				    " resp resid=%x\n", pkt->pkt_resp_resid);
14803			}
14804		}
14805	}
14806
14807	switch (dest) {
14808	case FP_CONSOLE_ONLY:
14809		cmn_err(level, "^%s", buf);
14810		break;
14811
14812	case FP_LOG_ONLY:
14813		cmn_err(level, "!%s", buf);
14814		break;
14815
14816	default:
14817		cmn_err(level, "%s", buf);
14818		break;
14819	}
14820
14821	kmem_free(buf, 256);
14822}
14823
14824static int
14825fp_fcio_login(fc_local_port_t *port, fcio_t *fcio, job_request_t *job)
14826{
14827	int			ret;
14828	uint32_t 		d_id;
14829	la_wwn_t		pwwn;
14830	fc_remote_port_t 	*pd = NULL;
14831	fc_remote_port_t 	*held_pd = NULL;
14832	fctl_ns_req_t		*ns_cmd;
14833	fc_portmap_t		*changelist;
14834
14835	bcopy(fcio->fcio_ibuf, &pwwn, sizeof (pwwn));
14836
14837	mutex_enter(&port->fp_mutex);
14838	if (FC_IS_TOP_SWITCH(port->fp_topology)) {
14839		mutex_exit(&port->fp_mutex);
14840		job->job_counter = 1;
14841
14842		job->job_result = FC_SUCCESS;
14843
14844		ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
14845		    sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
14846		    FCTL_NS_BUF_IS_USERLAND, KM_SLEEP);
14847
14848		ASSERT(ns_cmd != NULL);
14849
14850		ns_cmd->ns_cmd_code = NS_GID_PN;
14851		((ns_req_gid_pn_t *)(ns_cmd->ns_cmd_buf))->pwwn = pwwn;
14852
14853		ret = fp_ns_query(port, ns_cmd, job, 1, KM_SLEEP);
14854
14855		if (ret != FC_SUCCESS || job->job_result != FC_SUCCESS) {
14856			if (ret != FC_SUCCESS) {
14857				fcio->fcio_errno = ret;
14858			} else {
14859				fcio->fcio_errno = job->job_result;
14860			}
14861			fctl_free_ns_cmd(ns_cmd);
14862			return (EIO);
14863		}
14864		d_id = BE_32(*((uint32_t *)ns_cmd->ns_data_buf));
14865		fctl_free_ns_cmd(ns_cmd);
14866	} else {
14867		mutex_exit(&port->fp_mutex);
14868
14869		held_pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
14870		if (held_pd == NULL) {
14871			fcio->fcio_errno = FC_BADWWN;
14872			return (EIO);
14873		}
14874		pd = held_pd;
14875
14876		mutex_enter(&pd->pd_mutex);
14877		d_id = pd->pd_port_id.port_id;
14878		mutex_exit(&pd->pd_mutex);
14879	}
14880
14881	job->job_counter = 1;
14882
14883	pd = fctl_get_remote_port_by_did(port, d_id);
14884
14885	if (pd) {
14886		mutex_enter(&pd->pd_mutex);
14887		if (pd->pd_state == PORT_DEVICE_LOGGED_IN) {
14888			pd->pd_login_count++;
14889			mutex_exit(&pd->pd_mutex);
14890
14891			fcio->fcio_errno = FC_SUCCESS;
14892			if (held_pd) {
14893				fctl_release_remote_port(held_pd);
14894			}
14895
14896			return (0);
14897		}
14898		mutex_exit(&pd->pd_mutex);
14899	} else {
14900		mutex_enter(&port->fp_mutex);
14901		if (FC_IS_TOP_SWITCH(port->fp_topology)) {
14902			mutex_exit(&port->fp_mutex);
14903			pd = fp_create_remote_port_by_ns(port, d_id, KM_SLEEP);
14904			if (pd == NULL) {
14905				fcio->fcio_errno = FC_FAILURE;
14906				if (held_pd) {
14907					fctl_release_remote_port(held_pd);
14908				}
14909				return (EIO);
14910			}
14911		} else {
14912			mutex_exit(&port->fp_mutex);
14913		}
14914	}
14915
14916	job->job_flags &= ~JOB_TYPE_FP_ASYNC;
14917	job->job_counter = 1;
14918
14919	ret = fp_port_login(port, d_id, job, FP_CMD_PLOGI_RETAIN,
14920	    KM_SLEEP, pd, NULL);
14921
14922	if (ret != FC_SUCCESS) {
14923		fcio->fcio_errno = ret;
14924		if (held_pd) {
14925			fctl_release_remote_port(held_pd);
14926		}
14927		return (EIO);
14928	}
14929	fp_jobwait(job);
14930
14931	fcio->fcio_errno = job->job_result;
14932
14933	if (held_pd) {
14934		fctl_release_remote_port(held_pd);
14935	}
14936
14937	if (job->job_result != FC_SUCCESS) {
14938		return (EIO);
14939	}
14940
14941	pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
14942	if (pd == NULL) {
14943		fcio->fcio_errno = FC_BADDEV;
14944		return (ENODEV);
14945	}
14946
14947	changelist = kmem_zalloc(sizeof (*changelist), KM_SLEEP);
14948
14949	fctl_copy_portmap(changelist, pd);
14950	changelist->map_type = PORT_DEVICE_USER_LOGIN;
14951
14952	(void) fp_ulp_devc_cb(port, changelist, 1, 1, KM_SLEEP, 1);
14953
14954	mutex_enter(&pd->pd_mutex);
14955	pd->pd_type = PORT_DEVICE_NOCHANGE;
14956	mutex_exit(&pd->pd_mutex);
14957
14958	fctl_release_remote_port(pd);
14959
14960	return (0);
14961}
14962
14963
14964static int
14965fp_fcio_logout(fc_local_port_t *port, fcio_t *fcio, job_request_t *job)
14966{
14967	la_wwn_t		pwwn;
14968	fp_cmd_t		*cmd;
14969	fc_portmap_t		*changelist;
14970	fc_remote_port_t 	*pd;
14971
14972	bcopy(fcio->fcio_ibuf, &pwwn, sizeof (pwwn));
14973
14974	pd = fctl_hold_remote_port_by_pwwn(port, &pwwn);
14975	if (pd == NULL) {
14976		fcio->fcio_errno = FC_BADWWN;
14977		return (ENXIO);
14978	}
14979
14980	mutex_enter(&pd->pd_mutex);
14981	if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
14982		fcio->fcio_errno = FC_LOGINREQ;
14983		mutex_exit(&pd->pd_mutex);
14984
14985		fctl_release_remote_port(pd);
14986
14987		return (EINVAL);
14988	}
14989
14990	ASSERT(pd->pd_login_count >= 1);
14991
14992	if (pd->pd_flags == PD_ELS_IN_PROGRESS) {
14993		fcio->fcio_errno = FC_FAILURE;
14994		mutex_exit(&pd->pd_mutex);
14995
14996		fctl_release_remote_port(pd);
14997
14998		return (EBUSY);
14999	}
15000
15001	if (pd->pd_login_count > 1) {
15002		pd->pd_login_count--;
15003		fcio->fcio_errno = FC_SUCCESS;
15004		mutex_exit(&pd->pd_mutex);
15005
15006		changelist = kmem_zalloc(sizeof (*changelist), KM_SLEEP);
15007
15008		fctl_copy_portmap(changelist, pd);
15009		changelist->map_type = PORT_DEVICE_USER_LOGOUT;
15010
15011		fctl_release_remote_port(pd);
15012
15013		(void) fp_ulp_devc_cb(port, changelist, 1, 1, KM_SLEEP, 1);
15014
15015		return (0);
15016	}
15017
15018	pd->pd_flags = PD_ELS_IN_PROGRESS;
15019	mutex_exit(&pd->pd_mutex);
15020
15021	job->job_counter = 1;
15022
15023	cmd = fp_alloc_pkt(port, sizeof (la_els_logo_t),
15024	    FP_PORT_IDENTIFIER_LEN, KM_SLEEP, pd);
15025	if (cmd == NULL) {
15026		fcio->fcio_errno = FC_NOMEM;
15027		fctl_release_remote_port(pd);
15028
15029		mutex_enter(&pd->pd_mutex);
15030		pd->pd_flags = PD_IDLE;
15031		mutex_exit(&pd->pd_mutex);
15032
15033		return (ENOMEM);
15034	}
15035
15036	mutex_enter(&port->fp_mutex);
15037	mutex_enter(&pd->pd_mutex);
15038
15039	cmd->cmd_pkt.pkt_tran_flags = FC_TRAN_INTR | pd->pd_login_class;
15040	cmd->cmd_pkt.pkt_tran_type = FC_PKT_EXCHANGE;
15041	cmd->cmd_flags = FP_CMD_PLOGI_DONT_CARE;
15042	cmd->cmd_retry_count = 1;
15043	cmd->cmd_ulp_pkt = NULL;
15044
15045	fp_logo_init(pd, cmd, job);
15046
15047	mutex_exit(&pd->pd_mutex);
15048	mutex_exit(&port->fp_mutex);
15049
15050	if (fp_sendcmd(port, cmd, port->fp_fca_handle) != FC_SUCCESS) {
15051		mutex_enter(&pd->pd_mutex);
15052		pd->pd_flags = PD_IDLE;
15053		mutex_exit(&pd->pd_mutex);
15054
15055		fp_free_pkt(cmd);
15056		fctl_release_remote_port(pd);
15057
15058		return (EIO);
15059	}
15060
15061	fp_jobwait(job);
15062
15063	fcio->fcio_errno = job->job_result;
15064	if (job->job_result != FC_SUCCESS) {
15065		mutex_enter(&pd->pd_mutex);
15066		pd->pd_flags = PD_IDLE;
15067		mutex_exit(&pd->pd_mutex);
15068
15069		fctl_release_remote_port(pd);
15070
15071		return (EIO);
15072	}
15073
15074	ASSERT(pd != NULL);
15075
15076	changelist = kmem_zalloc(sizeof (*changelist), KM_SLEEP);
15077
15078	fctl_copy_portmap(changelist, pd);
15079	changelist->map_type = PORT_DEVICE_USER_LOGOUT;
15080	changelist->map_state = PORT_DEVICE_INVALID;
15081
15082	mutex_enter(&port->fp_mutex);
15083	mutex_enter(&pd->pd_mutex);
15084
15085	fctl_delist_did_table(port, pd);
15086	fctl_delist_pwwn_table(port, pd);
15087	pd->pd_flags = PD_IDLE;
15088
15089	mutex_exit(&pd->pd_mutex);
15090	mutex_exit(&port->fp_mutex);
15091
15092	(void) fp_ulp_devc_cb(port, changelist, 1, 1, KM_SLEEP, 1);
15093
15094	fctl_release_remote_port(pd);
15095
15096	return (0);
15097}
15098
15099
15100
15101/*
15102 * Send a syslog event for adapter port level events.
15103 */
15104static void
15105fp_log_port_event(fc_local_port_t *port, char *subclass)
15106{
15107	nvlist_t *attr_list;
15108
15109	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
15110	    KM_SLEEP) != DDI_SUCCESS) {
15111		goto alloc_failed;
15112	}
15113
15114	if (nvlist_add_uint32(attr_list, "instance",
15115	    port->fp_instance) != DDI_SUCCESS) {
15116		goto error;
15117	}
15118
15119	if (nvlist_add_byte_array(attr_list, "port-wwn",
15120	    port->fp_service_params.nport_ww_name.raw_wwn,
15121	    sizeof (la_wwn_t)) != DDI_SUCCESS) {
15122		goto error;
15123	}
15124
15125	(void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW, EC_SUNFC,
15126	    subclass, attr_list, NULL, DDI_SLEEP);
15127
15128	nvlist_free(attr_list);
15129	return;
15130
15131error:
15132	nvlist_free(attr_list);
15133alloc_failed:
15134	FP_TRACE(FP_NHEAD1(9, 0), "Unable to send %s event", subclass);
15135}
15136
15137
15138static void
15139fp_log_target_event(fc_local_port_t *port, char *subclass, la_wwn_t tgt_pwwn,
15140    uint32_t port_id)
15141{
15142	nvlist_t *attr_list;
15143
15144	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
15145	    KM_SLEEP) != DDI_SUCCESS) {
15146		goto alloc_failed;
15147	}
15148
15149	if (nvlist_add_uint32(attr_list, "instance",
15150	    port->fp_instance) != DDI_SUCCESS) {
15151		goto error;
15152	}
15153
15154	if (nvlist_add_byte_array(attr_list, "port-wwn",
15155	    port->fp_service_params.nport_ww_name.raw_wwn,
15156	    sizeof (la_wwn_t)) != DDI_SUCCESS) {
15157		goto error;
15158	}
15159
15160	if (nvlist_add_byte_array(attr_list, "target-port-wwn",
15161	    tgt_pwwn.raw_wwn, sizeof (la_wwn_t)) != DDI_SUCCESS) {
15162		goto error;
15163	}
15164
15165	if (nvlist_add_uint32(attr_list, "target-port-id",
15166	    port_id) != DDI_SUCCESS) {
15167		goto error;
15168	}
15169
15170	(void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW, EC_SUNFC,
15171	    subclass, attr_list, NULL, DDI_SLEEP);
15172
15173	nvlist_free(attr_list);
15174	return;
15175
15176error:
15177	nvlist_free(attr_list);
15178alloc_failed:
15179	FP_TRACE(FP_NHEAD1(9, 0), "Unable to send %s event", subclass);
15180}
15181
15182static uint32_t
15183fp_map_remote_port_state(uint32_t rm_state)
15184{
15185	switch (rm_state) {
15186		case PORT_DEVICE_LOGGED_IN:
15187			return (FC_HBA_PORTSTATE_ONLINE);
15188		case PORT_DEVICE_VALID:
15189		case PORT_DEVICE_INVALID:
15190		default:
15191			return (FC_HBA_PORTSTATE_UNKNOWN);
15192	}
15193}
15194