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 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 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 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 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 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 * 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 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 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 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 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 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 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 * 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