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