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