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