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