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  * Copyright 2023 Oxide Computer Company
28  */
29 
30 #include <sys/types.h>
31 #include <sys/kmem.h>
32 #include <sys/ksynch.h>
33 #include <sys/conf.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/sunndi.h>
37 
38 #include <sys/ib/clients/eoib/enx_impl.h>
39 
40 static char *eibnx_make_nodename(eibnx_thr_info_t *, uint16_t);
41 
42 /*
43  * This routine is only called when the port-monitor thread is
44  * about to die.  Between the time the first mcast solicitation
45  * was done by the port-monitor thread and the time it is asked
46  * to die, a lot of things could've happened and we need to
47  * cleanup all of it.
48  */
49 void
eibnx_cleanup_port_nodes(eibnx_thr_info_t * info)50 eibnx_cleanup_port_nodes(eibnx_thr_info_t *info)
51 {
52 	eibnx_t *ss = enx_global_ss;
53 	eibnx_nodeq_t *node;
54 	eibnx_nodeq_t *prev;
55 	eibnx_gw_info_t *gwi;
56 	eibnx_gw_info_t *gw_list;
57 	eibnx_gw_info_t *nxt_gwi;
58 	eibnx_child_t *child;
59 	eibnx_child_t *nxt_child;
60 	eibnx_child_t *children;
61 
62 	/*
63 	 * Since we would've already stopped processing completions for
64 	 * this thread's work queue, we don't have to worry about requests
65 	 * coming in for creation of new eoib nodes.  However, there may
66 	 * be pending node creation requests for this port (thr_info)
67 	 * that we will have to drop.
68 	 */
69 	mutex_enter(&ss->nx_nodeq_lock);
70 	prev = NULL;
71 	for (node = ss->nx_nodeq; node; node = node->nc_next) {
72 		if (node->nc_info != info) {
73 			prev = node;
74 		} else {
75 			if (prev == NULL) {
76 				ss->nx_nodeq = node->nc_next;
77 			} else {
78 				prev->nc_next = node->nc_next;
79 			}
80 			kmem_free(node, sizeof (eibnx_nodeq_t));
81 		}
82 	}
83 	mutex_exit(&ss->nx_nodeq_lock);
84 
85 	/*
86 	 * Now go through the list of all children and free up any
87 	 * resource we might've allocated;  note that the child dips
88 	 * could've been offlined/removed by now, so we don't do
89 	 * anything with them.
90 	 */
91 	mutex_enter(&info->ti_child_lock);
92 	children = info->ti_child;
93 	info->ti_child = NULL;
94 	mutex_exit(&info->ti_child_lock);
95 
96 	for (child = children; child; child = nxt_child) {
97 		nxt_child = child->ch_next;
98 
99 		if (child->ch_node_name) {
100 			kmem_free(child->ch_node_name, MAXNAMELEN);
101 		}
102 		kmem_free(child, sizeof (eibnx_child_t));
103 	}
104 
105 	/*
106 	 * Return all the swqes we've acquired for the gateway unicast
107 	 * solicitations, free any address vectors we've allocated and
108 	 * finally free the gw entries from the list.
109 	 */
110 	mutex_enter(&info->ti_gw_lock);
111 	gw_list = info->ti_gw;
112 	info->ti_gw = NULL;
113 	mutex_exit(&info->ti_gw_lock);
114 
115 	for (gwi = gw_list; gwi; gwi = nxt_gwi) {
116 		nxt_gwi = gwi->gw_next;
117 
118 		eibnx_release_swqe((eibnx_wqe_t *)(gwi->gw_swqe));
119 		if ((gwi->gw_addr).ga_vect) {
120 			kmem_free((gwi->gw_addr).ga_vect,
121 			    sizeof (ibt_adds_vect_t));
122 			(gwi->gw_addr).ga_vect = NULL;
123 		}
124 		mutex_destroy(&gwi->gw_adv_lock);
125 
126 		kmem_free(gwi, sizeof (eibnx_gw_info_t));
127 	}
128 }
129 
130 /*
131  * Communicate all the details we received about the gateway (via the
132  * advertisement control message) to the eoib instance we're creating.
133  */
134 void
eibnx_create_node_props(dev_info_t * dip,eibnx_thr_info_t * info,eibnx_gw_info_t * gwi)135 eibnx_create_node_props(dev_info_t *dip, eibnx_thr_info_t *info,
136     eibnx_gw_info_t *gwi)
137 {
138 	int ret;
139 
140 	ret = ndi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_HCA_GUID,
141 	    info->ti_hca_guid);
142 	if (ret != DDI_PROP_SUCCESS) {
143 		ENX_DPRINTF_WARN("ndi_prop_update_int64() failed to set "
144 		    "%s property to 0x%llx for child dip 0x%llx, ret=%d",
145 		    EIB_PROP_HCA_GUID, info->ti_hca_guid, dip, ret);
146 	}
147 
148 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_HCA_PORTNUM,
149 	    info->ti_pi->p_port_num);
150 	if (ret != DDI_PROP_SUCCESS) {
151 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
152 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
153 		    EIB_PROP_HCA_PORTNUM, info->ti_pi->p_port_num, dip, ret);
154 	}
155 
156 	ret = ndi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SYS_GUID,
157 	    gwi->gw_system_guid);
158 	if (ret != DDI_PROP_SUCCESS) {
159 		ENX_DPRINTF_WARN("ndi_prop_update_int64() failed to set "
160 		    "%s property to 0x%llx for child dip 0x%llx, ret=%d",
161 		    EIB_PROP_GW_SYS_GUID, gwi->gw_system_guid, dip, ret);
162 	}
163 
164 	ret = ndi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_GUID,
165 	    gwi->gw_guid);
166 	if (ret != DDI_PROP_SUCCESS) {
167 		ENX_DPRINTF_WARN("ndi_prop_update_int64() failed to set "
168 		    "%s property to 0x%llx for child dip 0x%llx, ret=%d",
169 		    EIB_PROP_GW_GUID, gwi->gw_guid, dip, ret);
170 	}
171 
172 	ret = ndi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SN_PREFIX,
173 	    (gwi->gw_addr).ga_gid.gid_prefix);
174 	if (ret != DDI_PROP_SUCCESS) {
175 		ENX_DPRINTF_WARN("ndi_prop_update_int64() failed to set "
176 		    "%s property to 0x%llx for child dip 0x%llx, ret=%d",
177 		    EIB_PROP_GW_SN_PREFIX, (gwi->gw_addr).ga_gid.gid_prefix,
178 		    dip, ret);
179 	}
180 
181 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_ADV_PERIOD,
182 	    gwi->gw_adv_period);
183 	if (ret != DDI_PROP_SUCCESS) {
184 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
185 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
186 		    EIB_PROP_GW_ADV_PERIOD, gwi->gw_adv_period, dip, ret);
187 	}
188 
189 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_KA_PERIOD,
190 	    gwi->gw_ka_period);
191 	if (ret != DDI_PROP_SUCCESS) {
192 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
193 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
194 		    EIB_PROP_GW_KA_PERIOD, gwi->gw_ka_period, dip, ret);
195 	}
196 
197 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_VNIC_KA_PERIOD,
198 	    gwi->gw_vnic_ka_period);
199 	if (ret != DDI_PROP_SUCCESS) {
200 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
201 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
202 		    EIB_PROP_VNIC_KA_PERIOD, gwi->gw_vnic_ka_period, dip, ret);
203 	}
204 
205 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_CTRL_QPN,
206 	    gwi->gw_ctrl_qpn);
207 	if (ret != DDI_PROP_SUCCESS) {
208 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
209 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
210 		    EIB_PROP_GW_CTRL_QPN, gwi->gw_ctrl_qpn, dip, ret);
211 	}
212 
213 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_LID,
214 	    gwi->gw_lid);
215 	if (ret != DDI_PROP_SUCCESS) {
216 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
217 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
218 		    EIB_PROP_GW_LID, gwi->gw_lid, dip, ret);
219 	}
220 
221 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_PORTID,
222 	    gwi->gw_portid);
223 	if (ret != DDI_PROP_SUCCESS) {
224 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
225 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
226 		    EIB_PROP_GW_PORTID, gwi->gw_portid, dip, ret);
227 	}
228 
229 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
230 	    EIB_PROP_GW_NUM_NET_VNICS, gwi->gw_num_net_vnics);
231 	if (ret != DDI_PROP_SUCCESS) {
232 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
233 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
234 		    EIB_PROP_GW_NUM_NET_VNICS, gwi->gw_num_net_vnics, dip, ret);
235 	}
236 
237 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_AVAILABLE,
238 	    gwi->gw_flag_available);
239 	if (ret != DDI_PROP_SUCCESS) {
240 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
241 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
242 		    EIB_PROP_GW_AVAILABLE, gwi->gw_flag_available, dip, ret);
243 	}
244 
245 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_HOST_VNICS,
246 	    gwi->gw_is_host_adm_vnics);
247 	if (ret != DDI_PROP_SUCCESS) {
248 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
249 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
250 		    EIB_PROP_GW_HOST_VNICS, gwi->gw_is_host_adm_vnics,
251 		    dip, ret);
252 	}
253 
254 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SL,
255 	    gwi->gw_sl);
256 	if (ret != DDI_PROP_SUCCESS) {
257 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
258 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
259 		    EIB_PROP_GW_SL, gwi->gw_sl, dip, ret);
260 	}
261 
262 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_N_RSS_QPN,
263 	    gwi->gw_n_rss_qpn);
264 	if (ret != DDI_PROP_SUCCESS) {
265 		ENX_DPRINTF_WARN("ndi_prop_update_int() failed to set "
266 		    "%s property to 0x%lx for child dip 0x%llx, ret=%d",
267 		    EIB_PROP_GW_N_RSS_QPN, gwi->gw_n_rss_qpn, dip, ret);
268 	}
269 
270 	ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SYS_NAME,
271 	    (char *)(gwi->gw_system_name));
272 	if (ret != DDI_PROP_SUCCESS) {
273 		ENX_DPRINTF_WARN("ndi_prop_update_string() failed to set "
274 		    "%s property to '%s' for child dip 0x%llx, ret=%d",
275 		    EIB_PROP_GW_SYS_NAME, gwi->gw_system_name, dip, ret);
276 	}
277 
278 	ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, EIB_PROP_GW_PORT_NAME,
279 	    (char *)(gwi->gw_port_name));
280 	if (ret != DDI_PROP_SUCCESS) {
281 		ENX_DPRINTF_WARN("ndi_prop_update_string() failed to set "
282 		    "%s property to '%s' for child dip 0x%llx, ret=%d",
283 		    EIB_PROP_GW_PORT_NAME, gwi->gw_port_name, dip, ret);
284 	}
285 
286 	ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, EIB_PROP_GW_VENDOR_ID,
287 	    (char *)(gwi->gw_vendor_id));
288 	if (ret != DDI_PROP_SUCCESS) {
289 		ENX_DPRINTF_WARN("ndi_prop_update_string() failed to set "
290 		    "%s property to '%s' for child dip 0x%llx, ret=%d",
291 		    EIB_PROP_GW_VENDOR_ID, gwi->gw_vendor_id, dip, ret);
292 	}
293 }
294 
295 int
eibnx_name_child(dev_info_t * child,char * name,size_t namesz)296 eibnx_name_child(dev_info_t *child, char *name, size_t namesz)
297 {
298 	char *node_name;
299 
300 	if ((node_name = ddi_get_parent_data(child)) == NULL) {
301 		ENX_DPRINTF_ERR("ddi_get_parent_data(child=0x%llx) "
302 		    "returned NULL", child);
303 		return (DDI_NOT_WELL_FORMED);
304 	}
305 
306 	/*
307 	 * Skip the name and "@" part in the eoib node path and copy the
308 	 * address part out to the caller.
309 	 */
310 	(void) strlcpy(name, node_name + strlen(EIB_DRV_NAME) + 1, namesz);
311 
312 	return (DDI_SUCCESS);
313 }
314 
315 /*
316  * Synchronization functions to mark/clear the in-progress status of
317  * bus config/unconfig operations
318  */
319 
320 void
eibnx_busop_inprog_enter(eibnx_t * ss)321 eibnx_busop_inprog_enter(eibnx_t *ss)
322 {
323 	mutex_enter(&ss->nx_busop_lock);
324 
325 	while (ss->nx_busop_flags & NX_FL_BUSOP_INPROG)
326 		cv_wait(&ss->nx_busop_cv, &ss->nx_busop_lock);
327 
328 	ss->nx_busop_flags |= NX_FL_BUSOP_INPROG;
329 
330 	mutex_exit(&ss->nx_busop_lock);
331 }
332 
333 void
eibnx_busop_inprog_exit(eibnx_t * ss)334 eibnx_busop_inprog_exit(eibnx_t *ss)
335 {
336 	mutex_enter(&ss->nx_busop_lock);
337 
338 	ss->nx_busop_flags &= (~NX_FL_BUSOP_INPROG);
339 
340 	cv_broadcast(&ss->nx_busop_cv);
341 	mutex_exit(&ss->nx_busop_lock);
342 }
343 
344 eibnx_thr_info_t *
eibnx_start_port_monitor(eibnx_hca_t * hca,eibnx_port_t * port)345 eibnx_start_port_monitor(eibnx_hca_t *hca, eibnx_port_t *port)
346 {
347 	eibnx_thr_info_t *ti;
348 	kthread_t *kt;
349 	dev_info_t *hca_dip;
350 	const char *hca_drv_name;
351 	int hca_drv_inst;
352 
353 	ti = kmem_zalloc(sizeof (eibnx_thr_info_t), KM_SLEEP);
354 
355 	mutex_init(&ti->ti_mcg_lock, NULL, MUTEX_DRIVER, NULL);
356 	mutex_init(&ti->ti_gw_lock, NULL, MUTEX_DRIVER, NULL);
357 	mutex_init(&ti->ti_child_lock, NULL, MUTEX_DRIVER, NULL);
358 	mutex_init(&ti->ti_event_lock, NULL, MUTEX_DRIVER, NULL);
359 	cv_init(&ti->ti_event_cv, NULL, CV_DEFAULT, NULL);
360 
361 	ti->ti_next = NULL;
362 	ti->ti_hca_guid = hca->hc_guid;
363 	ti->ti_hca = hca->hc_hdl;
364 	ti->ti_pd = hca->hc_pd;
365 	ti->ti_pi = port->po_pi;
366 	ti->ti_ident = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
367 
368 	/*
369 	 * Prepare the "ident" for EoIB nodes from this port monitor.  To
370 	 * associate eoib instances with the corresponding HCA nodes easily,
371 	 * and to make sure eoib instance numbers do not change when
372 	 * like-for-like HCA replacements are made, tie up the ident to
373 	 * HCA driver name, HCA driver instance and the HCA port number.
374 	 * The eoib node address is later composed using this ident and
375 	 * the gateway port ids after discovery.
376 	 */
377 	if ((hca_dip = ibtl_ibnex_hcaguid2dip(ti->ti_hca_guid)) == NULL) {
378 		ENX_DPRINTF_WARN("ibtl_ibnex_hcaguid2dip(hca_guid=0x%llx) "
379 		    "returned NULL", ti->ti_hca_guid);
380 	} else if ((hca_drv_name = ddi_driver_name(hca_dip)) == NULL) {
381 		ENX_DPRINTF_WARN("hca driver name NULL for "
382 		    "hca_guid=0x%llx, hca_dip=0x%llx",
383 		    ti->ti_hca_guid, hca_dip);
384 	} else if ((hca_drv_inst = ddi_get_instance(hca_dip)) < 0) {
385 		ENX_DPRINTF_ERR("hca driver instance (%d) invalid for "
386 		    "hca_guid=0x%llx, hca_dip=0x%llx",
387 		    ti->ti_hca_guid, hca_dip);
388 	} else {
389 		(void) snprintf(ti->ti_ident, MAXNAMELEN, "%s%d,%x",
390 		    hca_drv_name, hca_drv_inst, ti->ti_pi->p_port_num);
391 	}
392 
393 	kt = thread_create(NULL, 0, eibnx_port_monitor,
394 	    ti, 0, &p0, TS_RUN, minclsyspri);
395 
396 	ti->ti_kt_did = kt->t_did;
397 
398 	return (ti);
399 }
400 
401 void
eibnx_stop_port_monitor(eibnx_thr_info_t * ti)402 eibnx_stop_port_monitor(eibnx_thr_info_t *ti)
403 {
404 	/*
405 	 * Tell the port monitor thread to stop and wait for it to
406 	 * happen.  Before marking it for death, make sure there
407 	 * aren't any completions being processed.
408 	 */
409 	mutex_enter(&ti->ti_event_lock);
410 	while (ti->ti_event & ENX_EVENT_COMPLETION) {
411 		cv_wait(&ti->ti_event_cv, &ti->ti_event_lock);
412 	}
413 	ti->ti_event |= ENX_EVENT_DIE;
414 	cv_broadcast(&ti->ti_event_cv);
415 	mutex_exit(&ti->ti_event_lock);
416 
417 	thread_join(ti->ti_kt_did);
418 
419 	/*
420 	 * Destroy synchronization primitives initialized for this ti
421 	 */
422 	cv_destroy(&ti->ti_event_cv);
423 	mutex_destroy(&ti->ti_event_lock);
424 	mutex_destroy(&ti->ti_child_lock);
425 	mutex_destroy(&ti->ti_gw_lock);
426 	mutex_destroy(&ti->ti_mcg_lock);
427 
428 	kmem_free(ti->ti_ident, MAXNAMELEN);
429 	kmem_free(ti, sizeof (eibnx_thr_info_t));
430 }
431 
432 void
eibnx_terminate_monitors(void)433 eibnx_terminate_monitors(void)
434 {
435 	eibnx_t *ss = enx_global_ss;
436 	eibnx_thr_info_t *ti_list;
437 	eibnx_thr_info_t *ti;
438 	eibnx_thr_info_t *ti_next;
439 
440 	mutex_enter(&ss->nx_lock);
441 	ti_list = ss->nx_thr_info;
442 	ss->nx_thr_info = NULL;
443 	mutex_exit(&ss->nx_lock);
444 
445 	/*
446 	 * Ask all the port_monitor threads to die. Before marking them
447 	 * for death, make sure there aren't any completions being
448 	 * processed by the thread.
449 	 */
450 	for (ti = ti_list; ti; ti = ti_next) {
451 		ti_next = ti->ti_next;
452 		eibnx_stop_port_monitor(ti);
453 	}
454 
455 	mutex_enter(&ss->nx_lock);
456 	ss->nx_monitors_up = B_FALSE;
457 	mutex_exit(&ss->nx_lock);
458 }
459 
460 int
eibnx_configure_node(eibnx_thr_info_t * ti,eibnx_gw_info_t * gwi,dev_info_t ** childp)461 eibnx_configure_node(eibnx_thr_info_t *ti, eibnx_gw_info_t *gwi,
462     dev_info_t **childp)
463 {
464 	eibnx_t *ss = enx_global_ss;
465 	dev_info_t *child_dip;
466 	char *node_name;
467 	int ret;
468 
469 	/*
470 	 * Prepare the new node's name
471 	 */
472 	if ((node_name = eibnx_make_nodename(ti, gwi->gw_portid)) == NULL)
473 		return (ENX_E_FAILURE);
474 
475 	ndi_devi_enter(ss->nx_dip);
476 
477 	if (child_dip = ndi_devi_findchild(ss->nx_dip, node_name)) {
478 		ret = eibnx_update_child(ti, gwi, child_dip);
479 		if (ret == ENX_E_SUCCESS) {
480 			ndi_devi_exit(ss->nx_dip);
481 			kmem_free(node_name, MAXNAMELEN);
482 
483 			if (childp) {
484 				*childp = child_dip;
485 			}
486 			return (ENX_E_SUCCESS);
487 		}
488 	}
489 
490 	/*
491 	 * If the node does not already exist, we may need to create it
492 	 */
493 	if (child_dip == NULL) {
494 		ndi_devi_alloc_sleep(ss->nx_dip, EIB_DRV_NAME,
495 		    (pnode_t)DEVI_SID_NODEID, &child_dip);
496 
497 		ddi_set_parent_data(child_dip, node_name);
498 		eibnx_create_node_props(child_dip, ti, gwi);
499 	}
500 
501 	/*
502 	 * Whether there was no devinfo node at all for the given node name or
503 	 * we had a devinfo node, but it wasn't in our list of eoib children,
504 	 * we'll try to online the instance here.
505 	 */
506 	ENX_DPRINTF_DEBUG("onlining %s", node_name);
507 	ret = ndi_devi_online(child_dip, 0);
508 	if (ret != NDI_SUCCESS) {
509 		ENX_DPRINTF_ERR("ndi_devi_online(node_name=%s) failed "
510 		    "with ret=0x%x", node_name, ret);
511 
512 		ddi_set_parent_data(child_dip, NULL);
513 		(void) ndi_devi_free(child_dip);
514 
515 		ndi_devi_exit(ss->nx_dip);
516 		kmem_free(node_name, MAXNAMELEN);
517 
518 		return (ENX_E_FAILURE);
519 	}
520 
521 	eibnx_enqueue_child(ti, gwi, node_name, child_dip);
522 
523 	ndi_devi_exit(ss->nx_dip);
524 
525 	if (childp) {
526 		*childp = child_dip;
527 	}
528 
529 	return (ENX_E_SUCCESS);
530 }
531 
532 int
eibnx_unconfigure_node(eibnx_thr_info_t * ti,eibnx_gw_info_t * gwi)533 eibnx_unconfigure_node(eibnx_thr_info_t *ti, eibnx_gw_info_t *gwi)
534 {
535 	/*
536 	 * To unconfigure an eoib node, we only need to set the child's
537 	 * dip to NULL.  When the node gets configured again, we either
538 	 * find the dip for the pathname and set it in this child, or
539 	 * allocate a new dip and set it in this child.
540 	 */
541 	return (eibnx_update_child(ti, gwi, NULL));
542 }
543 
544 int
eibnx_locate_node_name(char * devname,eibnx_thr_info_t ** ti_p,eibnx_gw_info_t ** gwi_p)545 eibnx_locate_node_name(char *devname, eibnx_thr_info_t **ti_p,
546     eibnx_gw_info_t **gwi_p)
547 {
548 	eibnx_t *ss = enx_global_ss;
549 	eibnx_thr_info_t *ti;
550 	eibnx_gw_info_t *gwi;
551 	char name[MAXNAMELEN];
552 
553 	/*
554 	 * Locate the port monitor thread info and gateway info
555 	 * that corresponds to the supplied devname.
556 	 */
557 	mutex_enter(&ss->nx_lock);
558 	for (ti = ss->nx_thr_info; ti; ti = ti->ti_next) {
559 		if (ti->ti_ident[0] == '\0')
560 			continue;
561 
562 		mutex_enter(&ti->ti_gw_lock);
563 		for (gwi = ti->ti_gw; gwi; gwi = gwi->gw_next) {
564 			(void) snprintf(name, MAXNAMELEN,
565 			    "%s@%s,%x", EIB_DRV_NAME, ti->ti_ident,
566 			    gwi->gw_portid);
567 
568 			if (strcmp(name, devname) == 0)
569 				break;
570 		}
571 		mutex_exit(&ti->ti_gw_lock);
572 
573 		if (gwi) {
574 			break;
575 		}
576 	}
577 	mutex_exit(&ss->nx_lock);
578 
579 	if (ti == NULL || gwi == NULL) {
580 		return (ENX_E_FAILURE);
581 	}
582 
583 	*ti_p = ti;
584 	*gwi_p = gwi;
585 
586 	return (ENX_E_SUCCESS);
587 }
588 
589 int
eibnx_locate_unconfigured_node(eibnx_thr_info_t ** ti_p,eibnx_gw_info_t ** gwi_p)590 eibnx_locate_unconfigured_node(eibnx_thr_info_t **ti_p, eibnx_gw_info_t **gwi_p)
591 {
592 	eibnx_t *ss = enx_global_ss;
593 	eibnx_thr_info_t *ti;
594 	eibnx_child_t *ch;
595 
596 	mutex_enter(&ss->nx_lock);
597 	for (ti = ss->nx_thr_info; ti; ti = ti->ti_next) {
598 		mutex_enter(&ti->ti_child_lock);
599 		for (ch = ti->ti_child; ch; ch = ch->ch_next) {
600 			if (ch->ch_dip == NULL) {
601 				*ti_p = ti;
602 				*gwi_p = ch->ch_gwi;
603 
604 				mutex_exit(&ti->ti_child_lock);
605 				mutex_exit(&ss->nx_lock);
606 
607 				return (ENX_E_SUCCESS);
608 			}
609 		}
610 		mutex_exit(&ti->ti_child_lock);
611 	}
612 	mutex_exit(&ss->nx_lock);
613 
614 	return (ENX_E_FAILURE);
615 }
616 
617 static char *
eibnx_make_nodename(eibnx_thr_info_t * info,uint16_t gw_portid)618 eibnx_make_nodename(eibnx_thr_info_t *info, uint16_t gw_portid)
619 {
620 	char *name;
621 
622 	if (info->ti_ident[0] == '\0')
623 		return (NULL);
624 
625 	name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
626 	(void) snprintf(name, MAXNAMELEN, "%s@%s,%x", EIB_DRV_NAME,
627 	    info->ti_ident, gw_portid);
628 
629 	return (name);
630 }
631