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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * The Ethernet Over Infiniband Nexus driver is a bus nexus driver
28  * that enumerates all the EoIB nodes.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/devops.h>
34 #include <sys/kmem.h>
35 #include <sys/ksynch.h>
36 #include <sys/modctl.h>
37 #include <sys/stat.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 
42 #include <sys/ib/clients/eoib/enx_impl.h>
43 
44 /*
45  * Global per-instance EoIB Nexus data.  Only one instance
46  * of EoIB Nexus is supported
47  */
48 eibnx_t *enx_global_ss = NULL;
49 
50 /*
51  * Static function declarations
52  */
53 static int eibnx_attach(dev_info_t *, ddi_attach_cmd_t);
54 static int eibnx_detach(dev_info_t *, ddi_detach_cmd_t);
55 static int eibnx_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
56 static int eibnx_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
57     void *, void *);
58 
59 static int eibnx_get_eventcookie(dev_info_t *, dev_info_t *, char *,
60     ddi_eventcookie_t *);
61 static int eibnx_add_eventcall(dev_info_t *, dev_info_t *, ddi_eventcookie_t,
62     void (*)(dev_info_t *, ddi_eventcookie_t, void *, void *),
63     void *, ddi_callback_id_t *);
64 static int eibnx_remove_eventcall(dev_info_t *, ddi_callback_id_t);
65 static int eibnx_post_event(dev_info_t *, dev_info_t *,
66     ddi_eventcookie_t, void *);
67 
68 static int eibnx_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
69     void *, dev_info_t **);
70 static int eibnx_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
71     void *);
72 static int eibnx_config_all_children(dev_info_t *);
73 static void eibnx_unconfig_all_children(dev_info_t *);
74 static int eibnx_config_child(char *, dev_info_t **);
75 static int eibnx_unconfig_child(char *);
76 
77 /*
78  * Cbops
79  */
80 static struct cb_ops enx_cb_ops = {
81 	eibnx_devctl_open,	/* cb_open */
82 	eibnx_devctl_close,	/* cb_close */
83 	nodev,			/* cb_strategy */
84 	nodev,			/* cb_print */
85 	nodev,			/* cb_dump */
86 	nodev,			/* cb_read */
87 	nodev,			/* cb_write */
88 	eibnx_devctl_ioctl,	/* cb_ioctl */
89 	nodev,			/* cb_devmap */
90 	nodev,			/* cb_mmap */
91 	nodev,			/* cb_segmap */
92 	nochpoll,		/* cb_chpoll */
93 	ddi_prop_op,		/* cb_prop_op */
94 	NULL,			/* cb_str */
95 	D_MP,			/* cb_flag */
96 	CB_REV, 		/* cb_rev */
97 	nodev,			/* cb_aread */
98 	nodev			/* cb_awrite */
99 };
100 
101 /*
102  * Busops
103  */
104 static struct bus_ops enx_bus_ops = {
105 	BUSO_REV,
106 	nullbusmap,		/* bus_map */
107 	NULL,			/* bus_get_intrspec */
108 	NULL,			/* bus_add_intrspec */
109 	NULL,			/* bus_remove_intrspec */
110 	i_ddi_map_fault,	/* bus_map_fault */
111 	ddi_no_dma_map,		/* bus_dma_map */
112 	NULL,			/* bus_dma_allochdl */
113 	NULL,			/* bus_dma_freehdl */
114 	NULL,			/* bus_dma_bindhdl */
115 	NULL,			/* bus_dma_unbindhdl */
116 	NULL,			/* bus_dma_flush */
117 	NULL,			/* bus_dma_win */
118 	NULL,			/* bus_dma_ctl */
119 	eibnx_bus_ctl,		/* bus_ctl */
120 	ddi_bus_prop_op,	/* bus_prop_op */
121 	eibnx_get_eventcookie,	/* bus_get_eventcookie */
122 	eibnx_add_eventcall,	/* bus_add_eventcall */
123 	eibnx_remove_eventcall,	/* bus_remove_eventcall */
124 	eibnx_post_event,	/* bus_post_event */
125 	NULL,			/* bus_intr_ctl */
126 	eibnx_bus_config,	/* bus_config */
127 	eibnx_bus_unconfig,	/* bus_unconfig */
128 };
129 
130 /*
131  * Nexus ops
132  */
133 static struct dev_ops enx_ops = {
134 	DEVO_REV,		/* devo_rev, */
135 	0,			/* devo_refcnt  */
136 	eibnx_getinfo,		/* devo_info */
137 	nulldev,		/* devo_identify */
138 	nulldev,		/* devo_probe */
139 	eibnx_attach,		/* devo_attach */
140 	eibnx_detach,		/* devo_detach */
141 	nodev,			/* devo_reset */
142 	&enx_cb_ops,		/* devo_cb_ops */
143 	&enx_bus_ops,		/* devo_bus_ops */
144 	nulldev,		/* devo_power */
145 	ddi_quiesce_not_needed	/* devo_quiesce */
146 };
147 
148 /*
149  * Module linkage information for the kernel
150  */
151 static struct modldrv enx_modldrv = {
152 	&mod_driverops,		/* Driver module */
153 	"EoIB Nexus",		/* Driver name and version */
154 	&enx_ops,		/* Driver ops */
155 };
156 
157 static struct modlinkage enx_modlinkage = {
158 	MODREV_1, (void *)&enx_modldrv, NULL
159 };
160 
161 /*
162  * EoIB NDI events
163  */
164 static ndi_event_definition_t enx_ndi_event_defs[] = {
165 	{ ENX_EVENT_TAG_GW_INFO_UPDATE, EIB_NDI_EVENT_GW_INFO_UPDATE,
166 		EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
167 	{ ENX_EVENT_TAG_GW_AVAILABLE, EIB_NDI_EVENT_GW_AVAILABLE,
168 		EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
169 	{ ENX_EVENT_TAG_LOGIN_ACK, EIB_NDI_EVENT_LOGIN_ACK,
170 		EPL_KERNEL, NDI_EVENT_POST_TO_TGT }
171 };
172 #define	ENX_NUM_NDI_EVENTS		\
173 	(sizeof (enx_ndi_event_defs) / sizeof (enx_ndi_event_defs[0]))
174 
175 static ndi_event_set_t enx_ndi_events = {
176 	NDI_EVENTS_REV1,
177 	ENX_NUM_NDI_EVENTS,
178 	enx_ndi_event_defs
179 };
180 ndi_event_hdl_t enx_ndi_event_hdl;
181 
182 
183 /*
184  * Common loadable module entry points _init, _fini, _info
185  */
186 
187 int
_init(void)188 _init(void)
189 {
190 	int ret;
191 
192 	if ((ret = mod_install(&enx_modlinkage)) == 0)
193 		eibnx_debug_init();
194 
195 	return (ret);
196 }
197 
198 int
_fini(void)199 _fini(void)
200 {
201 	int ret;
202 
203 	if ((ret = mod_remove(&enx_modlinkage)) == 0)
204 		eibnx_debug_fini();
205 
206 	return (ret);
207 }
208 
209 int
_info(struct modinfo * modinfop)210 _info(struct modinfo *modinfop)
211 {
212 	return (mod_info(&enx_modlinkage, modinfop));
213 }
214 
215 /*
216  * Autoconfiguration entry points: attach, detach, getinfo
217  */
218 
219 static int
eibnx_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)220 eibnx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
221 {
222 	eibnx_t *ss;
223 	int instance;
224 
225 	if (cmd == DDI_RESUME)
226 		return (DDI_SUCCESS);
227 	else if (cmd != DDI_ATTACH)
228 		return (DDI_FAILURE);
229 
230 	/*
231 	 * Don't allow more than one instance to attach
232 	 */
233 	if (enx_global_ss)
234 		return (DDI_FAILURE);
235 
236 	/*
237 	 * Alloc this instance's softstate
238 	 */
239 	ss = kmem_zalloc(sizeof (eibnx_t), KM_SLEEP);
240 	ss->nx_dip = dip;
241 
242 	enx_global_ss = ss;
243 
244 	/*
245 	 * Allocate our NDI event handle and bind our event set
246 	 */
247 	if (ndi_event_alloc_hdl(dip, 0, &enx_ndi_event_hdl,
248 	    NDI_SLEEP) != NDI_SUCCESS) {
249 		ENX_DPRINTF_ERR("ndi_event_alloc_hdl(dip=0x%llx) "
250 		    "failed", dip);
251 
252 		kmem_free(enx_global_ss, sizeof (eibnx_t));
253 		enx_global_ss = NULL;
254 		return (DDI_FAILURE);
255 	}
256 	if (ndi_event_bind_set(enx_ndi_event_hdl, &enx_ndi_events,
257 	    NDI_SLEEP) != NDI_SUCCESS) {
258 		ENX_DPRINTF_ERR("ndi_event_bind_set(ndi_event_hdl=0x%llx) "
259 		    "failed", enx_ndi_event_hdl);
260 
261 		(void) ndi_event_free_hdl(enx_ndi_event_hdl);
262 		enx_ndi_event_hdl = NULL;
263 		kmem_free(enx_global_ss, sizeof (eibnx_t));
264 		enx_global_ss = NULL;
265 		return (DDI_FAILURE);
266 	}
267 
268 	/*
269 	 * Create "devctl" minor node for general ioctl interface to the
270 	 * eoib nexus. If we cannot, it isn't fatal - we'll operate without
271 	 * the support for devctl (but issue a warning).
272 	 */
273 	instance = ddi_get_instance(dip);
274 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
275 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
276 		ENX_DPRINTF_WARN("could not create devctl minor node "
277 		    "for instance %d", instance);
278 	}
279 
280 	/*
281 	 * Do IBTF related initializations. If we fail, we cannot operate,
282 	 * so fail the attach.
283 	 */
284 	if (eibnx_ibt_init(ss) != ENX_E_SUCCESS) {
285 		(void) ddi_remove_minor_node(dip, NULL);
286 		(void) ndi_event_unbind_set(enx_ndi_event_hdl,
287 		    &enx_ndi_events, NDI_SLEEP);
288 		(void) ndi_event_free_hdl(enx_ndi_event_hdl);
289 		enx_ndi_event_hdl = NULL;
290 		kmem_free(enx_global_ss, sizeof (eibnx_t));
291 		enx_global_ss = NULL;
292 		return (DDI_FAILURE);
293 	}
294 
295 	return (DDI_SUCCESS);
296 }
297 
298 static int
eibnx_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)299 eibnx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
300 {
301 	eibnx_t *ss = enx_global_ss;
302 
303 	if (cmd == DDI_SUSPEND)
304 		return (DDI_SUCCESS);
305 	else if (cmd != DDI_DETACH)
306 		return (DDI_FAILURE);
307 
308 	/*
309 	 * If there's no instance of eibnx attached, fail
310 	 */
311 	if (ss == NULL)
312 		return (DDI_FAILURE);
313 
314 	/*
315 	 * Before we do anything, we need to stop the port monitors
316 	 * we may have started earlier.
317 	 */
318 	eibnx_terminate_monitors();
319 
320 	/*
321 	 * If eibnx_ibt_fini() fails, it could be because one of the
322 	 * HCA's pd could not be freed, the hca could not be closed
323 	 * or the IBTF detach wasn't successful.  If this is the case,
324 	 * we have to return failure, but cannot do much about the
325 	 * port monitors we've already terminated.
326 	 */
327 	if (eibnx_ibt_fini(ss) == ENX_E_FAILURE)
328 		return (DDI_FAILURE);
329 
330 	/*
331 	 * Cleanup any devctl minor node we may have created, unbind and
332 	 * free ndi event handle and free the instance softstate.
333 	 */
334 	(void) ddi_remove_minor_node(dip, NULL);
335 	(void) ndi_event_unbind_set(enx_ndi_event_hdl,
336 	    &enx_ndi_events, NDI_SLEEP);
337 	(void) ndi_event_free_hdl(enx_ndi_event_hdl);
338 	enx_ndi_event_hdl = NULL;
339 	kmem_free(enx_global_ss, sizeof (eibnx_t));
340 	enx_global_ss = NULL;
341 
342 	return (DDI_SUCCESS);
343 }
344 
345 /*ARGSUSED*/
346 static int
eibnx_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)347 eibnx_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
348 {
349 	eibnx_t *ss = enx_global_ss;
350 	int ret;
351 
352 	if (cmd == DDI_INFO_DEVT2DEVINFO) {
353 		*resultp = (ss) ? ss->nx_dip : NULL;
354 		ret = (ss) ? DDI_SUCCESS : DDI_FAILURE;
355 	} else if (cmd == DDI_INFO_DEVT2INSTANCE) {
356 		*resultp = 0;
357 		ret = DDI_SUCCESS;
358 	} else {
359 		ret = DDI_FAILURE;
360 	}
361 
362 	return (ret);
363 }
364 
365 /*
366  * Busops: bus_ctl, bus_config, bus_unconfig
367  */
368 
369 /*ARGSUSED*/
370 static int
eibnx_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)371 eibnx_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
372     void *arg, void *result)
373 {
374 	dev_info_t *child = arg;
375 	int ret;
376 	char name[MAXNAMELEN];
377 
378 	switch (ctlop) {
379 	case DDI_CTLOPS_REPORTDEV:
380 		ENX_DPRINTF_DEBUG("EoIB device: %s@%s, %s%d",
381 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
382 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
383 		/*FALLTHROUGH*/
384 
385 	case DDI_CTLOPS_ATTACH:
386 	case DDI_CTLOPS_DETACH:
387 	case DDI_CTLOPS_POWER:
388 	case DDI_CTLOPS_SIDDEV:
389 	case DDI_CTLOPS_IOMIN:
390 		ret = DDI_SUCCESS;
391 		break;
392 
393 	case DDI_CTLOPS_INITCHILD:
394 		if ((ret = eibnx_name_child(child, name,
395 		    sizeof (name))) == DDI_SUCCESS) {
396 			ddi_set_name_addr(child, name);
397 		}
398 		break;
399 
400 	case DDI_CTLOPS_UNINITCHILD:
401 		ddi_set_name_addr(child, NULL);
402 		ret = DDI_SUCCESS;
403 		break;
404 
405 	default:
406 		ret = ddi_ctlops(dip, rdip, ctlop, arg, result);
407 		break;
408 	}
409 
410 	return (ret);
411 }
412 
413 /*ARGSUSED*/
414 static int
eibnx_bus_config(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)415 eibnx_bus_config(dev_info_t *parent, uint_t flags,
416     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
417 {
418 	eibnx_t *ss = enx_global_ss;
419 	int ret = NDI_SUCCESS;
420 
421 	switch (op) {
422 	case BUS_CONFIG_ONE:
423 		eibnx_busop_inprog_enter(ss);
424 		ret = eibnx_config_child(arg, childp);
425 		eibnx_busop_inprog_exit(ss);
426 		break;
427 
428 	case BUS_CONFIG_ALL:
429 	case BUS_CONFIG_DRIVER:
430 		eibnx_busop_inprog_enter(ss);
431 		if ((ss->nx_busop_flags & NX_FL_BUSCFG_COMPLETE) == 0) {
432 			ret = eibnx_config_all_children(parent);
433 			if (ret == NDI_SUCCESS)
434 				ss->nx_busop_flags |= NX_FL_BUSCFG_COMPLETE;
435 		}
436 		eibnx_busop_inprog_exit(ss);
437 		break;
438 
439 	default:
440 		ret = NDI_FAILURE;
441 	}
442 
443 	if (ret == NDI_SUCCESS)
444 		ret = ndi_busop_bus_config(parent, flags, op, arg, childp, 0);
445 
446 	return (ret);
447 }
448 
449 static int
eibnx_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)450 eibnx_bus_unconfig(dev_info_t *parent, uint_t flags,
451     ddi_bus_config_op_t op, void *arg)
452 {
453 	eibnx_t *ss = enx_global_ss;
454 	int ret;
455 
456 	ret = ndi_busop_bus_unconfig(parent, flags, op, arg);
457 	if (ret != NDI_SUCCESS)
458 		return (ret);
459 
460 	switch (op) {
461 	case BUS_UNCONFIG_ONE:
462 		if (flags & (NDI_UNCONFIG | NDI_DEVI_REMOVE)) {
463 			eibnx_busop_inprog_enter(ss);
464 
465 			if ((ret = eibnx_unconfig_child(arg)) == ENX_E_SUCCESS)
466 				ss->nx_busop_flags &= (~NX_FL_BUSCFG_COMPLETE);
467 			else {
468 				ENX_DPRINTF_DEBUG("eibnx_bus_config: "
469 				    "unconfig child %s failed", (char *)arg);
470 			}
471 
472 			eibnx_busop_inprog_exit(ss);
473 		}
474 		break;
475 
476 	case BUS_UNCONFIG_ALL:
477 	case BUS_UNCONFIG_DRIVER:
478 		if (flags & (NDI_UNCONFIG | NDI_DEVI_REMOVE)) {
479 			eibnx_busop_inprog_enter(ss);
480 
481 			eibnx_unconfig_all_children(parent);
482 			ss->nx_busop_flags &= (~NX_FL_BUSCFG_COMPLETE);
483 
484 			eibnx_busop_inprog_exit(ss);
485 		}
486 		break;
487 
488 	default:
489 		break;
490 	}
491 
492 	return (ret);
493 }
494 
495 /*
496  * Event Handling: bus_get_eventcookie, bus_add_eventcall, bus_remove_eventcall
497  * and bus_post_event
498  */
499 
500 /*ARGSUSED*/
501 static int
eibnx_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * name,ddi_eventcookie_t * cookiep)502 eibnx_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
503     char *name, ddi_eventcookie_t *cookiep)
504 {
505 	return (ndi_event_retrieve_cookie(enx_ndi_event_hdl, rdip, name,
506 	    cookiep, NDI_EVENT_NOPASS));
507 }
508 
509 /*ARGSUSED*/
510 static int
eibnx_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * cb_dip,ddi_eventcookie_t cb_cookie,void * cb_arg,void * cb_impl_data),void * arg,ddi_callback_id_t * cb_id)511 eibnx_add_eventcall(dev_info_t *dip, dev_info_t *rdip, ddi_eventcookie_t cookie,
512     void (*callback)(dev_info_t *cb_dip, ddi_eventcookie_t cb_cookie,
513     void *cb_arg, void *cb_impl_data),
514     void *arg, ddi_callback_id_t *cb_id)
515 {
516 	return (ndi_event_add_callback(enx_ndi_event_hdl, rdip, cookie,
517 	    callback, arg, NDI_SLEEP, cb_id));
518 }
519 
520 /*ARGSUSED*/
521 static int
eibnx_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)522 eibnx_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
523 {
524 	return (ndi_event_remove_callback(enx_ndi_event_hdl, cb_id));
525 }
526 
527 /*ARGSUSED*/
528 static int
eibnx_post_event(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void * impl_data)529 eibnx_post_event(dev_info_t *dip, dev_info_t *rdip,
530     ddi_eventcookie_t cookie, void *impl_data)
531 {
532 	return (ndi_event_run_callbacks(enx_ndi_event_hdl, rdip, cookie,
533 	    impl_data));
534 }
535 
536 /*
537  * Routines to configure/unconfigure EoIB node(s) on a system.
538  */
539 
540 /*ARGSUSED*/
541 static int
eibnx_config_all_children(dev_info_t * parent)542 eibnx_config_all_children(dev_info_t *parent)
543 {
544 	eibnx_t *ss = enx_global_ss;
545 	eibnx_hca_t *hca;
546 	eibnx_port_t *port;
547 	eibnx_thr_info_t *ti;
548 	eibnx_thr_info_t *ti_tail;
549 	eibnx_gw_info_t *gwi;
550 
551 	/*
552 	 * Go through each port of each hca and create a thread to solicit,
553 	 * monitor, receive advertisements, create eoib nodes and attach eoib
554 	 * driver instances.
555 	 */
556 	mutex_enter(&ss->nx_lock);
557 	if (!ss->nx_monitors_up) {
558 		ss->nx_thr_info = ti_tail = NULL;
559 		for (hca = ss->nx_hca; hca; hca = hca->hc_next) {
560 			for (port = hca->hc_port; port; port = port->po_next) {
561 				ti = eibnx_start_port_monitor(hca, port);
562 				if (ti_tail) {
563 					ti_tail->ti_next = ti;
564 				} else {
565 					ss->nx_thr_info = ti;
566 				}
567 				ti_tail = ti;
568 			}
569 		}
570 
571 		ss->nx_monitors_up = B_TRUE;
572 		mutex_exit(&ss->nx_lock);
573 
574 		return (NDI_SUCCESS);
575 	}
576 	mutex_exit(&ss->nx_lock);
577 
578 	while (eibnx_locate_unconfigured_node(&ti, &gwi) == ENX_E_SUCCESS)
579 		(void) eibnx_configure_node(ti, gwi, NULL);
580 
581 	return (NDI_SUCCESS);
582 }
583 
584 /*
585  * Routine to unconfigure all the EoIB nodes on a system. This terminates
586  * all the per-port monitor threads and releases any resources allocated.
587  */
588 
589 /*ARGSUSED*/
590 static void
eibnx_unconfig_all_children(dev_info_t * parent)591 eibnx_unconfig_all_children(dev_info_t *parent)
592 {
593 	eibnx_t *ss = enx_global_ss;
594 	eibnx_thr_info_t *ti;
595 	eibnx_child_t *ch;
596 
597 	mutex_enter(&ss->nx_lock);
598 	for (ti = ss->nx_thr_info; ti; ti = ti->ti_next) {
599 		mutex_enter(&ti->ti_child_lock);
600 		for (ch = ti->ti_child; ch; ch = ch->ch_next) {
601 			ch->ch_dip = NULL;
602 		}
603 		mutex_exit(&ti->ti_child_lock);
604 	}
605 	mutex_exit(&ss->nx_lock);
606 }
607 
608 /*ARGSUSED*/
609 static int
eibnx_config_child(char * devname,dev_info_t ** childp)610 eibnx_config_child(char *devname, dev_info_t **childp)
611 {
612 	eibnx_thr_info_t *ti;
613 	eibnx_gw_info_t *gwi;
614 
615 	if (eibnx_locate_node_name(devname, &ti, &gwi) == ENX_E_FAILURE) {
616 		ENX_DPRINTF_DEBUG("eibnx_config_child: invalid eoib "
617 		    "nodename %s, no such address", devname);
618 		return (ENX_E_FAILURE);
619 	}
620 
621 	return (eibnx_configure_node(ti, gwi, childp));
622 }
623 
624 /*ARGSUSED*/
625 static int
eibnx_unconfig_child(char * devname)626 eibnx_unconfig_child(char *devname)
627 {
628 	eibnx_thr_info_t *ti;
629 	eibnx_gw_info_t *gwi;
630 
631 	if (eibnx_locate_node_name(devname, &ti, &gwi) == ENX_E_FAILURE) {
632 		ENX_DPRINTF_DEBUG("eibnx_unconfig_child: invalid eoib "
633 		    "nodename %s, no such address", devname);
634 		return (ENX_E_FAILURE);
635 	}
636 
637 	return (eibnx_unconfigure_node(ti, gwi));
638 }
639