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