xref: /illumos-gate/usr/src/uts/common/io/ib/ibnex/ibnex_ioctl.c (revision b07ce584f4e28873b8927d7f83d9d3275a0f3ed2)
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 
26 /*
27  * This file contains support required for IB cfgadm plugin.
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/stat.h>
32 #include <sys/modctl.h>
33 #include <sys/ib/mgt/ibdm/ibdm_impl.h>
34 #include <sys/ib/ibnex/ibnex.h>
35 #include <sys/ib/ibnex/ibnex_devctl.h>
36 #include <sys/ib/ibtl/impl/ibtl_ibnex.h>
37 #include <sys/file.h>
38 #include <sys/sunndi.h>
39 #include <sys/fs/dv_node.h>
40 #include <sys/mdi_impldefs.h>
41 #include <sys/sunmdi.h>
42 
43 /*
44  * function prototypes
45  */
46 int			ibnex_open(dev_t *, int, int, cred_t *);
47 int			ibnex_close(dev_t, int, int, cred_t *);
48 int			ibnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
49 int			ibnex_offline_childdip(dev_info_t *);
50 static int		ibnex_get_num_devices(void);
51 static int		ibnex_get_snapshot(char **, size_t *, int);
52 static int		ibnex_get_commsvcnode_snapshot(nvlist_t **, ib_guid_t,
53 			    ib_guid_t, int, ib_pkey_t, ibnex_node_type_t);
54 static int		ibnex_fill_ioc_tmp(nvlist_t **, ibdm_ioc_info_t *);
55 static int		ibnex_fill_nodeinfo(nvlist_t **, ibnex_node_data_t *,
56 			    void *);
57 static void		ibnex_figure_ap_devstate(dev_info_t *,
58 			    devctl_ap_state_t *);
59 static void		ibnex_figure_ib_apid_devstate(devctl_ap_state_t *);
60 static	char 		*ibnex_get_apid(struct devctl_iocdata *);
61 static int		ibnex_get_dip_from_apid(char *, dev_info_t **,
62 			    ibnex_node_data_t **);
63 static ibnex_rval_t	ibnex_handle_pseudo_configure(char *);
64 static ibnex_rval_t	ibnex_handle_ioc_configure(char *);
65 static ibnex_rval_t	ibnex_handle_commsvcnode_configure(char *);
66 static void		ibnex_return_apid(dev_info_t *, char **);
67 static void		ibnex_port_conf_entry_add(char *);
68 static void		ibnex_vppa_conf_entry_add(char *);
69 static void		ibnex_hcasvc_conf_entry_add(char *);
70 static int		ibnex_port_conf_entry_delete(char *, char *);
71 static int		ibnex_vppa_conf_entry_delete(char *, char *);
72 static int		ibnex_hcasvc_conf_entry_delete(char *, char *);
73 
74 static ibnex_rval_t	ibnex_ioc_fininode(dev_info_t *, ibnex_ioc_node_t *);
75 static ibnex_rval_t	ibnex_commsvc_fininode(dev_info_t *);
76 static ibnex_rval_t	ibnex_pseudo_fininode(dev_info_t *);
77 
78 extern uint64_t		ibnex_str2hex(char *, int, int *);
79 extern int		ibnex_ioc_initnode_all_pi(ibdm_ioc_info_t *);
80 extern dev_info_t	*ibnex_commsvc_initnode(dev_info_t *,
81 			    ibdm_port_attr_t *, int, int, ib_pkey_t, int *,
82 			    int);
83 extern int		ibnex_get_dip_from_guid(ib_guid_t, int,
84 			    ib_pkey_t, dev_info_t **);
85 extern void		ibnex_reprobe_ioc_dev(void *arg);
86 extern void		ibnex_reprobe_ioc_all();
87 extern int		ibnex_pseudo_create_all_pi(ibnex_node_data_t *);
88 extern void		ibnex_pseudo_initnodes(void);
89 
90 extern ibnex_t	ibnex;
91 
92 /*
93  * ibnex_open()
94  */
95 /* ARGSUSED */
96 int
97 ibnex_open(dev_t *dev, int flag, int otyp, cred_t *credp)
98 {
99 	IBTF_DPRINTF_L4("ibnex", "\topen");
100 	return (0);
101 }
102 
103 
104 /*
105  * ibnex_close()
106  */
107 /* ARGSUSED */
108 int
109 ibnex_close(dev_t dev, int flag, int otyp, cred_t *credp)
110 {
111 	IBTF_DPRINTF_L4("ibnex", "\tclose");
112 	return (0);
113 }
114 
115 
116 /*
117  * ibnex_ioctl()
118  *	Ioctl routine for cfgadm controls
119  *	DEVCTL_AP_GETSTATE:	returns attachment point state
120  *	DEVCTL_AP_CONTROL:	Does "ibnex" specific ioctls listed below
121  *		IBNEX_NUM_DEVICE_NODES	Gives how many device nodes exist?
122  *		IBNEX_NUM_HCA_NODES	Gives how many HCAs exist in the fabric
123  *		IBNEX_UPDATE_PKEY_TBLS	"-x update_pkey_tbls"
124  *		IBNEX_GET_SNAPSHOT	Gets the "snapshot" back to user-land
125  *		IBNEX_SNAPSHOT_SIZE	What is "snapshot" size
126  *		IBNEX_DEVICE_PATH_SZ	What is device-path size
127  *		IBNEX_GET_DEVICE_PATH	Gets the device path for Dynamic ap
128  *		IBNEX_HCA_LIST_SZ	"-x list" option size for the HCA ap_id
129  *		IBNEX_HCA_LIST_INFO	"-x list" option info for the HCA ap_id
130  *		IBNEX_UNCFG_CLNTS_SZ	"-x unconfig_client option size"
131  *		IBNEX_UNCFG_CLNTS_INFO	"-x unconfig_client data"
132  *		IBNEX_CONF_ENTRY_ADD:	"-x add_service"
133  *		IBNEX_CONF_ENTRY_DEL:	"-x delete_service"
134  *		IBNEX_HCA_VERBOSE_SZ:	"-alv hca_apid data size"
135  *		IBNEX_HCA_VERBOSE_INFO: "-alv hca_apid actual data"
136  *		IBNEX_UPDATE_IOC_CONF	"-x update_ioc_conf"
137  *	DEVCTL_AP_CONFIGURE:	"configure" the attachment point
138  *	DEVCTL_AP_UNCONFIGURE:	"unconfigure" the attachment point
139  */
140 /* ARGSUSED */
141 int
142 ibnex_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
143     int *rvalp)
144 {
145 	int			ret, rv = 0, ioc_reprobe_pending = 0;
146 	int			circ;
147 	char			*snapshot = NULL;
148 	char			*apid_n = NULL;
149 	char			*service = NULL;
150 	char			*devnm = NULL;
151 	char			*msg;
152 	char			*guid_str;
153 	uint_t			num_hcas = 0;
154 	size_t			snapshot_sz  = 0;
155 	uint32_t		ssiz;
156 	uint32_t		apid_len;
157 	ib_guid_t		hca_guid;
158 	boolean_t		apid_alloced = B_FALSE;
159 	dev_info_t		*apid_dip = NULL;
160 	dev_info_t		*pdip;
161 	ibnex_rval_t		ret_val;
162 	ib_service_type_t	svc_type = IB_NONE;
163 	devctl_ap_state_t	ap_state;
164 	ibnex_node_data_t	*nodep, *scanp;
165 	struct devctl_iocdata	*dcp = NULL;
166 
167 	IBTF_DPRINTF_L4("ibnex", "\tioctl: cmd=%x, arg=%p, mode=%x, cred=%p, "
168 	    "\t\trval=%p dev=0x%x", cmd, arg, mode, credp, rvalp, dev);
169 
170 	/* read devctl ioctl data */
171 	if ((cmd != DEVCTL_AP_CONTROL) &&
172 	    (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
173 		IBTF_DPRINTF_L4("ibnex",
174 		    "\tioctl: ndi_dc_allochdl failed\n");
175 		return (EFAULT);
176 	}
177 
178 	mutex_enter(&ibnex.ibnex_mutex);
179 	switch (cmd) {
180 	case DEVCTL_AP_GETSTATE:
181 		msg = "\tioctl: DEVCTL_AP_GETSTATE";
182 		IBTF_DPRINTF_L4("ibnex", "%s:", msg);
183 
184 		apid_n = ibnex_get_apid(dcp);
185 		if (*apid_n == '\0') {
186 			IBTF_DPRINTF_L2("ibnex",
187 			    "%s: ibnex_get_apid failed", msg);
188 			rv = EIO;
189 			break;
190 		}
191 
192 		if (strncmp(apid_n, IBNEX_FABRIC, strlen(IBNEX_FABRIC)) == 0) {
193 			ibnex_figure_ib_apid_devstate(&ap_state);
194 			apid_dip = ibnex.ibnex_dip;
195 		} else {
196 			/* if this apid is already seen by IBNEX, get the dip */
197 			rv = ibnex_get_dip_from_apid(apid_n, &apid_dip, &nodep);
198 			if (rv != IBNEX_DYN_APID) {
199 				IBTF_DPRINTF_L2("ibnex",
200 				    "%s: ibnex_get_dip_from_apid failed", msg);
201 				rv = EIO;
202 				break;
203 			}
204 			if (apid_dip)
205 				ndi_rele_devi(apid_dip);
206 			/* rv could be something undesirable, so reset it */
207 			rv = 0;
208 
209 			ibnex_figure_ap_devstate(apid_dip, &ap_state);
210 		}
211 
212 		/* copy the return-AP-state information to the user space */
213 		if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
214 			IBTF_DPRINTF_L2("ibnex",
215 			    "%s: ndi_dc_return_ap_state failed", msg);
216 			rv = EFAULT;
217 		}
218 		break;
219 
220 	case DEVCTL_AP_CONTROL:
221 	{
222 		int			num_nodes = 0;
223 		ibnex_ioctl_data_t	ioc;	/* for 64-bit copies only */
224 
225 		msg = "\tioctl: DEVCTL_AP_CONTROL";
226 #ifdef	_MULTI_DATAMODEL
227 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
228 			ibnex_ioctl_data_32_t ioc32;
229 
230 			if (ddi_copyin((void *)arg, &ioc32,
231 			    sizeof (ioc32), mode) != 0) {
232 				IBTF_DPRINTF_L2("ibnex",
233 				    "%s: ddi_copyin err 1", msg);
234 				rv = EFAULT;
235 				break;
236 			}
237 			ioc.cmd		= (uint_t)ioc32.cmd;
238 			ioc.buf		= (caddr_t)(uintptr_t)ioc32.buf;
239 			ioc.bufsiz	= (uint_t)ioc32.bufsiz;
240 			ioc.ap_id	= (caddr_t)(uintptr_t)ioc32.ap_id;
241 			ioc.ap_id_len	= (uint_t)ioc32.ap_id_len;
242 			ioc.misc_arg	= (uint_t)ioc32.misc_arg;
243 		}
244 #else
245 		if (ddi_copyin((void *)arg, &ioc, sizeof (ioc),
246 		    mode) != 0) {
247 			IBTF_DPRINTF_L2("ibnex",
248 			    "%s: ddi_copyin 2 failed", msg);
249 			rv = EFAULT;
250 			break;
251 		}
252 #endif	/* _MULTI_DATAMODEL */
253 
254 		IBTF_DPRINTF_L4("ibnex", "%s: \n\tioc: cmd=%x buf=%p, "
255 		    "bufsiz=%d", msg, ioc.cmd, ioc.buf, ioc.bufsiz);
256 
257 		/*
258 		 * figure out ap_id name as passed from user-land
259 		 * NOTE: We don't need to figure out ap_id for these
260 		 * two sub-commands:-
261 		 *	IBNEX_NUM_DEVICE_NODES, IBNEX_NUM_HCA_NODES
262 		 *
263 		 * Hence, In user-land, these two ioctls force "ap_id_len" to 0.
264 		 */
265 		if (ioc.ap_id_len > 0) {
266 			apid_alloced = B_TRUE;
267 			apid_len = ioc.ap_id_len + 1;
268 			apid_n = kmem_zalloc(apid_len, KM_SLEEP);
269 			if (ddi_copyin((void *)ioc.ap_id, apid_n,
270 			    ioc.ap_id_len, mode) != 0) {
271 				IBTF_DPRINTF_L2("ibnex",
272 				    "%s: ddi_copyin err 3", msg);
273 				rv = EFAULT;
274 				break;
275 			}
276 
277 			IBTF_DPRINTF_L3("ibnex", "%s: apid_n= %s", msg, apid_n);
278 		}
279 
280 
281 		/* process sub-commands */
282 		switch (ioc.cmd) {
283 		case IBNEX_NUM_DEVICE_NODES:
284 			msg = "\tioctl: DEVCTL_AP_CONTROL: NUM_DEVICE_NODES";
285 
286 			/*
287 			 * figure out how many IOC, VPPA,
288 			 * Pseudo and Port nodes are present
289 			 */
290 			num_nodes = ibnex_get_num_devices();
291 			IBTF_DPRINTF_L4("ibnex", "%s: num_nodes = %d",
292 			    msg, num_nodes);
293 
294 			if (ddi_copyout(&num_nodes, ioc.buf,
295 			    ioc.bufsiz, mode) != 0) {
296 				IBTF_DPRINTF_L2("ibnex", "%s: copyout", msg);
297 				rv = EIO;
298 			}
299 			mutex_exit(&ibnex.ibnex_mutex);
300 			return (rv);
301 
302 		case IBNEX_NUM_HCA_NODES:
303 			msg = "\tioctl: DEVCTL_AP_CONTROL: NUM_HCA_NODES";
304 
305 			/* figure out how many HCAs are present in the host */
306 			mutex_exit(&ibnex.ibnex_mutex);
307 			num_hcas = ibt_get_hca_list(NULL);
308 			IBTF_DPRINTF_L4("ibnex", "%s: num %d", msg, num_hcas);
309 
310 			if (ddi_copyout(&num_hcas, ioc.buf,
311 			    ioc.bufsiz, mode) != 0) {
312 				IBTF_DPRINTF_L2("ibnex", "%s: copyout", msg);
313 				rv = EIO;
314 			}
315 			return (rv);
316 
317 		case IBNEX_UPDATE_PKEY_TBLS:
318 			msg = "\tioctl: DEVCTL_AP_CONTROL: UPDATE_PKEY_TBLS";
319 			IBTF_DPRINTF_L4("ibnex", "%s", msg);
320 
321 			/*
322 			 * update P_Key tables:
323 			 *	ibdm_ibnex_update_pkey_tbls() calls
324 			 *	ibt_query_hca_ports_byguids() for all the
325 			 *	HCAs that the IBDM has "seen" in the system.
326 			 *	This ends up updating the IBTL P_Key database.
327 			 *	NOTE: Changes in this area will break this
328 			 *	assumption. Initially the plan was to call
329 			 *	ibt_query_hca_ports_byguids() in IBTL but
330 			 *	IBDM needs to call it as well. So, eliminating
331 			 *	the first invocation.
332 			 *
333 			 *	It next updates the DM P_Key database.
334 			 *	Note that the DM P_Key database updating
335 			 *	will always be driven through cfgadm.
336 			 */
337 			mutex_exit(&ibnex.ibnex_mutex);
338 			ibdm_ibnex_update_pkey_tbls();
339 			mutex_enter(&ibnex.ibnex_mutex);
340 			break;
341 
342 		case IBNEX_GET_SNAPSHOT:
343 		case IBNEX_SNAPSHOT_SIZE:
344 			msg = (ioc.cmd == IBNEX_SNAPSHOT_SIZE) ?
345 			    "\tioctl: DEVCTL_AP_CONTROL: IBNEX_SNAPSHOT_SIZE" :
346 			    "\tioctl: DEVCTL_AP_CONTROL: IBNEX_GET_SNAPSHOT";
347 
348 			IBTF_DPRINTF_L4("ibnex", "%s:", msg);
349 
350 			if (ibnex_get_snapshot(&snapshot, &snapshot_sz,
351 			    ioc.misc_arg) != 0) {
352 				IBTF_DPRINTF_L2("ibnex",
353 				    "%s:\n\tibnex_get_snapshot failed", msg);
354 				rv = EIO;
355 				break;
356 			}
357 
358 			/* ssiz needs to be reinitialized again */
359 			ssiz = snapshot_sz;
360 			IBTF_DPRINTF_L4("ibnex",
361 			    "%s:\n\tsize =%x", msg, snapshot_sz);
362 
363 			if (ioc.cmd == IBNEX_SNAPSHOT_SIZE) {
364 				if (ddi_copyout(&ssiz, ioc.buf,
365 				    ioc.bufsiz, mode) != 0) {
366 					IBTF_DPRINTF_L2("ibnex",
367 					    "%s:\n\tddi_copyout 2 failed", msg);
368 					rv = EFAULT;
369 				}
370 
371 			} else {
372 				if (ioc.bufsiz != snapshot_sz) {
373 					IBTF_DPRINTF_L2("ibnex",
374 					    "%s:\n\tinvalid buffer size (%x %x)"
375 					    " ", msg, ioc.bufsiz, snapshot_sz);
376 					rv = EINVAL;
377 
378 				} else if (ddi_copyout(snapshot, ioc.buf,
379 				    ioc.bufsiz, mode) != 0) {
380 					IBTF_DPRINTF_L2("ibnex",
381 					    "%s:\n\tddi_copyout 3 failed", msg);
382 					rv = EFAULT;
383 				}
384 			}
385 
386 			kmem_free(snapshot, snapshot_sz);
387 			break;
388 
389 		case IBNEX_DEVICE_PATH_SZ:
390 		case IBNEX_GET_DEVICE_PATH:
391 		{
392 			char	 path[MAXPATHLEN];
393 
394 			msg = (ioc.cmd == IBNEX_DEVICE_PATH_SZ) ?
395 			    "\tioctl:DEVCTL_AP_CONTROL: IBNEX_DEVICE_PATH_SZ" :
396 			    "\tioctl:DEVCTL_AP_CONTROL: IBNEX_GET_DEVICE_PATH";
397 
398 			IBTF_DPRINTF_L4("ibnex", "%s: apid = %s", msg, apid_n);
399 
400 			/* if this apid is already seen by IBNEX, get the dip */
401 			rv = ibnex_get_dip_from_apid(apid_n, &apid_dip, &nodep);
402 			if (rv != IBNEX_DYN_APID || apid_dip == NULL) {
403 				IBTF_DPRINTF_L2("ibnex",
404 				    "%s:\n\tget_dip_from_apid failed", msg);
405 				rv = EIO;
406 				break;
407 			}
408 			ndi_rele_devi(apid_dip);
409 
410 			/* ddi_pathname doesn't supply /devices, so we do. */
411 			(void) strcpy(path, "/devices");
412 			(void) ddi_pathname(apid_dip, path + strlen(path));
413 			ssiz = (uint32_t)strlen(path) + 1;
414 			IBTF_DPRINTF_L4("ibnex",
415 			    "%s: len = %x\n\tpath = %s", msg, ssiz, path);
416 
417 			/* rv could be something undesirable, so reset it */
418 			rv = 0;
419 
420 			if (ioc.cmd == IBNEX_DEVICE_PATH_SZ) {
421 				if (ddi_copyout(&ssiz, ioc.buf,
422 				    ioc.bufsiz, mode) != 0) {
423 					IBTF_DPRINTF_L2("ibnex",
424 					    "%s: ddi_copyout 4 failed", msg);
425 					rv = EFAULT;
426 				}
427 
428 			} else {
429 				if (ioc.bufsiz != ssiz) {
430 					IBTF_DPRINTF_L2("ibnex",
431 					    "%s: invalid size (%x, %x)",
432 					    msg, ioc.bufsiz, ssiz);
433 					rv = EINVAL;
434 				} else if (ddi_copyout(&path, ioc.buf,
435 				    ioc.bufsiz, mode) != 0) {
436 					IBTF_DPRINTF_L2("ibnex", "%s "
437 					    "ddi_copyout 5 failed", msg);
438 					rv = EFAULT;
439 				}
440 			}
441 			break;
442 		}
443 
444 		case IBNEX_HCA_LIST_SZ:
445 		case IBNEX_HCA_LIST_INFO:
446 			msg = (ioc.cmd == IBNEX_HCA_LIST_SZ) ?
447 			    "DEVCTL_AP_CONTROL: IBNEX_HCA_LIST_SZ" :
448 			    "DEVCTL_AP_CONTROL: IBNEX_HCA_LIST_INFO";
449 
450 			guid_str = strrchr(apid_n, ':') + 1;
451 			IBTF_DPRINTF_L4("ibnex", "%s, input apid = %s, "
452 			    "guid = %s", msg, apid_n, guid_str);
453 
454 			if (guid_str == NULL) {
455 				IBTF_DPRINTF_L2("ibnex", "%s: invalid input "
456 				    "GUID passed %s", msg, guid_str);
457 				rv = EFAULT;
458 				break;
459 			}
460 
461 			/* Get the GUID(hex value) from apid_n */
462 			hca_guid = ibnex_str2hex(guid_str, strlen(guid_str),
463 			    &ret);
464 			if (ret != IBNEX_SUCCESS) {
465 				IBTF_DPRINTF_L2("ibnex", "%s: Invalid HCA "
466 				    "GUID string", msg);
467 				rv = EIO;
468 				break;
469 			}
470 			IBTF_DPRINTF_L4("ibnex", "%s HCA GUID = %llX",
471 			    msg, hca_guid);
472 			if (ibtl_ibnex_get_hca_info(hca_guid,
473 			    IBTL_IBNEX_LIST_CLNTS_FLAG, &snapshot, &snapshot_sz,
474 			    ibnex_return_apid) != IBT_SUCCESS) {
475 				IBTF_DPRINTF_L2("ibnex",
476 				    "%s: get HCA consumers failed", msg);
477 				rv = EIO;
478 				break;
479 			}
480 
481 			ssiz = snapshot_sz;
482 			IBTF_DPRINTF_L4("ibnex", "%s: size =%x", msg, ssiz);
483 
484 			if (ioc.cmd == IBNEX_HCA_LIST_SZ) {
485 				if (ddi_copyout(&ssiz, ioc.buf,
486 				    ioc.bufsiz, mode) != 0) {
487 					IBTF_DPRINTF_L2("ibnex",
488 					    "%s: ddi_copyout 6 failed", msg);
489 					rv = EFAULT;
490 				}
491 			} else {
492 				if (ioc.bufsiz != ssiz) {
493 					IBTF_DPRINTF_L2("ibnex", "%s: invalid "
494 					    "size (%x, %x)", msg, ioc.bufsiz,
495 					    ssiz);
496 					rv = EINVAL;
497 				} else if (ddi_copyout(snapshot, ioc.buf,
498 				    ioc.bufsiz, mode) != 0) {
499 					IBTF_DPRINTF_L2("ibnex", "%s "
500 					    "ddi_copyout 7 failed", msg);
501 					rv = EFAULT;
502 				}
503 			}
504 
505 			kmem_free(snapshot, snapshot_sz);
506 			break;
507 
508 		case IBNEX_UNCFG_CLNTS_SZ:
509 		case IBNEX_UNCFG_CLNTS_INFO:
510 			msg = (ioc.cmd == IBNEX_UNCFG_CLNTS_SZ) ?
511 			    "\tioctl:DEVCTL_AP_CONTROL: IBNEX_UNCFG_CLNTS_SZ" :
512 			    "\tioctl:DEVCTL_AP_CONTROL: IBNEX_UNCFG_CLNTS_INFO";
513 
514 			guid_str = strrchr(apid_n, ':') + 1;
515 			IBTF_DPRINTF_L4("ibnex", "%s, apid = %s, guid = %s",
516 			    msg, apid_n, guid_str);
517 
518 			if (guid_str == NULL) {
519 				IBTF_DPRINTF_L2("ibnex", "%s: invalid input "
520 				    "GUID %s", msg, guid_str);
521 				rv = EFAULT;
522 				break;
523 			}
524 
525 			/* Get the GUID(hex value) from apid_n */
526 			hca_guid = ibnex_str2hex(guid_str, strlen(guid_str),
527 			    &ret);
528 			if (ret != IBNEX_SUCCESS) {
529 				IBTF_DPRINTF_L2("ibnex", "%s: Invalid HCA "
530 				    "GUID string passed", msg);
531 				rv = EIO;
532 				break;
533 			}
534 			IBTF_DPRINTF_L4("ibnex", "%s G = %llX", msg, hca_guid);
535 			if (ibtl_ibnex_get_hca_info(hca_guid,
536 			    IBTL_IBNEX_UNCFG_CLNTS_FLAG, &snapshot,
537 			    &snapshot_sz, ibnex_return_apid) != IBT_SUCCESS) {
538 				IBTF_DPRINTF_L2("ibnex",
539 				    "%s: get HCA consumers failed", msg);
540 				rv = EIO;
541 				break;
542 			}
543 			/* ssiz needs to be reinitialized again */
544 			ssiz = snapshot_sz;
545 
546 			IBTF_DPRINTF_L4("ibnex", "%s: size =%x", msg, ssiz);
547 
548 			if (ioc.cmd == IBNEX_UNCFG_CLNTS_SZ) {
549 				if (ddi_copyout(&ssiz, ioc.buf,
550 				    ioc.bufsiz, mode) != 0) {
551 					IBTF_DPRINTF_L2("ibnex",
552 					    "%s: ddi_copyout 9 failed", msg);
553 					rv = EFAULT;
554 				}
555 
556 			} else {
557 				if (ioc.bufsiz != ssiz) {
558 					IBTF_DPRINTF_L2("ibnex",
559 					    "%s: invalid size (%x, %x)",
560 					    msg, ioc.bufsiz, ssiz);
561 					rv = EINVAL;
562 				} else if (ddi_copyout(snapshot, ioc.buf,
563 				    ioc.bufsiz, mode) != 0) {
564 					IBTF_DPRINTF_L2("ibnex", "%s "
565 					    "ddi_copyout 10 failed", msg);
566 					rv = EFAULT;
567 				}
568 			}
569 
570 			kmem_free(snapshot, snapshot_sz);
571 			break;
572 
573 		case IBNEX_CONF_ENTRY_ADD:
574 			msg = "\tioctl: IBNEX_CONF_ENTRY_ADD: ";
575 			service = kmem_zalloc(ioc.bufsiz + 1, KM_SLEEP);
576 			/* read in the "service" name */
577 			if (ddi_copyin(ioc.buf, service,
578 			    ioc.bufsiz, mode) != 0) {
579 				IBTF_DPRINTF_L2("ibnex", "%s: ddi_copyin err 6",
580 				    msg);
581 				rv = EFAULT;
582 				break;
583 			}
584 
585 			/* read in the "service type" */
586 			svc_type = ioc.misc_arg;
587 			IBTF_DPRINTF_L4("ibnex", "%s: service = %s, type = %d",
588 			    msg, service, svc_type);
589 
590 			if (svc_type == IB_PORT_SERVICE) {
591 				ibnex_port_conf_entry_add(service);
592 			} else if (svc_type == IB_VPPA_SERVICE) {
593 				ibnex_vppa_conf_entry_add(service);
594 			} else if (svc_type == IB_HCASVC_SERVICE) {
595 				ibnex_hcasvc_conf_entry_add(service);
596 			}
597 			kmem_free(service, ioc.bufsiz + 1);
598 			break;
599 
600 		case IBNEX_CONF_ENTRY_DEL:
601 			msg = "\tioctl:IBNEX_CONF_ENTRY_DEL: ";
602 			service = kmem_zalloc(ioc.bufsiz + 1, KM_SLEEP);
603 			/* read in the "service" name */
604 			if (ddi_copyin(ioc.buf, service,
605 			    ioc.bufsiz, mode) != 0) {
606 				IBTF_DPRINTF_L2("ibnex", "%s: ddi_copyin err 7",
607 				    msg);
608 				rv = EFAULT;
609 				break;
610 			}
611 
612 			/* read in the "service type" */
613 			svc_type = ioc.misc_arg;
614 			IBTF_DPRINTF_L4("ibnex", "%s: service = %s, type = %d",
615 			    msg, service, svc_type);
616 
617 			if (svc_type == IB_PORT_SERVICE) {
618 				rv = ibnex_port_conf_entry_delete(msg, service);
619 			} else if (svc_type == IB_VPPA_SERVICE) {
620 				rv = ibnex_vppa_conf_entry_delete(msg, service);
621 			} else if (svc_type == IB_HCASVC_SERVICE) {
622 				rv = ibnex_hcasvc_conf_entry_delete(msg,
623 				    service);
624 			}
625 			kmem_free(service, ioc.bufsiz + 1);
626 			break;
627 
628 		case IBNEX_HCA_VERBOSE_SZ:
629 		case IBNEX_HCA_VERBOSE_INFO:
630 			msg = (ioc.cmd == IBNEX_HCA_VERBOSE_SZ) ?
631 			    "DEVCTL_AP_CONTROL: IBNEX_HCA_VERBOSE_SZ" :
632 			    "DEVCTL_AP_CONTROL: IBNEX_HCA_VERBOSE_INFO";
633 
634 			guid_str = strrchr(apid_n, ':') + 1;
635 			IBTF_DPRINTF_L4("ibnex", "%s, apid = %s, guid = %s",
636 			    msg, apid_n, guid_str);
637 
638 			if (guid_str == NULL) {
639 				IBTF_DPRINTF_L2("ibnex", "%s: invalid GUID %s",
640 				    msg, guid_str);
641 				rv = EFAULT;
642 				break;
643 			}
644 
645 			/* Get the GUID(hex value) from apid_n */
646 			hca_guid = ibnex_str2hex(guid_str, strlen(guid_str),
647 			    &ret);
648 			if (ret != IBNEX_SUCCESS) {
649 				IBTF_DPRINTF_L2("ibnex", "%s: Invalid HCA GUID "
650 				    "string", msg);
651 				rv = EIO;
652 				break;
653 			}
654 			IBTF_DPRINTF_L4("ibnex", "%s HCA GUID = 0x%llX",
655 			    msg, hca_guid);
656 
657 			if (ibtl_ibnex_get_hca_verbose_data(hca_guid, &snapshot,
658 			    &snapshot_sz) != IBT_SUCCESS) {
659 				IBTF_DPRINTF_L2("ibnex", "%s: get HCA verbose "
660 				    "data failed", msg);
661 				rv = EIO;
662 				break;
663 			}
664 
665 			ssiz = snapshot_sz;
666 			IBTF_DPRINTF_L4("ibnex", "%s: size =%x", msg, ssiz);
667 
668 			if (ioc.cmd == IBNEX_HCA_VERBOSE_SZ) {
669 				if (ddi_copyout(&ssiz, ioc.buf,
670 				    ioc.bufsiz, mode) != 0) {
671 					IBTF_DPRINTF_L2("ibnex",
672 					    "%s: ddi_copyout 11 failed", msg);
673 					rv = EFAULT;
674 				}
675 			} else {
676 				if (ioc.bufsiz != ssiz) {
677 					IBTF_DPRINTF_L2("ibnex",
678 					    "%s: invalid size (%x, %x)",
679 					    msg, ioc.bufsiz, ssiz);
680 					rv = EINVAL;
681 				} else if (ddi_copyout(snapshot,
682 				    ioc.buf, ioc.bufsiz, mode) != 0) {
683 					IBTF_DPRINTF_L2("ibnex", "%s "
684 					    "ddi_copyout 12 failed", msg);
685 					rv = EFAULT;
686 				}
687 			}
688 
689 			kmem_free(snapshot, snapshot_sz);
690 			break;
691 
692 		case IBNEX_UPDATE_IOC_CONF :
693 			msg = "\tioctl:IBNEX_UPDATE_IOC_CONF: ";
694 
695 			/*
696 			 * If IB fabric APID, call ibnex_update_all
697 			 * If IOC APID, get the apid dip and call
698 			 * ibnex_update_ioc
699 			 */
700 			if (ioc.misc_arg == IBNEX_BASE_APID) {
701 				/*
702 				 * If reprobe is in progress or another reprobe
703 				 * is already waiting, wait.
704 				 */
705 				if (ibnex.ibnex_reprobe_state != 0) {
706 					if (ibnex.ibnex_reprobe_state ==
707 					    IBNEX_REPROBE_ALL_PROGRESS)
708 						ibnex.ibnex_reprobe_state =
709 						    IBNEX_REPROBE_ALL_WAIT;
710 					while (ibnex.ibnex_reprobe_state) {
711 						cv_wait(&ibnex.ibnex_reprobe_cv,
712 						    &ibnex.ibnex_mutex);
713 					}
714 
715 					/*
716 					 * Pending reprobe all completed, return
717 					 */
718 					break;
719 				}
720 
721 				/* Check if reprobe for any IOC is pending */
722 				/* CONSTCOND */
723 				while (1) {
724 					ioc_reprobe_pending = 0;
725 					for (scanp = ibnex.ibnex_ioc_node_head;
726 					    scanp;
727 					    scanp = scanp->node_next) {
728 						if (scanp->node_reprobe_state
729 						    != 0) {
730 							ioc_reprobe_pending =
731 							    1;
732 							break;
733 						}
734 					}
735 					if (ioc_reprobe_pending == 0) {
736 						ibnex.ibnex_reprobe_state &=
737 						    ~IBNEX_REPROBE_IOC_WAIT;
738 						break;
739 					}
740 
741 					ibnex.ibnex_reprobe_state =
742 					    IBNEX_REPROBE_IOC_WAIT;
743 					cv_wait(&ibnex.ibnex_reprobe_cv,
744 					    &ibnex.ibnex_mutex);
745 				}
746 
747 				/*
748 				 * Set the REPROBE_ALL_PROGRESS state &
749 				 * start reprobe
750 				 */
751 				ibnex.ibnex_reprobe_state =
752 				    IBNEX_REPROBE_ALL_PROGRESS;
753 				mutex_exit(&ibnex.ibnex_mutex);
754 				ibnex_reprobe_ioc_all();
755 				mutex_enter(&ibnex.ibnex_mutex);
756 			} else if (ioc.misc_arg == IBNEX_DYN_APID) {
757 				rv = ibnex_get_dip_from_apid(apid_n, &apid_dip,
758 				    &nodep);
759 				ASSERT(rv == IBNEX_DYN_APID);
760 
761 				/* Device unconfigured: return */
762 				if (apid_dip == NULL)
763 					break;
764 
765 				ndi_rele_devi(apid_dip);
766 				/* Reset return value back to 0 */
767 				rv = 0;
768 				if (ibnex.ibnex_reprobe_state != 0 ||
769 				    nodep->node_reprobe_state != 0) {
770 					while (ibnex.ibnex_reprobe_state != 0 &&
771 					    nodep->node_reprobe_state != 0) {
772 						cv_wait(&ibnex.ibnex_reprobe_cv,
773 						    &ibnex.ibnex_mutex);
774 					}
775 					/* Pending reprobe completed, return */
776 					break;
777 				}
778 
779 				/* Set node_reprobe_state and start reprobe */
780 				nodep->node_reprobe_state =
781 				    IBNEX_NODE_REPROBE_NOTIFY_ON_UPDATE;
782 				mutex_exit(&ibnex.ibnex_mutex);
783 				ibnex_reprobe_ioc_dev((void *)apid_dip);
784 				mutex_enter(&ibnex.ibnex_mutex);
785 			} else {
786 				rv = EINVAL;
787 			}
788 
789 			break;
790 
791 		default:
792 			IBTF_DPRINTF_L2("ibnex",
793 			    "DEVCTL_AP_CONTROL: ioc:unknown cmd = %x", ioc.cmd);
794 			break;
795 		}
796 	}
797 	break;
798 
799 	case DEVCTL_AP_UNCONFIGURE:
800 		msg = "DEVCTL_AP_UNCONFIGURE";
801 		IBTF_DPRINTF_L4("ibnex", "%s", msg);
802 
803 		/* Check for write permissions */
804 		if (!(mode & FWRITE)) {
805 			IBTF_DPRINTF_L2("ibnex", "%s: invalid mode %x",
806 			    msg, mode);
807 			rv = EPERM;
808 			break;
809 		}
810 
811 		if ((apid_n = ibnex_get_apid(dcp)) == '\0') {
812 			IBTF_DPRINTF_L2("ibnex",
813 			    "%s: ibnex_get_apid failed", msg);
814 			rv = EIO;
815 			break;
816 		}
817 
818 		/*
819 		 * If this apid is already seen by IBNEX, get the dip
820 		 * NOTE: ibnex_get_dip_from_apid() finds a valid dip
821 		 * and also does a ndi_devi_hold() on the child.
822 		 */
823 		rv = ibnex_get_dip_from_apid(apid_n, &apid_dip, &nodep);
824 		if ((rv != IBNEX_DYN_APID) || (apid_dip == NULL)) {
825 			IBTF_DPRINTF_L2("ibnex", "%s: get_dip_from_apid "
826 			    "failed with 0x%x", msg, rv);
827 			rv = EIO;
828 			break;
829 		}
830 		IBTF_DPRINTF_L4("ibnex", "%s: DIP = %p", msg, apid_dip);
831 
832 		/* Check if it is a valid node type? */
833 		if (!IBNEX_VALID_NODE_TYPE(nodep)) {
834 			IBTF_DPRINTF_L2("ibnex", "%s: invalid IB node", msg);
835 			rv = ENODEV;
836 			ndi_rele_devi(apid_dip);
837 			break;
838 		}
839 
840 		/*
841 		 * continue unconfigure operation, only if device node
842 		 * is already configured. Return EBUSY if another
843 		 * configure/unconfigure operation is in progress.
844 		 */
845 		if (nodep->node_state == IBNEX_CFGADM_CONFIGURING ||
846 		    nodep->node_state == IBNEX_CFGADM_UNCONFIGURING) {
847 			rv = EBUSY;
848 			ndi_rele_devi(apid_dip);
849 			break;
850 		}
851 
852 		/* do this before to avoid races */
853 		nodep->node_dip = NULL;
854 		nodep->node_state = IBNEX_CFGADM_UNCONFIGURING;
855 
856 		/*
857 		 * Call devfs_clean first
858 		 * NOTE: The code so far is protected by holding ibnex_mutex
859 		 * and by doing a ndi_devi_hold() on the child.
860 		 */
861 		pdip = ddi_get_parent(apid_dip);
862 		if (i_ddi_node_state(apid_dip) >= DS_INITIALIZED) {
863 			devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP);
864 			(void) ddi_deviname(apid_dip, devnm);
865 			mutex_exit(&ibnex.ibnex_mutex);
866 			(void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE);
867 			mutex_enter(&ibnex.ibnex_mutex);
868 			kmem_free(devnm, MAXNAMELEN + 1);
869 		}
870 
871 		mutex_exit(&ibnex.ibnex_mutex);
872 		ndi_devi_enter(pdip, &circ);
873 		ndi_rele_devi(apid_dip);
874 		mutex_enter(&ibnex.ibnex_mutex);
875 
876 		/* unconfigure the Port/VPPA/HCA_SVC node */
877 		if (IBNEX_COMMSVC_NODE_TYPE(nodep)) {
878 			ret_val = ibnex_commsvc_fininode(apid_dip);
879 		} else if (nodep->node_type == IBNEX_IOC_NODE) {
880 			/* unconfigure the IOC node */
881 			ret_val = ibnex_ioc_fininode(apid_dip,
882 			    &nodep->node_data.ioc_node);
883 		} else if (nodep->node_type == IBNEX_PSEUDO_NODE) {
884 			/* unconfigure the pseudo node */
885 			ret_val = ibnex_pseudo_fininode(apid_dip);
886 		}
887 
888 		/* reset upon failure */
889 		if (ret_val != IBNEX_SUCCESS) {
890 			nodep->node_dip = apid_dip;
891 			nodep->node_state = IBNEX_CFGADM_CONFIGURED;
892 		} else {
893 			nodep->node_state = IBNEX_CFGADM_UNCONFIGURED;
894 			nodep->node_ap_state = IBNEX_NODE_AP_UNCONFIGURED;
895 		}
896 
897 		rv = (ret_val != IBNEX_SUCCESS) ? EIO : 0;
898 		ndi_devi_exit(pdip, circ);
899 		IBTF_DPRINTF_L2("ibnex", "%s: DONE !! It %s", msg,
900 		    rv ? "failed" : "succeeded");
901 		break;
902 
903 	case DEVCTL_AP_CONFIGURE:
904 		msg = "DEVCTL_AP_CONFIGURE";
905 		IBTF_DPRINTF_L4("ibnex", "%s", msg);
906 		mutex_exit(&ibnex.ibnex_mutex);
907 		ndi_devi_enter(ibnex.ibnex_dip, &circ);
908 		mutex_enter(&ibnex.ibnex_mutex);
909 
910 		/* Check for write permissions */
911 		if (!(mode & FWRITE)) {
912 			IBTF_DPRINTF_L2("ibnex", "%s: invalid mode %x",
913 			    msg, mode);
914 			rv = EPERM;
915 			ndi_devi_exit(ibnex.ibnex_dip, circ);
916 			break;
917 		}
918 
919 		if ((apid_n = ibnex_get_apid(dcp)) == '\0') {
920 			IBTF_DPRINTF_L2("ibnex",
921 			    "%s: ibnex_get_apid failed", msg);
922 			rv = EIO;
923 			ndi_devi_exit(ibnex.ibnex_dip, circ);
924 			break;
925 		}
926 
927 		/*
928 		 * Let's get the node if it already exists.
929 		 * NOTE: ibnex_get_dip_from_apid() finds a valid dip
930 		 * and also does a ndi_devi_hold() on the child.
931 		 */
932 		nodep = NULL;
933 		ret_val = ibnex_get_dip_from_apid(apid_n, &apid_dip, &nodep);
934 		/*
935 		 * We need the node_data but not the dip. If we get a dip for
936 		 * this apid, it means it's already configured. We need to
937 		 * return.
938 		 */
939 		if (apid_dip != NULL) {
940 			ndi_rele_devi(apid_dip);
941 			ndi_devi_exit(ibnex.ibnex_dip, circ);
942 			rv = 0;
943 			break;
944 		}
945 
946 		/*
947 		 * A node exits for this apid but not a dip. So we must have
948 		 * unconfigured it earlier. Set the node_ap_state to configuring
949 		 * to allow configure operation.
950 		 */
951 		if (nodep != NULL) {
952 			nodep->node_ap_state = IBNEX_NODE_AP_CONFIGURING;
953 		}
954 
955 
956 		/*
957 		 * Five types of APIDs are supported:
958 		 *	o HCA_GUID,0,service-name	(HCA-SVC device)
959 		 *	o IOC_GUID 			(IOC device)
960 		 *	o PORT_GUID,0,service-name	(Port device)
961 		 *	o pseudo_name,unit-address, 	(Pseudo device)
962 		 *	o PORT_GUID,P_Key,service-name	(VPPA device)
963 		 * If the apid doesn't have "," then treat it as an IOC
964 		 * If the apid has one "," then it is Pseudo device
965 		 * If the apid has 2 ","s then it is one of the
966 		 * Port,VPPA,HCA_SVC devices
967 		 */
968 		if (strrchr(apid_n, ',') == NULL) {
969 			ret_val = ibnex_handle_ioc_configure(apid_n);
970 		} else {
971 			char *first = strchr(apid_n, ',');
972 			char *second;
973 
974 			second = first ? strchr(first + 1, ',') : NULL;
975 			if (first != NULL && second == NULL) {
976 				ret_val = ibnex_handle_pseudo_configure(apid_n);
977 			} else if (first != NULL && second != NULL) {
978 				ret_val = ibnex_handle_commsvcnode_configure(
979 				    apid_n);
980 			}
981 		} /* end of else */
982 
983 		if (ret_val != IBNEX_SUCCESS) {
984 			rv = (ret_val == IBNEX_BUSY) ? EBUSY : EIO;
985 		} else {
986 			/*
987 			 * Get the newly created node and set the state to
988 			 * IBNEX_NODE_AP_CONFIGURED.
989 			 * NOTE: ibnex_get_dip_from_apid() finds a valid dip
990 			 * and also does a ndi_devi_hold() on the child.
991 			 */
992 			if (!nodep)
993 				ret_val = ibnex_get_dip_from_apid(apid_n,
994 				    &apid_dip, &nodep);
995 			if (nodep != NULL) {
996 				nodep->node_ap_state = IBNEX_NODE_AP_CONFIGURED;
997 			}
998 			if (apid_dip != NULL) {
999 				ndi_rele_devi(apid_dip);
1000 			}
1001 		}
1002 		IBTF_DPRINTF_L2("ibnex", "%s: DONE !! It %s", msg,
1003 		    rv ? "failed" : "succeeded");
1004 		ndi_devi_exit(ibnex.ibnex_dip, circ);
1005 		break;
1006 
1007 	default:
1008 		rv = EIO;
1009 		break;
1010 	}
1011 	mutex_exit(&ibnex.ibnex_mutex);
1012 
1013 	if ((apid_alloced == B_TRUE) && (apid_n != NULL)) {
1014 		kmem_free(apid_n, apid_len);
1015 	}
1016 
1017 	if (dcp) {
1018 		ndi_dc_freehdl(dcp);
1019 	}
1020 	return (rv);
1021 }
1022 
1023 
1024 /*
1025  * ibnex_get_num_devices()
1026  *	Figure out how many IOC, VPPA, Pseudo, HCA_SVC and Port devices exist
1027  */
1028 static int
1029 ibnex_get_num_devices(void)
1030 {
1031 	int			j, k, l, hca_count;
1032 	int			num_nodes = 0;
1033 	ibdm_hca_list_t		*hca_list, *hcap;
1034 	ibdm_port_attr_t	*pattr;
1035 	ibnex_node_data_t	*nodep;
1036 
1037 	ASSERT(mutex_owned(&ibnex.ibnex_mutex));
1038 
1039 	/* Get a count of HCAs, first. */
1040 	mutex_exit(&ibnex.ibnex_mutex);
1041 	ibdm_ibnex_get_hca_list(&hca_list, &hca_count);
1042 	mutex_enter(&ibnex.ibnex_mutex);
1043 	for (hcap = hca_list; hca_list != NULL; hca_list = hca_list->hl_next) {
1044 		for (j = 0; j < ibnex.ibnex_nhcasvc_comm_svcs; j++)
1045 			num_nodes++;
1046 		for (j = 0; j < hca_list->hl_nports; j++) {
1047 			for (k = 0; k < ibnex.ibnex_num_comm_svcs; k++)
1048 				num_nodes++;
1049 
1050 			pattr = &hca_list->hl_port_attr[j];
1051 			for (k = 0; k < pattr->pa_npkeys; k++) {
1052 				if (IBNEX_INVALID_PKEY(pattr->pa_pkey_tbl[k].
1053 				    pt_pkey))
1054 					continue;
1055 
1056 				for (l = 0; l < ibnex.ibnex_nvppa_comm_svcs;
1057 				    l++, ++num_nodes)
1058 					;
1059 			} /* end of pa_npkeys */
1060 		} /* end of  hl_nports */
1061 	} /* end of hca_list != NULL */
1062 	if (hcap)
1063 		ibdm_ibnex_free_hca_list(hcap);
1064 
1065 	/*
1066 	 * Now figure out how many IOC nodes are present.
1067 	 * Add count of configured "diconnected" IOCs
1068 	 */
1069 	mutex_exit(&ibnex.ibnex_mutex);
1070 	num_nodes += ibdm_ibnex_get_ioc_count();
1071 	mutex_enter(&ibnex.ibnex_mutex);
1072 	num_nodes += ibnex.ibnex_num_disconnect_iocs;
1073 
1074 	/* Last: figure out how many Pseudo nodes are present. */
1075 	for (nodep = ibnex.ibnex_pseudo_node_head; nodep;
1076 	    nodep = nodep->node_next) {
1077 		if (nodep->node_data.pseudo_node.pseudo_merge_node == 1)
1078 			continue;
1079 
1080 		num_nodes++;
1081 	}
1082 	return (num_nodes);
1083 }
1084 
1085 
1086 /*
1087  * ibnex_get_snapshot()
1088  *	Get a snapshot of all Port/IOC/VPPA/HCA_SVC/Pseudo nodes
1089  *	Snapshot includes IBNEX_NODE_INFO_NVL, IBNEX_NODE_TYPE_NVL,
1090  *	IBNEX_NODE_RSTATE_NVL, IBNEX_NODE_OSTATE_NVL and
1091  *	IBNEX_NODE_COND_NVL
1092  */
1093 static int
1094 ibnex_get_snapshot(char **buf, size_t *sz, int allow_probe)
1095 {
1096 	int			i, j, k, l, hca_count;
1097 	nvlist_t		*nvl;
1098 	ib_pkey_t 		pkey;
1099 	boolean_t		found;
1100 	ibdm_ioc_info_t		*ioc_listp;
1101 	ibdm_ioc_info_t		*iocp;
1102 	ibdm_hca_list_t		*hca_list, *hcap;
1103 	ibdm_port_attr_t	*port_attr;
1104 	ibnex_node_data_t	*nodep;
1105 
1106 	ASSERT(mutex_owned(&ibnex.ibnex_mutex));
1107 
1108 	*buf = NULL;
1109 	*sz = 0;
1110 
1111 	if (!ibnex.ibnex_pseudo_inited) {
1112 		mutex_exit(&ibnex.ibnex_mutex);
1113 		ibnex_pseudo_initnodes();
1114 		mutex_enter(&ibnex.ibnex_mutex);
1115 		ibnex.ibnex_pseudo_inited = 1;
1116 	}
1117 
1118 	/* First, Port/VPPA/HCA_SVC nodes */
1119 	mutex_exit(&ibnex.ibnex_mutex);
1120 	ibdm_ibnex_get_hca_list(&hca_list, &hca_count);
1121 	mutex_enter(&ibnex.ibnex_mutex);
1122 
1123 	(void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1124 
1125 	/* Go thru all the ports of all the HCAs and all the port-svc indices */
1126 	for (hcap = hca_list, i = 0; i < hca_count;
1127 	    hca_list = hca_list->hl_next, i++) {
1128 
1129 		IBTF_DPRINTF_L4("ibnex", "ibnex_get_snapshot: "
1130 		    "fill in  COMM service HCA_SVC nodes");
1131 		port_attr = hca_list->hl_hca_port_attr;
1132 		for (j = 0; j < ibnex.ibnex_nhcasvc_comm_svcs; j++) {
1133 			if (ibnex_get_commsvcnode_snapshot(&nvl,
1134 			    port_attr->pa_hca_guid,
1135 			    port_attr->pa_hca_guid, j, (ib_pkey_t)0,
1136 			    IBNEX_HCASVC_COMMSVC_NODE) != 0) {
1137 				IBTF_DPRINTF_L2("ibnex",
1138 				    "ibnex_get_snapshot: failed to fill"
1139 				    " HCA_SVC device (%x %x)", i, j);
1140 				ibdm_ibnex_free_hca_list(hcap);
1141 				nvlist_free(nvl);
1142 				return (-1);
1143 			}
1144 
1145 		}
1146 
1147 		for (j = 0; j < hca_list->hl_nports; j++) {
1148 			port_attr = &hca_list->hl_port_attr[j];
1149 
1150 			IBTF_DPRINTF_L4("ibnex", "ibnex_get_snapshot: "
1151 			    "fill in  COMM service Port nodes");
1152 			for (k = 0; k < ibnex.ibnex_num_comm_svcs; k++) {
1153 
1154 				if (ibnex_get_commsvcnode_snapshot(&nvl,
1155 				    port_attr->pa_hca_guid,
1156 				    port_attr->pa_port_guid, k, (ib_pkey_t)0,
1157 				    IBNEX_PORT_COMMSVC_NODE) != 0) {
1158 					IBTF_DPRINTF_L2("ibnex",
1159 					    "ibnex_get_snapshot: failed to fill"
1160 					    " Port device (%x %x %x)", i, j, k);
1161 					ibdm_ibnex_free_hca_list(hcap);
1162 					nvlist_free(nvl);
1163 					return (-1);
1164 				}
1165 
1166 			} /* end of num_comm_svcs for loop */
1167 
1168 			IBTF_DPRINTF_L4("ibnex", "ibnex_get_snapshot: "
1169 			    "fill in  VPPA service port nodes");
1170 			for (l = 0; l < port_attr->pa_npkeys; l++) {
1171 				pkey = port_attr->pa_pkey_tbl[l].pt_pkey;
1172 				if (IBNEX_INVALID_PKEY(pkey))
1173 					continue;
1174 
1175 				for (k = 0; k < ibnex.ibnex_nvppa_comm_svcs;
1176 				    k++) {
1177 
1178 					if (ibnex_get_commsvcnode_snapshot(&nvl,
1179 					    port_attr->pa_hca_guid,
1180 					    port_attr->pa_port_guid, k, pkey,
1181 					    IBNEX_VPPA_COMMSVC_NODE) != 0) {
1182 						IBTF_DPRINTF_L2("ibnex",
1183 						    "ibnex_get_snapshot: "
1184 						    "failed to fill VPPA "
1185 						    "device (%x %x %x % x)",
1186 						    i, j, k, l);
1187 						ibdm_ibnex_free_hca_list(hcap);
1188 						nvlist_free(nvl);
1189 						return (-1);
1190 					}
1191 				} /* end of ibnex_nvppa_comm_svcs loop */
1192 
1193 			} /* end of pa_npkeys for loop */
1194 
1195 		} /* end of hl_nports for loop */
1196 
1197 	} /* end of hca_count for loop */
1198 
1199 	if (hcap)
1200 		ibdm_ibnex_free_hca_list(hcap);
1201 
1202 	/* save it to free up the entire list */
1203 	mutex_exit(&ibnex.ibnex_mutex);
1204 	iocp = ioc_listp = ibdm_ibnex_get_ioc_list(allow_probe);
1205 	mutex_enter(&ibnex.ibnex_mutex);
1206 	for (; ioc_listp != NULL; ioc_listp = ioc_listp->ioc_next) {
1207 
1208 		/*
1209 		 * Say we have N IOCs and all were deleted from ibnex
1210 		 * but not from IBDM
1211 		 */
1212 		if (ibnex.ibnex_ioc_node_head == NULL) {
1213 			if (ibnex_fill_ioc_tmp(&nvl, ioc_listp) != 0) {
1214 				IBTF_DPRINTF_L2("ibnex", "ibnex_get_snapshot: "
1215 				    "filling NVL data failed");
1216 				ibdm_ibnex_free_ioc_list(iocp);
1217 				nvlist_free(nvl);
1218 				return (-1);
1219 			}
1220 			continue;
1221 
1222 		} else {
1223 			found = B_FALSE;
1224 
1225 			/* Check first, if we have already seen this IOC? */
1226 			for (nodep = ibnex.ibnex_ioc_node_head; nodep != NULL;
1227 			    nodep = nodep->node_next) {
1228 				if (ioc_listp->ioc_profile.ioc_guid ==
1229 				    nodep->node_data.ioc_node.ioc_guid) {
1230 					found = B_TRUE;
1231 					break;
1232 				}
1233 			}
1234 
1235 
1236 			/* have we seen this IOC before? */
1237 			if (found == B_TRUE) {
1238 				if (ibnex_fill_nodeinfo(&nvl, nodep,
1239 				    &ioc_listp->ioc_profile) != 0) {
1240 					IBTF_DPRINTF_L2("ibnex",
1241 					    "ibnex_get_snapshot: filling NVL "
1242 					    "for IOC node %p failed", nodep);
1243 					ibdm_ibnex_free_ioc_list(iocp);
1244 					nvlist_free(nvl);
1245 					return (-1);
1246 				}
1247 
1248 			} else {
1249 
1250 				if (ibnex_fill_ioc_tmp(&nvl, ioc_listp) != 0) {
1251 					IBTF_DPRINTF_L2("ibnex",
1252 					    "ibnex_get_snapshot: filling NVL "
1253 					    "tmp for IOC node %p failed",
1254 					    ioc_listp);
1255 					ibdm_ibnex_free_ioc_list(iocp);
1256 					nvlist_free(nvl);
1257 					return (-1);
1258 				}
1259 			}
1260 
1261 		} /* end of else ibnex_ioc_node_head == NULL */
1262 	} /* end of external for */
1263 
1264 	ibdm_ibnex_free_ioc_list(iocp);
1265 
1266 	/*
1267 	 * Add list of "disconnected" IOCs, not unconfigured.
1268 	 */
1269 	for (nodep = ibnex.ibnex_ioc_node_head; nodep != NULL;
1270 	    nodep = nodep->node_next) {
1271 		if (nodep->node_data.ioc_node.ioc_ngids == 0 &&
1272 		    nodep->node_data.ioc_node.ioc_profile != NULL &&
1273 		    nodep->node_state != IBNEX_CFGADM_UNCONFIGURED) {
1274 			if (ibnex_fill_nodeinfo(&nvl, nodep,
1275 			    nodep->node_data.ioc_node.ioc_profile) != 0) {
1276 					IBTF_DPRINTF_L2("ibnex",
1277 					    "ibnex_get_snapshot: filling NVL "
1278 					    "for disconnected IOC node %p "
1279 					    "failed", nodep);
1280 					nvlist_free(nvl);
1281 					return (-1);
1282 			}
1283 		}
1284 	}
1285 
1286 	/* lastly; pseudo nodes */
1287 	for (nodep = ibnex.ibnex_pseudo_node_head; nodep;
1288 	    nodep = nodep->node_next) {
1289 		if (nodep->node_data.pseudo_node.pseudo_merge_node == 1)
1290 			continue;
1291 		if (ibnex_fill_nodeinfo(&nvl, nodep, NULL) != 0) {
1292 			IBTF_DPRINTF_L2("ibnex", "ibnex_get_snapshot: "
1293 			    "filling NVL data for Pseudo %p failed", nodep);
1294 			nvlist_free(nvl);
1295 			return (-1);
1296 		}
1297 	}
1298 
1299 	/* pack the data into the buffer */
1300 	if (nvlist_pack(nvl, buf, sz, NV_ENCODE_NATIVE, KM_SLEEP)) {
1301 		IBTF_DPRINTF_L2("ibnex",
1302 		    "ibnex_get_snapshot: nvlist_pack failed");
1303 		nvlist_free(nvl);
1304 		return (-1);
1305 	}
1306 
1307 	IBTF_DPRINTF_L4("ibnex", "ibnex_get_snapshot: size = 0x%x", *sz);
1308 	nvlist_free(nvl);
1309 	return (0);
1310 }
1311 
1312 
1313 /*
1314  * ibnex_get_commsvcnode_snapshot()
1315  *	A utility function to fill in a "dummy" Port/VPPA/HCA_SVC
1316  *	information. Cfgadm plugin will display all Port/VPPA/
1317  *	HCA_SVCs seen even if they are not all configured by IBNEX.
1318  *
1319  *	This function uses information from IBDM to fill up Port/VPPA/
1320  *	HCA_SVC snapshot. If none exists then it makes up a "temporary"
1321  *	node which will be displayed as "connected/unconfigured/unknown".
1322  *
1323  *	For HCA_SVC node port_guid will be same as hca_guid.
1324  */
1325 static int
1326 ibnex_get_commsvcnode_snapshot(nvlist_t **nvlpp, ib_guid_t hca_guid,
1327     ib_guid_t port_guid, int svc_index, ib_pkey_t p_key,
1328     ibnex_node_type_t node_type)
1329 {
1330 	int			rval;
1331 	dev_info_t		*dip = NULL;
1332 	ibnex_node_data_t	*nodep;
1333 	ibnex_node_data_t	dummy;
1334 	ibnex_node_data_t	*tmp = &dummy;
1335 
1336 	IBTF_DPRINTF_L4("ibnex", "ibnex_get_commsvcnode_snapshot: "
1337 	    "HCA GUID: %llX Port GUID: %llX svc_index = %x pkey = %x "
1338 	    "node_type = %x", hca_guid, port_guid, svc_index, p_key, node_type);
1339 
1340 	/* check if this node was seen before? */
1341 	rval = ibnex_get_dip_from_guid(port_guid, svc_index, p_key, &dip);
1342 	if (rval == IBNEX_SUCCESS && dip) {
1343 		nodep = ddi_get_parent_data(dip);
1344 
1345 		if (ibnex_fill_nodeinfo(nvlpp, nodep, NULL) != 0) {
1346 			IBTF_DPRINTF_L2("ibnex",
1347 			    "ibnex_get_commsvcnode_snapshot: failed to fill "
1348 			    "Port/VPPA device node %p NVL data", nodep);
1349 			return (-1);
1350 		}
1351 
1352 	} else {
1353 		/* Fake up a Port/VPPA/HCA_SVC node */
1354 		IBTF_DPRINTF_L4("ibnex", "ibnex_get_commsvcnode_snapshot: "
1355 		    "VPPA/Port/HCA_SVC not seen by ibnex");
1356 		bzero(tmp, sizeof (ibnex_node_data_t));
1357 		tmp->node_type = node_type;
1358 		tmp->node_data.port_node.port_guid = port_guid;
1359 		tmp->node_data.port_node.port_hcaguid = hca_guid;
1360 		tmp->node_data.port_node.port_commsvc_idx = svc_index;
1361 		/* Fill P_Key only for VPPA nodes */
1362 		if (node_type == IBNEX_VPPA_COMMSVC_NODE) {
1363 			tmp->node_data.port_node.port_pkey = p_key;
1364 		}
1365 
1366 		if (ibnex_fill_nodeinfo(nvlpp, tmp, NULL) != 0) {
1367 			IBTF_DPRINTF_L2("ibnex",
1368 			    "ibnex_get_commsvcnode_snapshot: failed to fill "
1369 			    "tmp Port/VPPA device node %p NVL data", tmp);
1370 			return (-1);
1371 		}
1372 	}
1373 
1374 	return (0);
1375 }
1376 
1377 
1378 /*
1379  * ibnex_fill_ioc_tmp()
1380  *	A utility function to fill in a "dummy" IOC information.
1381  *	Cfgadm plugin will display all IOCs seen by IBDM even if they
1382  *	are configured or not by IBNEX.
1383  *
1384  *	This function uses information from IBDM to fill up a
1385  *	dummy IOC information. It will be displayed as
1386  *	"connected/unconfigured/unknown".
1387  */
1388 static int
1389 ibnex_fill_ioc_tmp(nvlist_t **nvlpp, ibdm_ioc_info_t *ioc_listp)
1390 {
1391 	ibnex_node_data_t	dummy;
1392 	ibnex_node_data_t	*nodep = &dummy;
1393 
1394 	IBTF_DPRINTF_L4("ibnex", "\tibnex_fill_ioc_tmp:");
1395 
1396 	bzero(nodep, sizeof (ibnex_node_data_t));
1397 	nodep->node_type = IBNEX_IOC_NODE;
1398 	nodep->node_data.ioc_node.ioc_guid = ioc_listp->ioc_profile.ioc_guid;
1399 	nodep->node_data.ioc_node.iou_guid = ioc_listp->ioc_iou_guid;
1400 	(void) strncpy(nodep->node_data.ioc_node.ioc_id_string,
1401 	    (char *)ioc_listp->ioc_profile.ioc_id_string,
1402 	    IB_DM_IOC_ID_STRING_LEN);
1403 	IBTF_DPRINTF_L4("ibnex", "\tibnex_fill_ioc_tmp: %s",
1404 	    nodep->node_data.ioc_node.ioc_id_string);
1405 
1406 	if (ibnex_fill_nodeinfo(nvlpp, nodep, &ioc_listp->ioc_profile) != 0) {
1407 		IBTF_DPRINTF_L2("ibnex", "\tibnex_fill_ioc_tmp: filling NVL "
1408 		    "data for IOC node %p failed", nodep);
1409 		return (-1);
1410 	}
1411 
1412 	return (0);
1413 }
1414 
1415 
1416 /*
1417  * ibnex_fill_nodeinfo()
1418  *	A utility function to fill in to the NVLIST information about
1419  *	a Port/IOC/VPPA/HCA_SVC/Pseudo driver that is then passed over
1420  *	to cfgadm utility for display. This information is used only
1421  *	for cfgadm -ll displays.
1422  *
1423  *	Information that is filled in here is:-
1424  *		AP_ID_NAME
1425  *		AP_ID_INFO
1426  *		AP_ID_TYPE
1427  *		AP_ID_OCCUPANT_STATE
1428  *		AP_ID_RECEPTACLE_STATE
1429  *		AP_ID_CONDITION
1430  */
1431 static int
1432 ibnex_fill_nodeinfo(nvlist_t **nvlpp, ibnex_node_data_t *node_datap, void *tmp)
1433 {
1434 	char			*svcname;
1435 	char			*node_name;
1436 	char			apid[IBTL_IBNEX_APID_LEN];
1437 	char			info_data[MAXNAMELEN];
1438 	ib_dm_ioc_ctrl_profile_t *profilep;
1439 	devctl_ap_state_t	state;
1440 
1441 	IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: 0x%x addr is %p",
1442 	    node_datap->node_type, node_datap);
1443 
1444 	if (node_datap->node_type == IBNEX_PORT_COMMSVC_NODE) {
1445 		svcname = ibnex.ibnex_comm_svc_names[node_datap->node_data.
1446 		    port_node.port_commsvc_idx];
1447 		(void) snprintf(apid, IBTL_IBNEX_APID_LEN, "%llX,0,%s",
1448 		    (longlong_t)node_datap->node_data.port_node.port_guid,
1449 		    svcname);
1450 
1451 		/* Node APID */
1452 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_APID_NVL, apid)) {
1453 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1454 			    "failed to fill %s", IBNEX_NODE_APID_NVL);
1455 			return (-1);
1456 		}
1457 
1458 		/* Node Info */
1459 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_INFO_NVL, svcname)) {
1460 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1461 			    "failed to fill Port %s", IBNEX_NODE_INFO_NVL);
1462 			return (-1);
1463 		}
1464 
1465 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: "
1466 		    "Port %s = %s, %s = %s",
1467 		    IBNEX_NODE_INFO_NVL, apid, IBNEX_NODE_APID_NVL, svcname);
1468 
1469 	} else if (node_datap->node_type == IBNEX_VPPA_COMMSVC_NODE) {
1470 		svcname = ibnex.ibnex_vppa_comm_svc_names[node_datap->node_data.
1471 		    port_node.port_commsvc_idx];
1472 		(void) snprintf(apid, IBTL_IBNEX_APID_LEN, "%llX,%x,%s",
1473 		    (longlong_t)node_datap->node_data.port_node.port_guid,
1474 		    node_datap->node_data.port_node.port_pkey, svcname);
1475 
1476 		/* Node APID */
1477 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_APID_NVL, apid)) {
1478 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1479 			    "failed to fill %s", IBNEX_NODE_APID_NVL);
1480 			return (-1);
1481 		}
1482 
1483 		/* Node Info */
1484 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_INFO_NVL, svcname)) {
1485 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1486 			    "failed to fill VPPA %s", IBNEX_NODE_INFO_NVL);
1487 			return (-1);
1488 		}
1489 
1490 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: "
1491 		    "VPPA %s = %s, %s = %s",
1492 		    IBNEX_NODE_APID_NVL, apid, IBNEX_NODE_INFO_NVL, svcname);
1493 
1494 	} else if (node_datap->node_type == IBNEX_HCASVC_COMMSVC_NODE) {
1495 		svcname = ibnex.ibnex_hcasvc_comm_svc_names[node_datap->
1496 		    node_data.port_node.port_commsvc_idx];
1497 		(void) snprintf(apid, IBTL_IBNEX_APID_LEN, "%llX,0,%s",
1498 		    (longlong_t)node_datap->node_data.port_node.port_guid,
1499 		    svcname);
1500 
1501 		/* Node APID */
1502 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_APID_NVL, apid)) {
1503 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1504 			    "failed to fill %s", IBNEX_NODE_APID_NVL);
1505 			return (-1);
1506 		}
1507 
1508 		/* Node Info */
1509 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_INFO_NVL, svcname)) {
1510 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1511 			    "failed to fill Port %s", IBNEX_NODE_INFO_NVL);
1512 			return (-1);
1513 		}
1514 
1515 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: "
1516 		    "Port %s = %s, %s = %s",
1517 		    IBNEX_NODE_INFO_NVL, apid, IBNEX_NODE_APID_NVL, svcname);
1518 
1519 	} else if (node_datap->node_type == IBNEX_IOC_NODE) {
1520 
1521 		/*
1522 		 * get the IOC profile pointer from the args
1523 		 */
1524 		profilep = (ib_dm_ioc_ctrl_profile_t *)tmp;
1525 		IBNEX_FORM_GUID(apid, IBTL_IBNEX_APID_LEN, profilep->ioc_guid);
1526 
1527 		/* Node APID */
1528 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_APID_NVL, apid)) {
1529 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1530 			    "failed to fill in %s", IBNEX_NODE_APID_NVL);
1531 			return (-1);
1532 		}
1533 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s %s",
1534 		    IBNEX_NODE_APID_NVL, apid);
1535 
1536 		/*
1537 		 * IOC "info" filed will display the following fields
1538 		 * VendorID, IOCDeviceID, DeviceVersion, SubsystemVendorID,
1539 		 * SubsystemID, Class, Subclass, Protocol, ProtocolVersion
1540 		 */
1541 		(void) snprintf(info_data, MAXNAMELEN,
1542 		    "VID: 0x%x DEVID: 0x%x VER: 0x%x SUBSYS_VID: 0x%x "
1543 		    "SUBSYS_ID: 0x%x CLASS: 0x%x SUBCLASS: 0x%x PROTO: 0x%x "
1544 		    "PROTOVER: 0x%x ID_STRING: %s", profilep->ioc_vendorid,
1545 		    profilep->ioc_deviceid, profilep->ioc_device_ver,
1546 		    profilep->ioc_subsys_vendorid, profilep->ioc_subsys_id,
1547 		    profilep->ioc_io_class, profilep->ioc_io_subclass,
1548 		    profilep->ioc_protocol, profilep->ioc_protocol_ver,
1549 		    (char *)profilep->ioc_id_string);
1550 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s", info_data);
1551 
1552 		/* Node Info */
1553 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_INFO_NVL, info_data)) {
1554 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1555 			    "failed to fill IOC %s", IBNEX_NODE_INFO_NVL);
1556 			return (-1);
1557 		}
1558 
1559 	} else if (node_datap->node_type == IBNEX_PSEUDO_NODE) {
1560 		(void) snprintf(apid, IBTL_IBNEX_APID_LEN, "%s",
1561 		    node_datap->node_data.pseudo_node.pseudo_node_addr);
1562 
1563 		/* Node APID */
1564 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_APID_NVL, apid)) {
1565 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1566 			    "failed to fill in %s", IBNEX_NODE_APID_NVL);
1567 			return (-1);
1568 		}
1569 
1570 		/* Node Info */
1571 		node_name = node_datap->node_data.pseudo_node.pseudo_devi_name;
1572 		(void) snprintf(info_data, MAXNAMELEN,
1573 		    "Pseudo Driver = \"%s\", Unit-address = \"%s\"",
1574 		    node_name, apid + strlen(node_name) + 1);
1575 		if (nvlist_add_string(*nvlpp, IBNEX_NODE_INFO_NVL, info_data)) {
1576 			IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1577 			    "failed to fill Pseudo %s", IBNEX_NODE_INFO_NVL);
1578 			return (-1);
1579 		}
1580 
1581 		IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: Pseudo %s = %s,"
1582 		    "%s = %s", IBNEX_NODE_APID_NVL, apid, IBNEX_NODE_INFO_NVL,
1583 		    info_data);
1584 	}
1585 
1586 	/* Node type */
1587 	if (nvlist_add_int32(*nvlpp, IBNEX_NODE_TYPE_NVL,
1588 	    node_datap->node_type)) {
1589 		IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1590 		    "failed to fill in %s", IBNEX_NODE_TYPE_NVL);
1591 		return (-1);
1592 	}
1593 	IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s %d",
1594 	    IBNEX_NODE_TYPE_NVL, node_datap->node_type);
1595 
1596 	/* figure out "ostate", "rstate" and "condition" */
1597 	ibnex_figure_ap_devstate(node_datap->node_dip, &state);
1598 
1599 	if (nvlist_add_int32(*nvlpp, IBNEX_NODE_RSTATE_NVL, state.ap_rstate)) {
1600 		IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1601 		    "failed to fill in %s", IBNEX_NODE_RSTATE_NVL);
1602 		return (-1);
1603 	}
1604 	IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s %d",
1605 	    IBNEX_NODE_RSTATE_NVL, state.ap_rstate);
1606 
1607 	if (nvlist_add_int32(*nvlpp, IBNEX_NODE_OSTATE_NVL, state.ap_ostate)) {
1608 		IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1609 		    "failed to fill in %s", IBNEX_NODE_OSTATE_NVL);
1610 		return (-1);
1611 	}
1612 	IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s %d",
1613 	    IBNEX_NODE_OSTATE_NVL, state.ap_ostate);
1614 
1615 	if (nvlist_add_int32(*nvlpp, IBNEX_NODE_COND_NVL, state.ap_condition)) {
1616 		IBTF_DPRINTF_L2("ibnex", "ibnex_fill_nodeinfo: "
1617 		    "failed to fill in %s", IBNEX_NODE_COND_NVL);
1618 		return (-1);
1619 	}
1620 	IBTF_DPRINTF_L5("ibnex", "ibnex_fill_nodeinfo: %s %d",
1621 	    IBNEX_NODE_COND_NVL, state.ap_condition);
1622 
1623 	return (0);
1624 }
1625 
1626 
1627 /*
1628  * ibnex_figure_ap_devstate()
1629  *	Fills the "devctl_ap_state_t" for a given ap_id
1630  *
1631  *	currently it assumes that we don't support "error_code" and
1632  *	"last_change" value.
1633  */
1634 static void
1635 ibnex_figure_ap_devstate(dev_info_t *dip, devctl_ap_state_t *ap_state)
1636 {
1637 	IBTF_DPRINTF_L5("ibnex", "ibnex_figure_ap_devstate: dip = %p", dip);
1638 
1639 	ap_state->ap_rstate = AP_RSTATE_CONNECTED;
1640 	if (dip == NULL) {	/* for nodes not seen by IBNEX yet */
1641 		ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
1642 		ap_state->ap_condition = AP_COND_UNKNOWN;
1643 	} else {
1644 		if (i_ddi_node_state(dip) < DS_BOUND) {
1645 			ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
1646 			ap_state->ap_condition = AP_COND_UNKNOWN;
1647 		} else {
1648 			ap_state->ap_ostate = AP_OSTATE_CONFIGURED;
1649 			ap_state->ap_condition = AP_COND_OK;
1650 		}
1651 	}
1652 	ap_state->ap_last_change = (time_t)-1;
1653 	ap_state->ap_error_code = 0;
1654 	ap_state->ap_in_transition = 0;
1655 }
1656 
1657 
1658 /*
1659  * ibnex_figure_ib_apid_devstate()
1660  *	Fills the "devctl_ap_state_t" for a IB static ap_id
1661  */
1662 static void
1663 ibnex_figure_ib_apid_devstate(devctl_ap_state_t *ap_state)
1664 {
1665 	ap_state->ap_rstate = AP_RSTATE_CONNECTED;
1666 	ap_state->ap_condition = AP_COND_OK;
1667 	ap_state->ap_ostate = (ibt_get_hca_list(NULL) == 0) ?
1668 	    AP_OSTATE_UNCONFIGURED : AP_OSTATE_CONFIGURED;
1669 	ap_state->ap_last_change = (time_t)-1;
1670 	ap_state->ap_error_code = 0;
1671 	ap_state->ap_in_transition = 0;
1672 }
1673 
1674 
1675 /*
1676  * ibnex_get_apid()
1677  *	Reads in the ap_id passed as an nvlist_string from user-land
1678  */
1679 static char *
1680 ibnex_get_apid(struct devctl_iocdata *dcp)
1681 {
1682 	char *ap_id;
1683 
1684 	ASSERT(mutex_owned(&ibnex.ibnex_mutex));
1685 
1686 	/* Get which ap_id to operate on.  */
1687 	if (nvlist_lookup_string(ndi_dc_get_ap_data(dcp), "apid",
1688 	    &ap_id) != 0) {
1689 		IBTF_DPRINTF_L4("ibnex", "ibnex_get_apid: ap_id lookup failed");
1690 		ap_id = NULL;
1691 	}
1692 
1693 	IBTF_DPRINTF_L4("ibnex", "ibnex_get_apid: ap_id=%s", ap_id);
1694 	return (ap_id);
1695 }
1696 
1697 
1698 /*
1699  * ibnex_get_dip_from_apid()
1700  *	Figures out the dip/node_data from an ap_id given that this ap_id
1701  *	exists as a "name" in the "ibnex" list
1702  *
1703  * NOTE: ap_id was on stack earlier and gets manipulated here. Since this
1704  * function may be called twice; it is better to make a local copy of
1705  * ap_id; if the ap_id were to be reused.
1706  */
1707 static int
1708 ibnex_get_dip_from_apid(char *apid, dev_info_t **ret_dip,
1709     ibnex_node_data_t **ret_node_datap)
1710 {
1711 	int			rv, ret;
1712 	int			index;
1713 	int			len = strlen((char *)apid) + 1;
1714 	char			*dyn;
1715 	char			*ap_id;
1716 	char			*first;
1717 	char			*second = NULL;
1718 	char			*node_addr;
1719 	char			name[100];
1720 	ibnex_node_data_t	*nodep = NULL;
1721 
1722 	ap_id = i_ddi_strdup(apid, KM_SLEEP);
1723 	IBTF_DPRINTF_L4("ibnex", "\tibnex_get_dip_from_apid: %s", ap_id);
1724 	ASSERT(mutex_owned(&ibnex.ibnex_mutex));
1725 
1726 	if ((dyn = GET_DYN(ap_id)) != NULL) {
1727 		rv = IBNEX_DYN_APID;
1728 	} else {	/* either static, hca or unknown */
1729 		*ret_dip = NULL;
1730 		if (strstr(ap_id, "hca") != 0) {
1731 			rv = IBNEX_HCA_APID;
1732 		} else if (strstr(ap_id, IBNEX_FABRIC) != 0) {
1733 			rv = IBNEX_BASE_APID;
1734 		} else {
1735 			rv = IBNEX_UNKNOWN_APID;
1736 		}
1737 		kmem_free(ap_id, len);
1738 		return (rv);
1739 	}
1740 
1741 	dyn += strlen(DYN_SEP);
1742 	if (*dyn == '\0') {
1743 		*ret_dip = NULL;
1744 		kmem_free(ap_id, len);
1745 		return (IBNEX_UNKNOWN_APID);
1746 	}
1747 
1748 	/* APID */
1749 	first = strchr(dyn, ',');
1750 	if (first != NULL)
1751 		second = strchr(first+1, ',');
1752 
1753 	/* Implies Port or VPPA or HCA_SVC Driver ap_id */
1754 	if (first != NULL && second != NULL) {
1755 		int	str_len;
1756 		int	pkey_val = 0;
1757 		char	*pkey_str = strchr(ap_id, ',');
1758 		char	*svc_str = strrchr(pkey_str, ',');
1759 
1760 		/* dyn contains ,GUID,p_key,svc_name. Change it to GUID */
1761 		str_len = strlen(dyn) - strlen(pkey_str);
1762 		dyn[str_len] = '\0';
1763 		IBTF_DPRINTF_L4("ibnex", "\tibnex_get_dip_from_apid: "
1764 		    "Port / Node Guid %s", dyn);
1765 
1766 		/* figure out comm or vppa. figure out pkey  */
1767 		++pkey_str; /* pkey_str used to point to ",p_key,svc_name" */
1768 
1769 		/* pkey_str contains p_key,svc_name. Change it to p_key */
1770 		str_len = strlen(pkey_str) - strlen(svc_str);
1771 		pkey_str[str_len] = '\0';
1772 
1773 		/* convert the string P_KEY to hex value */
1774 		pkey_val = ibnex_str2hex(pkey_str, strlen(pkey_str), &ret);
1775 		if (ret != IBNEX_SUCCESS) {
1776 			*ret_dip = NULL;
1777 			kmem_free(ap_id, len);
1778 			return (IBNEX_UNKNOWN_APID);
1779 		}
1780 
1781 		++svc_str;	/* svc_str used to point to ",svc_name" */
1782 		IBTF_DPRINTF_L5("ibnex", "\tibnex_get_dip_from_apid: pkey %s"
1783 		    ":%x service name = %s", pkey_str, pkey_val, svc_str);
1784 
1785 		for (nodep = ibnex.ibnex_port_node_head;
1786 		    nodep != NULL; nodep = nodep->node_next) {
1787 			index = nodep->node_data.port_node.port_commsvc_idx;
1788 			IBNEX_FORM_GUID(name, IBTL_IBNEX_APID_LEN,
1789 			    nodep->node_data.port_node.port_guid);
1790 
1791 			/*
1792 			 * Match P_Key, name string & service string:
1793 			 * For COMM / HCA_SVC services these should be true:
1794 			 *	P_Key matches to 0, svc_str in comm_svc_names[]
1795 			 *	and name matches the dynamic part of the ap_id
1796 			 * For VPPA services this should be true:
1797 			 *	P_Key != 0 & matches, svc_str in
1798 			 *	vppa_comm_svc_names[] and the name matches the
1799 			 *	dynamic part of the ap_id.
1800 			 */
1801 			if ((pkey_val == nodep->node_data.port_node.
1802 			    port_pkey) && (strstr(dyn, name) != NULL)) {
1803 
1804 				/* pkey != 0, COMM / HCA_SVC service */
1805 				if (((pkey_val == 0) && (
1806 					/* Port Service */
1807 				    ((ibnex.ibnex_comm_svc_names != NULL) &&
1808 				    (index < ibnex.ibnex_num_comm_svcs) &&
1809 				    (strstr(svc_str, ibnex.
1810 				    ibnex_comm_svc_names[index]) != NULL)) ||
1811 					/* HCA_SVC service */
1812 				    ((ibnex.ibnex_hcasvc_comm_svc_names !=
1813 				    NULL) && (index <
1814 				    ibnex.ibnex_nhcasvc_comm_svcs) &&
1815 				    (strstr(svc_str, ibnex.
1816 				    ibnex_hcasvc_comm_svc_names[index])
1817 				    != NULL)))) ||
1818 					/* next the VPPA strings */
1819 				    ((pkey_val != 0) && (strstr(svc_str, ibnex.
1820 				    ibnex_vppa_comm_svc_names[index]) !=
1821 				    NULL))) {
1822 					if (nodep->node_dip)
1823 						ndi_hold_devi(nodep->node_dip);
1824 					*ret_node_datap = nodep;
1825 					*ret_dip = nodep->node_dip;
1826 					kmem_free(ap_id, len);
1827 					return (rv);
1828 				}
1829 			}
1830 
1831 		} /* end of for */
1832 
1833 	} else if (first != NULL && second == NULL) {
1834 		/* pseudo ap_id */
1835 		for (nodep = ibnex.ibnex_pseudo_node_head; nodep;
1836 		    nodep = nodep->node_next) {
1837 			if (nodep->node_data.pseudo_node.pseudo_merge_node
1838 			    == 1)
1839 				continue;
1840 			node_addr = nodep->node_data.pseudo_node.
1841 			    pseudo_node_addr;
1842 			if (strncmp(dyn, node_addr, strlen(node_addr)) == 0) {
1843 				if (nodep->node_dip)
1844 					ndi_hold_devi(nodep->node_dip);
1845 				*ret_node_datap = nodep;
1846 				*ret_dip = nodep->node_dip;
1847 				kmem_free(ap_id, len);
1848 				return (rv);
1849 			}
1850 		}
1851 
1852 	} else if (first == NULL && second == NULL) {
1853 		/* This is an IOC ap_id */
1854 		for (nodep = ibnex.ibnex_ioc_node_head; nodep != NULL;
1855 		    nodep = nodep->node_next) {
1856 			IBNEX_FORM_GUID(name, IBTL_IBNEX_APID_LEN,
1857 			    nodep->node_data.ioc_node.ioc_guid);
1858 			if (strstr(dyn, name) != NULL) {
1859 				if (nodep->node_dip)
1860 					ndi_hold_devi(nodep->node_dip);
1861 				*ret_node_datap = nodep;
1862 				*ret_dip = nodep->node_dip;
1863 				kmem_free(ap_id, len);
1864 				return (rv);
1865 			}
1866 		}
1867 	}
1868 
1869 	/* Could not find a matching IB device */
1870 	*ret_dip = (nodep) ? nodep->node_dip : NULL;
1871 	kmem_free(ap_id, len);
1872 	return (rv);
1873 }
1874 
1875 
1876 /*
1877  * ibnex_handle_pseudo_configure()
1878  *	Do DEVCTL_AP_CONNECT processing for Pseudo devices only.
1879  *	The code also checks if the given ap_id is valid or not.
1880  */
1881 static ibnex_rval_t
1882 ibnex_handle_pseudo_configure(char *apid)
1883 {
1884 	char			*node_addr;
1885 	char			*last = strrchr(apid, ':') + 1;
1886 	ibnex_rval_t		retval = IBNEX_FAILURE;
1887 	ibnex_node_data_t	*nodep;
1888 
1889 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_pseudo_configure: "
1890 	    "last = %s\n\t\tapid = %s", last, apid);
1891 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
1892 
1893 	/* Check if the APID is valid first */
1894 	if (apid == NULL || last == NULL) {
1895 		IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_pseudo_configure: "
1896 		    "invalid apid %s", apid);
1897 		return (retval);
1898 	}
1899 
1900 	/* find the matching entry and configure it */
1901 	for (nodep = ibnex.ibnex_pseudo_node_head; nodep != NULL;
1902 	    nodep = nodep->node_next) {
1903 		if (nodep->node_data.pseudo_node.pseudo_merge_node == 1)
1904 			continue;
1905 		node_addr = nodep->node_data.pseudo_node.pseudo_node_addr;
1906 		if (strncmp(node_addr, last, strlen(last)))
1907 			continue;
1908 
1909 		if (nodep->node_dip != NULL) {
1910 			/*
1911 			 * Return BUSY if another configure
1912 			 * operation is in progress
1913 			 */
1914 			if (nodep->node_state ==
1915 			    IBNEX_CFGADM_CONFIGURING)
1916 				return (IBNEX_BUSY);
1917 			else
1918 				return (IBNEX_SUCCESS);
1919 		}
1920 
1921 		/*
1922 		 * Return BUSY if another unconfigure operation is
1923 		 * in progress
1924 		 */
1925 		if (nodep->node_state == IBNEX_CFGADM_UNCONFIGURING)
1926 			return (IBNEX_BUSY);
1927 
1928 		ASSERT(nodep->node_state != IBNEX_CFGADM_CONFIGURED);
1929 		nodep->node_state = IBNEX_CFGADM_CONFIGURING;
1930 
1931 		mutex_exit(&ibnex.ibnex_mutex);
1932 		retval = ibnex_pseudo_create_all_pi(nodep);
1933 		mutex_enter(&ibnex.ibnex_mutex);
1934 		if (retval == NDI_SUCCESS) {
1935 			nodep->node_state = IBNEX_CFGADM_CONFIGURED;
1936 			return (IBNEX_SUCCESS);
1937 		} else {
1938 			nodep->node_state = IBNEX_CFGADM_UNCONFIGURED;
1939 			return (IBNEX_FAILURE);
1940 		}
1941 	}
1942 
1943 	IBTF_DPRINTF_L4("ibnex", "\thandle_pseudo_configure: retval=%d",
1944 	    retval);
1945 	return (retval);
1946 }
1947 
1948 
1949 /*
1950  * ibnex_handle_ioc_configure()
1951  *	Do DEVCTL_AP_CONNECT processing for IOCs only.
1952  *	The code also checks if the given ap_id is valid or not.
1953  */
1954 static ibnex_rval_t
1955 ibnex_handle_ioc_configure(char *apid)
1956 {
1957 	int			ret;
1958 	char			*guid_str = strrchr(apid, ':') + 1;
1959 	ib_guid_t		ioc_guid;
1960 	ibnex_rval_t		retval = IBNEX_FAILURE;
1961 	ibdm_ioc_info_t		*ioc_info;
1962 
1963 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
1964 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_ioc_configure: %s", apid);
1965 
1966 	/* Check if the APID is valid first */
1967 	if (guid_str == NULL) {
1968 		IBTF_DPRINTF_L4("ibnex",
1969 		    "\tibnex_handle_ioc_configure: invalid apid %s", apid);
1970 		return (retval);
1971 	}
1972 
1973 	/*
1974 	 * Call into IBDM to get IOC information
1975 	 */
1976 	ioc_guid = ibnex_str2hex(guid_str, strlen(guid_str), &ret);
1977 	if (ret != IBNEX_SUCCESS)
1978 		return (ret);
1979 
1980 	IBTF_DPRINTF_L4("ibnex",
1981 	    "\tibnex_handle_ioc_configure: IOC GUID = %llX", ioc_guid);
1982 	mutex_exit(&ibnex.ibnex_mutex);
1983 	ioc_info = ibdm_ibnex_get_ioc_info(ioc_guid);
1984 	mutex_enter(&ibnex.ibnex_mutex);
1985 	if (ioc_info == NULL) {
1986 		IBTF_DPRINTF_L2("ibnex",
1987 		    "\tibnex_handle_ioc_configure: probe_iocguid failed");
1988 		return (retval);
1989 	}
1990 
1991 	retval = ibnex_ioc_initnode_all_pi(ioc_info);
1992 	ibdm_ibnex_free_ioc_list(ioc_info);
1993 
1994 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_ioc_configure: "
1995 	    "done retval = %d", retval);
1996 	return (retval);
1997 }
1998 
1999 
2000 /*
2001  * ibnex_handle_commsvcnode_configure()
2002  *	Do DEVCTL_AP_CONNECT processing
2003  *	This is done for Port/VPPA/HCA_SVC drivers Only.
2004  *	The code also checks if the given ap_id is valid or not.
2005  */
2006 static ibnex_rval_t
2007 ibnex_handle_commsvcnode_configure(char *apid)
2008 {
2009 	int			ret, str_len, circ;
2010 	int			sndx;
2011 	int			port_pkey = 0;
2012 	char			*pkey_str = strchr(apid, ',');
2013 	char			*guid_str = strrchr(apid, ':') + 1;
2014 	char			*svc_str = strrchr(pkey_str, ',');
2015 	boolean_t		found = B_FALSE;
2016 	boolean_t		is_hcasvc_node = B_FALSE;
2017 	ib_guid_t		guid;	/* Port / Node GUID */
2018 	dev_info_t		*parent;
2019 	ibnex_rval_t		retval = IBNEX_FAILURE;
2020 	ibdm_port_attr_t	*port_attr;
2021 	int			node_type;
2022 	ibdm_hca_list_t		*hca_list;
2023 
2024 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2025 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_commsvcnode_configure: %s",
2026 	    apid);
2027 
2028 	/* Check if the APID is valid first */
2029 	if (guid_str == NULL || ((guid_str != NULL) &&
2030 	    (pkey_str == NULL || svc_str == NULL))) {
2031 		IBTF_DPRINTF_L4("ibnex",
2032 		    "\tibnex_handle_commsvcnode_configure: "
2033 		    "invalid apid %s", apid);
2034 		return (retval);
2035 	}
2036 
2037 	/* guid_str contains GUID,p_key,svc_name. Change it to GUID */
2038 	str_len = strlen(guid_str) - strlen(pkey_str);
2039 	guid_str[str_len] = '\0';
2040 
2041 	/* convert the string GUID to hex value */
2042 	guid = ibnex_str2hex(guid_str, strlen(guid_str), &ret);
2043 	if (ret == IBNEX_FAILURE)
2044 		return (ret);
2045 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_commsvcnode_configure: "
2046 	    "Port / Node Guid %llX", guid);
2047 
2048 	/* figure out Port/HCA_SVC or VPPA. Also figure out the P_Key.  */
2049 	++pkey_str;	/* pkey_str used to point to ",p_key,svc_name" */
2050 
2051 	/* pkey_str contains p_key,svc_name. Change it to P_Key */
2052 	str_len = strlen(pkey_str) - strlen(svc_str);
2053 	pkey_str[str_len] = '\0';
2054 	IBTF_DPRINTF_L5("ibnex", "\tibnex_handle_commsvcnode_configure: "
2055 	    "p_key %s", pkey_str);
2056 
2057 	/* convert the string P_Key to a hexadecimal value */
2058 	port_pkey = ibnex_str2hex(pkey_str, strlen(pkey_str), &ret);
2059 	IBTF_DPRINTF_L5("ibnex", "\tibnex_handle_commsvcnode_configure: "
2060 	    "PKEY num %x", port_pkey);
2061 	if (ret == IBNEX_FAILURE)
2062 		return (ret);
2063 
2064 	++svc_str;	/* svc_str used to point to ",svc_name" */
2065 
2066 	/* find the service index */
2067 	if (port_pkey == 0) {
2068 		/* PORT Devices */
2069 		for (sndx = 0; sndx < ibnex.ibnex_num_comm_svcs; sndx++) {
2070 			if (strncmp(ibnex.ibnex_comm_svc_names[sndx],
2071 			    svc_str, strlen(svc_str)) == 0) {
2072 				found = B_TRUE;
2073 				break;
2074 			}
2075 		}
2076 
2077 		/* HCA_SVC Devices */
2078 		if (found == B_FALSE) {
2079 			for (sndx = 0; sndx < ibnex.ibnex_nhcasvc_comm_svcs;
2080 			    sndx++) {
2081 				if (strncmp(ibnex.ibnex_hcasvc_comm_svc_names
2082 				    [sndx], svc_str, strlen(svc_str)) == 0) {
2083 					found = B_TRUE;
2084 					is_hcasvc_node = B_TRUE;
2085 					break;
2086 				}
2087 			}
2088 		}
2089 
2090 	} else {
2091 		for (sndx = 0; sndx < ibnex.ibnex_nvppa_comm_svcs; sndx++) {
2092 			if (strncmp(ibnex.ibnex_vppa_comm_svc_names[sndx],
2093 			    svc_str, strlen(svc_str)) == 0) {
2094 				found = B_TRUE;
2095 				break;
2096 			}
2097 		}
2098 	}
2099 
2100 	if (found == B_FALSE) {
2101 		IBTF_DPRINTF_L2("ibnex",
2102 		    "\tibnex_handle_commsvcnode_configure: "
2103 		    "invalid service %s", svc_str);
2104 		return (retval);
2105 	}
2106 
2107 	/* get Port attributes structure */
2108 	mutex_exit(&ibnex.ibnex_mutex);
2109 	if (is_hcasvc_node == B_FALSE) {
2110 		port_attr = ibdm_ibnex_get_port_attrs(guid);
2111 		if (port_attr == NULL) {
2112 			IBTF_DPRINTF_L2("ibnex",
2113 			    "\tibnex_handle_commsvcnode_configure: "
2114 			    "ibdm_ibnex_get_port_attrs failed");
2115 			mutex_enter(&ibnex.ibnex_mutex);
2116 			return (retval);
2117 		}
2118 	} else {
2119 		hca_list = ibdm_ibnex_get_hca_info_by_guid(guid);
2120 		if (hca_list == NULL) {
2121 			IBTF_DPRINTF_L2("ibnex",
2122 			    "\tibnex_handle_commsvcnode_configure: "
2123 			    "ibdm_ibnex_get_hca_info_by_guid failed");
2124 			mutex_enter(&ibnex.ibnex_mutex);
2125 			return (retval);
2126 		}
2127 		port_attr = hca_list->hl_hca_port_attr;
2128 	}
2129 
2130 	/* get HCA's dip */
2131 	parent = ibtl_ibnex_hcaguid2dip(port_attr->pa_hca_guid);
2132 
2133 	if (parent == NULL) {
2134 		IBTF_DPRINTF_L2("ibnex",
2135 		    "\tibnex_handle_commsvcnode_configure: "
2136 		    "no HCA present");
2137 		mutex_enter(&ibnex.ibnex_mutex);
2138 		if (is_hcasvc_node == B_FALSE)
2139 			ibdm_ibnex_free_port_attr(port_attr);
2140 		else
2141 			ibdm_ibnex_free_hca_list(hca_list);
2142 		return (retval);
2143 	}
2144 
2145 	if (port_pkey == 0)
2146 		node_type = (is_hcasvc_node == B_FALSE) ?
2147 		    IBNEX_PORT_COMMSVC_NODE : IBNEX_HCASVC_COMMSVC_NODE;
2148 	else
2149 		node_type = IBNEX_VPPA_COMMSVC_NODE;
2150 
2151 	mutex_enter(&ibnex.ibnex_mutex);
2152 	ndi_devi_enter(parent, &circ);
2153 	if (ibnex_commsvc_initnode(parent, port_attr, sndx, node_type,
2154 	    port_pkey, &ret, IBNEX_CFGADM_ENUMERATE) != NULL) {
2155 		retval = IBNEX_SUCCESS;
2156 	} else {
2157 		retval = (ret == IBNEX_BUSY) ? IBNEX_BUSY : IBNEX_FAILURE;
2158 	}
2159 	ndi_devi_exit(parent, circ);
2160 
2161 	if (is_hcasvc_node == B_FALSE)
2162 		ibdm_ibnex_free_port_attr(port_attr);
2163 	else
2164 		ibdm_ibnex_free_hca_list(hca_list);
2165 
2166 	IBTF_DPRINTF_L4("ibnex", "\tibnex_handle_commsvcnode_configure: "
2167 	    "done retval = %d", retval);
2168 
2169 	return (retval);
2170 }
2171 
2172 
2173 /*
2174  * ibnex_return_apid()
2175  *	Construct the ap_id of a given IBTF client in kernel
2176  */
2177 static void
2178 ibnex_return_apid(dev_info_t *childp, char **ret_apid)
2179 {
2180 	ibnex_node_data_t	*nodep;
2181 
2182 	IBTF_DPRINTF_L4("ibnex", "ibnex_return_apid:");
2183 
2184 	ASSERT(childp != NULL);
2185 	nodep = ddi_get_parent_data(childp);
2186 
2187 	if (nodep->node_type == IBNEX_PORT_COMMSVC_NODE) {
2188 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN,
2189 		    "ib%s%llX,0,%s", DYN_SEP,
2190 		    (longlong_t)nodep->node_data.port_node.port_guid,
2191 		    ibnex.ibnex_comm_svc_names[nodep->node_data.port_node.
2192 		    port_commsvc_idx]);
2193 
2194 	} else if (nodep->node_type == IBNEX_HCASVC_COMMSVC_NODE) {
2195 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN,
2196 		    "ib%s%llX,0,%s", DYN_SEP,
2197 		    (longlong_t)nodep->node_data.port_node.port_guid, ibnex.
2198 		    ibnex_hcasvc_comm_svc_names[nodep->node_data.port_node.
2199 		    port_commsvc_idx]);
2200 
2201 	} else if (nodep->node_type == IBNEX_VPPA_COMMSVC_NODE) {
2202 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN,
2203 		    "ib%s%llX,%x,%s", DYN_SEP,
2204 		    (longlong_t)nodep->node_data.port_node.port_guid,
2205 		    nodep->node_data.port_node.port_pkey,
2206 		    ibnex.ibnex_vppa_comm_svc_names[nodep->node_data.port_node.
2207 		    port_commsvc_idx]);
2208 
2209 	} else if (nodep->node_type == IBNEX_IOC_NODE) {
2210 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN,
2211 		    "ib%s%llX", DYN_SEP,
2212 		    (longlong_t)nodep->node_data.ioc_node.ioc_guid);
2213 
2214 	} else if (nodep->node_type == IBNEX_PSEUDO_NODE) {
2215 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN, "ib%s%s",
2216 		    DYN_SEP, nodep->node_data.pseudo_node.pseudo_node_addr);
2217 
2218 	} else {
2219 		(void) snprintf(*ret_apid, IBTL_IBNEX_APID_LEN, "%s", "-");
2220 	}
2221 
2222 	IBTF_DPRINTF_L4("ibnex", "ibnex_return_apid: %x %s",
2223 	    nodep->node_type, ret_apid);
2224 }
2225 
2226 
2227 /*
2228  * ibnex_vppa_conf_entry_add()
2229  *	Add a new service to the ibnex data base of VPPA communication
2230  *	services.
2231  */
2232 static void
2233 ibnex_vppa_conf_entry_add(char *service)
2234 {
2235 	int	i, nsvcs;
2236 	char	**service_name;
2237 
2238 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2239 	nsvcs = ibnex.ibnex_nvppa_comm_svcs;
2240 
2241 	/* Allocate space for new "ibnex.ibnex_nvppa_comm_svcs + 1" */
2242 	service_name = kmem_alloc((nsvcs + 1) * sizeof (char *), KM_SLEEP);
2243 	/*
2244 	 * Copy over the existing "ibnex.ibnex_vppa_comm_svc_names"
2245 	 * array. Add the new service at the end.
2246 	 */
2247 	for (i = 0; i < nsvcs; i++)
2248 		service_name[i] = ibnex.ibnex_vppa_comm_svc_names[i];
2249 	service_name[i] = kmem_alloc(strlen(service) + 1, KM_SLEEP);
2250 	(void) snprintf(service_name[i], 5, "%s", service);
2251 
2252 	/* Replace existing pointer to VPPA services w/ newly allocated one */
2253 	if (ibnex.ibnex_vppa_comm_svc_names) {
2254 		kmem_free(ibnex.ibnex_vppa_comm_svc_names, nsvcs *
2255 		    sizeof (char *));
2256 	}
2257 	ibnex.ibnex_nvppa_comm_svcs++;
2258 	ibnex.ibnex_vppa_comm_svc_names = service_name;
2259 }
2260 
2261 /*
2262  * ibnex_port_conf_entry_add()
2263  *	Add a new service to the ibnex data base of Port communication
2264  *	services.
2265  */
2266 static void
2267 ibnex_port_conf_entry_add(char *service)
2268 {
2269 	int	i, nsvcs;
2270 	char	**service_name;
2271 
2272 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2273 	nsvcs = ibnex.ibnex_num_comm_svcs;
2274 
2275 	/* Allocate space for new "ibnex.ibnex_num_comm_svcs + 1" */
2276 	service_name = kmem_alloc((nsvcs + 1) * sizeof (char *), KM_SLEEP);
2277 	/*
2278 	 * Copy over the existing "ibnex.ibnex_comm_svc_names" array.
2279 	 * Add the new service to the end.
2280 	 */
2281 	for (i = 0; i < nsvcs; i++)
2282 		service_name[i] = ibnex.ibnex_comm_svc_names[i];
2283 	service_name[i] = kmem_alloc(strlen(service) + 1, KM_SLEEP);
2284 	(void) snprintf(service_name[i], 5, "%s", service);
2285 
2286 	/* Replace existing pointer to Port services w/ newly allocated one */
2287 	if (ibnex.ibnex_comm_svc_names) {
2288 		kmem_free(ibnex.ibnex_comm_svc_names, nsvcs * sizeof (char *));
2289 	}
2290 	ibnex.ibnex_num_comm_svcs++;
2291 	ibnex.ibnex_comm_svc_names = service_name;
2292 }
2293 
2294 /*
2295  * ibnex_hcasvc_conf_entry_add()
2296  *	Add a new service to the ibnex data base of HCA_SVC communication
2297  *	services.
2298  */
2299 static void
2300 ibnex_hcasvc_conf_entry_add(char *service)
2301 {
2302 	int	i, nsvcs;
2303 	char	**service_name;
2304 
2305 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2306 	nsvcs = ibnex.ibnex_nhcasvc_comm_svcs;
2307 
2308 	/* Allocate space for new "ibnex.ibnex_nvppa_comm_svcs + 1" */
2309 	service_name = kmem_alloc((nsvcs + 1) * sizeof (char *), KM_SLEEP);
2310 	/*
2311 	 * Copy over the existing "ibnex.ibnex_hcasvc_comm_svc_names"
2312 	 * array. Add the new service at the end.
2313 	 */
2314 	for (i = 0; i < nsvcs; i++)
2315 		service_name[i] = ibnex.ibnex_hcasvc_comm_svc_names[i];
2316 	service_name[i] = kmem_alloc(strlen(service) + 1, KM_SLEEP);
2317 	(void) snprintf(service_name[i], 5, "%s", service);
2318 
2319 	/*
2320 	 * Replace existing pointer to HCA_SVC services w/ newly
2321 	 * allocated one
2322 	 */
2323 	if (ibnex.ibnex_hcasvc_comm_svc_names) {
2324 		kmem_free(ibnex.ibnex_hcasvc_comm_svc_names, nsvcs *
2325 		    sizeof (char *));
2326 	}
2327 	ibnex.ibnex_nhcasvc_comm_svcs++;
2328 	ibnex.ibnex_hcasvc_comm_svc_names = service_name;
2329 }
2330 
2331 
2332 /*
2333  * ibnex_vppa_conf_entry_delete()
2334  *	Delete an existing service entry from ibnex data base of
2335  *	VPPA communication services.
2336  */
2337 static int
2338 ibnex_vppa_conf_entry_delete(char *msg, char *service)
2339 {
2340 	int			i, j, nsvcs;
2341 	int			len;
2342 	int			match_ndx;
2343 	char			**service_name;
2344 	boolean_t		found = B_FALSE;
2345 	ibnex_node_data_t	*node_datap = ibnex.ibnex_port_node_head;
2346 
2347 	IBTF_DPRINTF_L4("ibnex", "\tvppa_conf_entry_delete: %s", service);
2348 
2349 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2350 	nsvcs = ibnex.ibnex_nvppa_comm_svcs;
2351 
2352 	/* find matching index */
2353 	for (i = 0; i < nsvcs; i++) {
2354 		if (strncmp(ibnex.ibnex_vppa_comm_svc_names[i], service,
2355 		    strlen(service)))
2356 			continue;
2357 		found = B_TRUE;
2358 		match_ndx = i;
2359 		break;
2360 	}
2361 
2362 	/* check for valid "nsvcs" */
2363 	if (found == B_FALSE || nsvcs == 0) {
2364 		IBTF_DPRINTF_L2("ibnex", "%s: invalid vppa services %x",
2365 		    msg, nsvcs);
2366 		return (EIO);
2367 	}
2368 
2369 	/* Check if service is in use; return failure if so */
2370 	for (; node_datap; node_datap = node_datap->node_next) {
2371 		if ((node_datap->node_data.port_node.port_commsvc_idx == i) &&
2372 		    node_datap->node_type == IBNEX_VPPA_COMMSVC_NODE &&
2373 		    node_datap->node_dip) {
2374 			IBTF_DPRINTF_L2("ibnex", "%s: service %s is in use",
2375 			    msg, service);
2376 			return (EIO);
2377 		}
2378 	}
2379 
2380 	/* if nsvcs == 1, bailout early */
2381 	if (nsvcs == 1) {
2382 		/* free up that single entry */
2383 		len = strlen(ibnex.ibnex_vppa_comm_svc_names[0]) + 1;
2384 		kmem_free(ibnex.ibnex_vppa_comm_svc_names[0], len);
2385 		kmem_free(ibnex.ibnex_vppa_comm_svc_names, sizeof (char *));
2386 		ibnex.ibnex_vppa_comm_svc_names = NULL;
2387 		ibnex.ibnex_nvppa_comm_svcs = 0;
2388 		return (0);
2389 	}
2390 
2391 	/* Allocate space for new "ibnex.ibnex_nvppa_comm_svcs - 1" */
2392 	service_name = kmem_alloc((nsvcs - 1) * sizeof (char *), KM_SLEEP);
2393 	/*
2394 	 * Copy over the existing "ibnex.ibnex_vppa_comm_svc_names"
2395 	 * array. Do not copy over the matching service.
2396 	 */
2397 	for (i = 0, j = 0; i < nsvcs; i++) {
2398 		if (i == match_ndx) {
2399 			/* free up that entry */
2400 			len = strlen(ibnex.ibnex_vppa_comm_svc_names[i]) + 1;
2401 			kmem_free(ibnex.ibnex_vppa_comm_svc_names[i], len);
2402 			continue;
2403 		}
2404 		service_name[j++] = ibnex.ibnex_vppa_comm_svc_names[i];
2405 	}
2406 
2407 	/* Replace existing pointer to VPPA services w/ newly adjusted one */
2408 	if (ibnex.ibnex_vppa_comm_svc_names) {
2409 		kmem_free(ibnex.ibnex_vppa_comm_svc_names, nsvcs *
2410 		    sizeof (char *));
2411 		ibnex.ibnex_nvppa_comm_svcs--;
2412 		ibnex.ibnex_vppa_comm_svc_names = service_name;
2413 	}
2414 	return (0);
2415 }
2416 
2417 
2418 /*
2419  * ibnex_port_conf_entry_delete()
2420  *	Delete an existing service entry from ibnex data base of
2421  *	Port communication services.
2422  */
2423 static int
2424 ibnex_port_conf_entry_delete(char *msg, char *service)
2425 {
2426 	int			i, j, nsvcs;
2427 	int			match_ndx;
2428 	int			len;
2429 	char			**service_name;
2430 	boolean_t		found = B_FALSE;
2431 	ibnex_node_data_t	*node_datap = ibnex.ibnex_port_node_head;
2432 
2433 	IBTF_DPRINTF_L4("ibnex", "\tport_conf_entry_delete: %s", service);
2434 
2435 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2436 	nsvcs = ibnex.ibnex_num_comm_svcs;
2437 
2438 	/* find matching index */
2439 	for (i = 0; i < nsvcs; i++) {
2440 		if (strncmp(ibnex.ibnex_comm_svc_names[i], service,
2441 		    strlen(service)))
2442 			continue;
2443 		found = B_TRUE;
2444 		match_ndx = i;
2445 		break;
2446 	}
2447 
2448 	/* check for valid "nsvcs" */
2449 	if (found == B_FALSE || nsvcs == 0) {
2450 		IBTF_DPRINTF_L2("ibnex", "%s: invalid services %x", msg, nsvcs);
2451 		return (EIO);
2452 	}
2453 
2454 	/* Check if service is in use; return failure if so */
2455 	for (; node_datap; node_datap = node_datap->node_next) {
2456 		if ((node_datap->node_data.port_node.port_commsvc_idx == i) &&
2457 		    node_datap->node_type == IBNEX_PORT_COMMSVC_NODE &&
2458 		    node_datap->node_dip)
2459 			return (EIO);
2460 	}
2461 
2462 	/* if nsvcs == 1, bailout early */
2463 	if (nsvcs == 1) {
2464 		/* free up that single entry */
2465 		len = strlen(ibnex.ibnex_comm_svc_names[0]) + 1;
2466 		kmem_free(ibnex.ibnex_comm_svc_names[0], len);
2467 		kmem_free(ibnex.ibnex_comm_svc_names, sizeof (char *));
2468 		ibnex.ibnex_comm_svc_names = NULL;
2469 		ibnex.ibnex_num_comm_svcs = 0;
2470 		return (0);
2471 	}
2472 
2473 	/* Allocate space for new "ibnex.ibnex_num_comm_svcs - 1" */
2474 	service_name = kmem_alloc((nsvcs - 1) * sizeof (char *), KM_SLEEP);
2475 	/*
2476 	 * Copy over the existing "ibnex.ibnex_comm_svc_names" array.
2477 	 * Skip the matching service.
2478 	 */
2479 	for (i = 0, j = 0; i < nsvcs; i++) {
2480 		if (i == match_ndx) {
2481 			/* free up that entry */
2482 			len = strlen(ibnex.ibnex_comm_svc_names[i]) + 1;
2483 			kmem_free(ibnex.ibnex_comm_svc_names[i], len);
2484 			continue;
2485 		}
2486 		service_name[j++] = ibnex.ibnex_comm_svc_names[i];
2487 	}
2488 
2489 	/* Replace existing pointer to Port services w/ newly adjusted one */
2490 	if (ibnex.ibnex_comm_svc_names) {
2491 		kmem_free(ibnex.ibnex_comm_svc_names, nsvcs * sizeof (char *));
2492 		ibnex.ibnex_num_comm_svcs--;
2493 		ibnex.ibnex_comm_svc_names = service_name;
2494 	}
2495 	return (0);
2496 }
2497 
2498 /*
2499  * ibnex_hcasvc_conf_entry_delete()
2500  *	Delete an existing service entry from ibnex data base of
2501  *	HCA_SVC communication services.
2502  */
2503 static int
2504 ibnex_hcasvc_conf_entry_delete(char *msg, char *service)
2505 {
2506 	int			i, j, nsvcs;
2507 	int			len;
2508 	int			match_ndx;
2509 	char			**service_name;
2510 	boolean_t		found = B_FALSE;
2511 	ibnex_node_data_t	*node_datap = ibnex.ibnex_port_node_head;
2512 
2513 	IBTF_DPRINTF_L4("ibnex", "\thcasvc_conf_entry_delete: %s", service);
2514 
2515 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2516 	nsvcs = ibnex.ibnex_nhcasvc_comm_svcs;
2517 
2518 	/* find matching index */
2519 	for (i = 0; i < nsvcs; i++) {
2520 		if (strncmp(ibnex.ibnex_hcasvc_comm_svc_names[i], service,
2521 		    strlen(service)))
2522 			continue;
2523 		found = B_TRUE;
2524 		match_ndx = i;
2525 		break;
2526 	}
2527 
2528 	/* check for valid "nsvcs" */
2529 	if (found == B_FALSE || nsvcs == 0) {
2530 		IBTF_DPRINTF_L2("ibnex", "%s: invalid hca_svc services %x",
2531 		    msg, nsvcs);
2532 		return (EIO);
2533 	}
2534 
2535 	/* Check if service is in use; return failure if so */
2536 	for (; node_datap; node_datap = node_datap->node_next) {
2537 		if ((node_datap->node_data.port_node.port_commsvc_idx == i) &&
2538 		    node_datap->node_type == IBNEX_HCASVC_COMMSVC_NODE &&
2539 		    node_datap->node_dip) {
2540 			IBTF_DPRINTF_L2("ibnex", "%s: service %s is in use",
2541 			    msg, service);
2542 			return (EIO);
2543 		}
2544 	}
2545 
2546 	/* if nsvcs == 1, bailout early */
2547 	if (nsvcs == 1) {
2548 		/* free up that single entry */
2549 		len = strlen(ibnex.ibnex_hcasvc_comm_svc_names[0]) + 1;
2550 		kmem_free(ibnex.ibnex_hcasvc_comm_svc_names[0], len);
2551 		kmem_free(ibnex.ibnex_hcasvc_comm_svc_names, sizeof (char *));
2552 		ibnex.ibnex_hcasvc_comm_svc_names = NULL;
2553 		ibnex.ibnex_nhcasvc_comm_svcs = 0;
2554 		return (0);
2555 	}
2556 
2557 	/* Allocate space for new "ibnex.ibnex_nhcasvc_comm_svcs - 1" */
2558 	service_name = kmem_alloc((nsvcs - 1) * sizeof (char *), KM_SLEEP);
2559 	/*
2560 	 * Copy over the existing "ibnex.ibnex_hcasvc_comm_svc_names"
2561 	 * array. Do not copy over the matching service.
2562 	 */
2563 	for (i = 0, j = 0; i < nsvcs; i++) {
2564 		if (i == match_ndx) {
2565 			/* free up that entry */
2566 			len = strlen(ibnex.ibnex_hcasvc_comm_svc_names[i]) + 1;
2567 			kmem_free(ibnex.ibnex_hcasvc_comm_svc_names[i], len);
2568 			continue;
2569 		}
2570 		service_name[j++] = ibnex.ibnex_hcasvc_comm_svc_names[i];
2571 	}
2572 
2573 	/* Replace existing pointer to VPPA services w/ newly adjusted one */
2574 	if (ibnex.ibnex_hcasvc_comm_svc_names) {
2575 		kmem_free(ibnex.ibnex_hcasvc_comm_svc_names, nsvcs *
2576 		    sizeof (char *));
2577 		ibnex.ibnex_nhcasvc_comm_svcs--;
2578 		ibnex.ibnex_hcasvc_comm_svc_names = service_name;
2579 	}
2580 	return (0);
2581 }
2582 
2583 
2584 /*
2585  * ibnex_ioc_fininode()
2586  *	Un-initialize a child device node for IOC device node
2587  *	Returns IBNEX_SUCCESS/IBNEX_FAILURE
2588  */
2589 static ibnex_rval_t
2590 ibnex_ioc_fininode(dev_info_t *dip, ibnex_ioc_node_t *ioc_nodep)
2591 {
2592 	int	rval = MDI_SUCCESS;
2593 
2594 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2595 	IBTF_DPRINTF_L4("ibnex", "\tioc_fininode");
2596 
2597 	/*
2598 	 * For a dis-connected IOC,
2599 	 *	Free the ioc_profile &&
2600 	 *	decrement ibnex_num_disconnect_iocs
2601 	 */
2602 	if (ioc_nodep->ioc_ngids == 0 && ioc_nodep->ioc_profile) {
2603 		IBTF_DPRINTF_L4("ibnex", "\tioc_fininode: unconfigure "
2604 		    "disconnected IOC: GUID %lX", ioc_nodep->ioc_guid);
2605 		ibnex.ibnex_num_disconnect_iocs--;
2606 		kmem_free(ioc_nodep->ioc_profile,
2607 		    sizeof (ib_dm_ioc_ctrl_profile_t));
2608 		ioc_nodep->ioc_profile = NULL;
2609 	}
2610 
2611 	mutex_exit(&ibnex.ibnex_mutex);
2612 	ASSERT(i_ddi_node_state(dip) >= DS_BOUND);
2613 
2614 	IBTF_DPRINTF_L4("ibnex", "\tioc_fininode: offlining the IOC");
2615 	rval = ibnex_offline_childdip(dip);
2616 
2617 	if (rval != MDI_SUCCESS) {
2618 		rval = NDI_FAILURE;
2619 		IBTF_DPRINTF_L2("ibnex", "\toffline failed for IOC "
2620 		    "dip %p with 0x%x", dip, rval);
2621 	}
2622 
2623 	mutex_enter(&ibnex.ibnex_mutex);
2624 	return (rval == MDI_SUCCESS ? IBNEX_SUCCESS : IBNEX_OFFLINE_FAILED);
2625 }
2626 
2627 
2628 int
2629 ibnex_offline_childdip(dev_info_t *dip)
2630 {
2631 	int		rval = MDI_SUCCESS;
2632 	mdi_pathinfo_t	*path = NULL, *temp;
2633 
2634 	IBTF_DPRINTF_L4("ibnex", "\toffline_childdip; begin");
2635 	if (dip == NULL) {
2636 		IBTF_DPRINTF_L2("ibnex", "\toffline_childdip; NULL dip");
2637 		return (MDI_FAILURE);
2638 	}
2639 
2640 	for (path = mdi_get_next_phci_path(dip, path); path; ) {
2641 		IBTF_DPRINTF_L4("ibnex", "\toffline_childdip: "
2642 		    "offling path %p", path);
2643 		rval = mdi_pi_offline(path, NDI_UNCONFIG);
2644 		if (rval != MDI_SUCCESS) {
2645 			IBTF_DPRINTF_L2("ibnex", "\toffline_childdip: "
2646 			    "mdi_pi_offline failed %p", dip);
2647 			break;
2648 		}
2649 		temp = path;
2650 		path = mdi_get_next_phci_path(dip, path);
2651 		(void) mdi_pi_free(temp, 0);
2652 	}
2653 	return (rval);
2654 }
2655 
2656 
2657 /*
2658  * ibnex_commsvc_fininode()
2659  *
2660  * Un-initialize a child device node for HCA port / node GUID
2661  * for a communication service.
2662  *	Returns IBNEX_SUCCESS/IBNEX_FAILURE
2663  */
2664 static ibnex_rval_t
2665 ibnex_commsvc_fininode(dev_info_t *dip)
2666 {
2667 	int	rval = NDI_SUCCESS;
2668 
2669 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2670 	IBTF_DPRINTF_L4("ibnex", "\tcommsvc_fininode");
2671 
2672 	mutex_exit(&ibnex.ibnex_mutex);
2673 	if (i_ddi_node_state(dip) < DS_BOUND) {
2674 		/*
2675 		 * if the child hasn't been bound yet, we can
2676 		 * just free the dip. This path is currently
2677 		 * untested.
2678 		 */
2679 		(void) ddi_remove_child(dip, 0);
2680 		IBTF_DPRINTF_L4("ibnex",
2681 		    "\tcommsvc_fininode: ddi_remove_child");
2682 	} else {
2683 		IBTF_DPRINTF_L4("ibnex", "\tcommsvc_fininode: offlining the "
2684 		    "Commsvc node");
2685 
2686 		rval = ndi_devi_offline(dip, NDI_DEVI_REMOVE | NDI_UNCONFIG);
2687 		if (rval != NDI_SUCCESS)
2688 			IBTF_DPRINTF_L2("ibnex", "\toffline failed for Commsvc "
2689 			    "dip %p with 0x%x", dip, rval);
2690 	}
2691 	mutex_enter(&ibnex.ibnex_mutex);
2692 	return (rval == NDI_SUCCESS ? IBNEX_SUCCESS : IBNEX_OFFLINE_FAILED);
2693 }
2694 
2695 
2696 /*
2697  * ibnex_pseudo_fininode()
2698  *	Un-initialize a child pseudo device node
2699  *	Returns IBNEX_SUCCESS/IBNEX_FAILURE
2700  */
2701 static ibnex_rval_t
2702 ibnex_pseudo_fininode(dev_info_t *dip)
2703 {
2704 	int	rval = MDI_SUCCESS;
2705 
2706 	ASSERT(MUTEX_HELD(&ibnex.ibnex_mutex));
2707 	IBTF_DPRINTF_L4("ibnex", "\tpseudo_fininode: dip = %p", dip);
2708 
2709 	mutex_exit(&ibnex.ibnex_mutex);
2710 	ASSERT(i_ddi_node_state(dip) >= DS_BOUND);
2711 
2712 	IBTF_DPRINTF_L4("ibnex", "\tpseudo_fininode: offlining the "
2713 	    "pseudo device");
2714 	rval = ibnex_offline_childdip(dip);
2715 	if (rval != MDI_SUCCESS) {
2716 		rval = NDI_FAILURE;
2717 		IBTF_DPRINTF_L2("ibnex", "\tpseudo offline failed for "
2718 		    "dip %p with 0x%x", dip, rval);
2719 	}
2720 
2721 	mutex_enter(&ibnex.ibnex_mutex);
2722 	return (rval == MDI_SUCCESS ? IBNEX_SUCCESS : IBNEX_OFFLINE_FAILED);
2723 }
2724