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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * Solaris SCSI RDMA Protocol Target (SRP) transport port provider
  28  * module for the COMSTAR framework.
  29  */
  30 
  31 #include <sys/cpuvar.h>
  32 #include <sys/types.h>
  33 #include <sys/conf.h>
  34 #include <sys/stat.h>
  35 #include <sys/file.h>
  36 #include <sys/ddi.h>
  37 #include <sys/sunddi.h>
  38 #include <sys/modctl.h>
  39 #include <sys/sysmacros.h>
  40 #include <sys/sdt.h>
  41 #include <sys/taskq.h>
  42 
  43 #include <sys/stmf.h>
  44 #include <sys/stmf_ioctl.h>
  45 #include <sys/portif.h>
  46 
  47 #include "srp.h"
  48 #include "srpt_impl.h"
  49 #include "srpt_ioc.h"
  50 #include "srpt_stp.h"
  51 #include "srpt_cm.h"
  52 #include "srpt_ioctl.h"
  53 #include "srpt_common.h"
  54 
  55 #define SRPT_NAME_VERSION       "COMSTAR SRP Target"
  56 
  57 /*
  58  * srpt_enable_by_default - configurable parameter that
  59  * determines whether targets are created automatically for
  60  * all HCAs when the service is enabled.
  61  *
  62  * B_TRUE is the legacy default as srpt originally shipped
  63  * this way.  Changing it to false is highly desirable.
  64  */
  65 boolean_t       srpt_enable_by_default = B_TRUE;
  66 
  67 /*
  68  * srpt_send_msg_depth - Tunable parameter that specifies the
  69  * maximum messages that could be in flight for a channel.
  70  */
  71 uint16_t        srpt_send_msg_depth = SRPT_DEFAULT_SEND_MSG_DEPTH;
  72 
  73 /*
  74  * srpt_errlevel -- determine which error conditions are logged
  75  */
  76 uint_t          srpt_errlevel = SRPT_LOG_DEFAULT_LEVEL;
  77 
  78 /*
  79  * srpt_iu_size -- must be a multiple of 64 as it is registered
  80  * as memory regions with IB.  To support a scatter/gather table
  81  * size of 32, the size must be at not less than 960.  To support
  82  * the maximum scatter/gather table size of 255, the IU must
  83  * be at least 4160 bytes.
  84  */
  85 uint32_t        srpt_iu_size = SRPT_DEFAULT_SEND_MSG_SIZE;
  86 
  87 srpt_ctxt_t     *srpt_ctxt;
  88 
  89 /*
  90  * DDI entry points.
  91  */
  92 static int srpt_drv_attach(dev_info_t *, ddi_attach_cmd_t);
  93 static int srpt_drv_detach(dev_info_t *, ddi_detach_cmd_t);
  94 static int srpt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
  95 static int srpt_drv_open(dev_t *, int, int, cred_t *);
  96 static int srpt_drv_close(dev_t, int, int, cred_t *);
  97 static int srpt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  98 
  99 /* helper functions */
 100 static int srpt_disable_srp_services(void);
 101 static int srpt_enable_srp_services(void);
 102 static int srpt_ibdma_ops_load(srpt_ibdma_ops_t *);
 103 static void srpt_ibdma_ops_unload(srpt_ibdma_ops_t *);
 104 
 105 extern struct mod_ops mod_miscops;
 106 
 107 static struct cb_ops srpt_cb_ops = {
 108         srpt_drv_open,          /* cb_open */
 109         srpt_drv_close,         /* cb_close */
 110         nodev,                  /* cb_strategy */
 111         nodev,                  /* cb_print */
 112         nodev,                  /* cb_dump */
 113         nodev,                  /* cb_read */
 114         nodev,                  /* cb_write */
 115         srpt_drv_ioctl,         /* cb_ioctl */
 116         nodev,                  /* cb_devmap */
 117         nodev,                  /* cb_mmap */
 118         nodev,                  /* cb_segmap */
 119         nochpoll,               /* cb_chpoll */
 120         ddi_prop_op,            /* cb_prop_op */
 121         NULL,                   /* cb_streamtab */
 122         D_MP,                   /* cb_flag */
 123         CB_REV,                 /* cb_rev */
 124         nodev,                  /* cb_aread */
 125         nodev,                  /* cb_awrite */
 126 };
 127 
 128 static struct dev_ops srpt_dev_ops = {
 129         DEVO_REV,               /* devo_rev */
 130         0,                      /* devo_refcnt */
 131         srpt_drv_getinfo,       /* devo_getinfo */
 132         nulldev,                /* devo_identify */
 133         nulldev,                /* devo_probe */
 134         srpt_drv_attach,        /* devo_attach */
 135         srpt_drv_detach,        /* devo_detach */
 136         nodev,                  /* devo_reset */
 137         &srpt_cb_ops,               /* devo_cb_ops */
 138         NULL,                   /* devo_bus_ops */
 139         NULL,                   /* devo_power */
 140         ddi_quiesce_not_needed, /* quiesce */
 141 };
 142 
 143 static struct modldrv modldrv = {
 144         &mod_driverops,
 145         SRPT_NAME_VERSION,
 146         &srpt_dev_ops,
 147 };
 148 
 149 static struct modlinkage srpt_modlinkage = {
 150         MODREV_1,
 151         { &modldrv, NULL }
 152 };
 153 
 154 static char srpt_pp_name[] = "srpt";
 155 
 156 /*
 157  * Prototypes
 158  */
 159 static void srpt_pp_cb(stmf_port_provider_t *, int, void *, uint32_t);
 160 
 161 /*
 162  * _init()
 163  */
 164 int
 165 _init(void)
 166 {
 167         int status;
 168 
 169         /*
 170          * Global one time initialization.
 171          */
 172         srpt_ctxt = kmem_zalloc(sizeof (srpt_ctxt_t), KM_SLEEP);
 173         ASSERT(srpt_ctxt != NULL);
 174         rw_init(&srpt_ctxt->sc_rwlock, NULL, RW_DRIVER, NULL);
 175 
 176         /* Start-up state is DISABLED.  SMF will tell us if we should enable. */
 177         srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
 178         list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
 179             offsetof(srpt_ioc_t, ioc_node));
 180 
 181         list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
 182             offsetof(srpt_ioc_t, ioc_node));
 183 
 184         status = mod_install(&srpt_modlinkage);
 185         if (status != DDI_SUCCESS) {
 186                 cmn_err(CE_CONT, "_init, failed mod_install %d", status);
 187                 rw_destroy(&srpt_ctxt->sc_rwlock);
 188                 kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
 189                 srpt_ctxt = NULL;
 190         }
 191 
 192         return (status);
 193 }
 194 
 195 /*
 196  * _info()
 197  */
 198 int
 199 _info(struct modinfo *modinfop)
 200 {
 201         return (mod_info(&srpt_modlinkage, modinfop));
 202 }
 203 
 204 /*
 205  * _fini()
 206  */
 207 int
 208 _fini(void)
 209 {
 210         int status;
 211 
 212         status = mod_remove(&srpt_modlinkage);
 213         if (status != DDI_SUCCESS) {
 214                 return (status);
 215         }
 216 
 217         list_destroy(&srpt_ctxt->sc_ioc_list);
 218 
 219         rw_destroy(&srpt_ctxt->sc_rwlock);
 220         kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
 221         srpt_ctxt = NULL;
 222 
 223         return (status);
 224 }
 225 
 226 /*
 227  * DDI entry points.
 228  */
 229 
 230 /*
 231  * srpt_getinfo()
 232  */
 233 /* ARGSUSED */
 234 static int
 235 srpt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 236 {
 237 
 238         switch (cmd) {
 239         case DDI_INFO_DEVT2DEVINFO:
 240                 *result = srpt_ctxt->sc_dip;
 241                 return (DDI_SUCCESS);
 242 
 243         case DDI_INFO_DEVT2INSTANCE:
 244                 *result = NULL;
 245                 return (DDI_SUCCESS);
 246 
 247         default:
 248                 break;
 249         }
 250         return (DDI_FAILURE);
 251 }
 252 
 253 /*
 254  * srpt_drv_attach()
 255  */
 256 static int
 257 srpt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 258 {
 259         int             status;
 260 
 261         switch (cmd) {
 262         case DDI_ATTACH:
 263                 break;
 264 
 265         case DDI_RESUME:
 266                 return (DDI_SUCCESS);
 267 
 268         default:
 269                 return (DDI_FAILURE);
 270         }
 271 
 272         /*
 273          * We only allow a single instance.
 274          */
 275         if (ddi_get_instance(dip) != 0) {
 276                 SRPT_DPRINTF_L1("drv_attach, error non-zero instance");
 277                 return (DDI_FAILURE);
 278         }
 279 
 280         /*
 281          * Create minor node that might ultimately be used to create
 282          * targets outside of srpt.
 283          */
 284         status = ddi_create_minor_node(dip, ddi_get_name(dip),
 285             S_IFCHR, 0, DDI_PSEUDO, 0);
 286         if (status != DDI_SUCCESS) {
 287                 SRPT_DPRINTF_L1("drv_attach, minor node creation error (%d)",
 288                     status);
 289                 return (DDI_FAILURE);
 290         }
 291 
 292         rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 293         srpt_ctxt->sc_dip = dip;
 294         rw_exit(&srpt_ctxt->sc_rwlock);
 295 
 296         return (DDI_SUCCESS);
 297 }
 298 
 299 /*
 300  * srpt_enable_srp_services()
 301  *
 302  * Caller must be holding the sc_rwlock as RW_WRITER.
 303  */
 304 static int
 305 srpt_enable_srp_services(void)
 306 {
 307         int             status;
 308 
 309         ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
 310 
 311         SRPT_DPRINTF_L3("srpt_enable_srp_services");
 312 
 313         /* Register the port provider */
 314         srpt_ctxt->sc_pp = (stmf_port_provider_t *)
 315             stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
 316         srpt_ctxt->sc_pp->pp_portif_rev = PORTIF_REV_1;
 317         srpt_ctxt->sc_pp->pp_name = srpt_pp_name;
 318         srpt_ctxt->sc_pp->pp_cb   = srpt_pp_cb;
 319         status = stmf_register_port_provider(srpt_ctxt->sc_pp);
 320         if (status != STMF_SUCCESS) {
 321                 SRPT_DPRINTF_L1("enable_srp: SRP port_provider registration"
 322                     " failed(%d)", status);
 323                 goto err_exit_1;
 324         }
 325 
 326         /*
 327          * Initialize IB resources, creating a list of SRP I/O Controllers
 328          * and for each controller, register the SCSI Target Port with STMF
 329          * and prepare profile and services.
 330          */
 331         status = srpt_ioc_attach();
 332         if (status != DDI_SUCCESS) {
 333                 SRPT_DPRINTF_L1("enable_srp: error attach I/O"
 334                     " Controllers (%d)", status);
 335                 goto err_exit_2;
 336         }
 337 
 338         /*
 339          * No configured controllers is not a fatal error.  This can happen
 340          * if all HCAs are currently disabled for use by SRP.  The service
 341          * should remain running in case the user changes their mind and
 342          * enables an HCA for SRP services.
 343          */
 344         if (srpt_ctxt->sc_num_iocs == 0) {
 345                 SRPT_DPRINTF_L2("enable_srp: no IB I/O Controllers found");
 346                 return (DDI_SUCCESS);
 347         }
 348 
 349         return (DDI_SUCCESS);
 350 
 351 err_exit_2:
 352         (void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
 353 
 354 err_exit_1:
 355         stmf_free(srpt_ctxt->sc_pp);
 356         srpt_ctxt->sc_pp = NULL;
 357 
 358         return (status);
 359 }
 360 
 361 /*
 362  * srpt_drv_detach()
 363  *
 364  * Refuse the detach request if we have channels open on
 365  * any IOC.  Users should use 'svcadm disable' to shutdown
 366  * active targets.
 367  */
 368 /*ARGSUSED*/
 369 static int
 370 srpt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 371 {
 372         switch (cmd) {
 373         case DDI_DETACH:
 374                 rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 375                 if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
 376                         rw_exit(&srpt_ctxt->sc_rwlock);
 377                         return (DDI_FAILURE);
 378                 }
 379 
 380                 ddi_remove_minor_node(dip, NULL);
 381                 srpt_ctxt->sc_dip = NULL;
 382 
 383                 if (srpt_ctxt->sc_cfg_hca_nv != NULL) {
 384                         nvlist_free(srpt_ctxt->sc_cfg_hca_nv);
 385                         srpt_ctxt->sc_cfg_hca_nv = NULL;
 386                 }
 387 
 388                 rw_exit(&srpt_ctxt->sc_rwlock);
 389 
 390                 break;
 391 
 392         case DDI_SUSPEND:
 393                 return (DDI_FAILURE);
 394 
 395         default:
 396                 return (DDI_FAILURE);
 397         }
 398 
 399         return (DDI_SUCCESS);
 400 }
 401 
 402 /*
 403  * srpt_disable_srp_services()
 404  *
 405  * Offlines all targets, deregisters all IOCs.  Caller must hold
 406  * the srpt_ctxt->sc_rwlock as RW_WRITER.
 407  */
 408 static int
 409 srpt_disable_srp_services(void)
 410 {
 411         stmf_status_t                   stmf_status;
 412         srpt_ioc_t                      *ioc;
 413         srpt_target_port_t              *tgt;
 414         int                             ret_status = 0;
 415 
 416         ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
 417 
 418         /*
 419          * For each I/O Controller remove all SRP services and de-register
 420          * with the associated I/O Unit's IB Device Management Agent.
 421          */
 422         ioc = list_head(&srpt_ctxt->sc_ioc_list);
 423 
 424         while (ioc != NULL) {
 425                 rw_enter(&ioc->ioc_rwlock, RW_WRITER);
 426 
 427                 tgt = ioc->ioc_tgt_port;
 428                 if (tgt != NULL) {
 429                         stmf_status = srpt_stp_destroy_port(tgt);
 430                         if (stmf_status == STMF_SUCCESS) {
 431                                 ioc->ioc_tgt_port = NULL;
 432                                 (void) srpt_stp_free_port(tgt);
 433                         } else {
 434                                 ret_status = DDI_FAILURE;
 435                                 break;
 436                         }
 437                 }
 438 
 439                 rw_exit(&ioc->ioc_rwlock);
 440                 ioc = list_next(&srpt_ctxt->sc_ioc_list, ioc);
 441         }
 442 
 443         /* don't release IOCs until all ports are deregistered */
 444         if (ret_status != 0) {
 445                 return (ret_status);
 446         }
 447 
 448         /*
 449          * Release I/O Controller(s) resources and detach.
 450          */
 451         srpt_ioc_detach();
 452 
 453         /* De-register ourselves as an STMF port provider */
 454         (void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
 455         stmf_free(srpt_ctxt->sc_pp);
 456         srpt_ctxt->sc_pp = NULL;
 457 
 458         return (0);
 459 }
 460 
 461 /*
 462  * srpt_drv_open()
 463  */
 464 /* ARGSUSED */
 465 static int
 466 srpt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 467 {
 468         SRPT_DPRINTF_L3("drv_open, invoked");
 469         return (0);
 470 }
 471 
 472 /*
 473  * srpt_drv_close()
 474  */
 475 /* ARGSUSED */
 476 static int
 477 srpt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
 478 {
 479         SRPT_DPRINTF_L3("drv_close, invoked");
 480         return (0);
 481 }
 482 
 483 /*
 484  * srpt_drv_ioctl()
 485  */
 486 /* ARGSUSED */
 487 static int
 488 srpt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
 489     int *retval)
 490 {
 491         int             ret = 0;
 492 
 493         SRPT_DPRINTF_L3("drv_ioctl, invoked, cmd = %d", cmd);
 494 
 495         if (drv_priv(cred) != 0) {
 496                 return (EPERM);
 497         }
 498 
 499         rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 500 
 501         switch (cmd) {
 502                 case SRPT_IOC_ENABLE_SVC:
 503                         if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
 504                                 break;
 505                         }
 506 
 507                         ret = srpt_ibdma_ops_load(&srpt_ctxt->sc_ibdma_ops);
 508                         if (ret != 0) {
 509                                 break;
 510                         }
 511 
 512                         ret = srpt_enable_srp_services();
 513                         if (ret == 0) {
 514                                 srpt_ctxt->sc_svc_state = SRPT_SVC_ENABLED;
 515                         }
 516 
 517                         break;
 518 
 519                 case SRPT_IOC_DISABLE_SVC:
 520                         if (srpt_ctxt->sc_svc_state != SRPT_SVC_ENABLED) {
 521                                 break;
 522                         }
 523 
 524                         ret = srpt_disable_srp_services();
 525                         if (ret == 0) {
 526                                 srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
 527                         }
 528 
 529                         srpt_ibdma_ops_unload(&srpt_ctxt->sc_ibdma_ops);
 530 
 531                         break;
 532 
 533                 default:
 534                         ret = EFAULT;
 535                         break;
 536         }
 537 
 538         rw_exit(&srpt_ctxt->sc_rwlock);
 539 
 540         return (ret);
 541 }
 542 
 543 /*
 544  * srpt_pp_cb()
 545  */
 546 /* ARGSUSED */
 547 static void
 548 srpt_pp_cb(stmf_port_provider_t *pp, int cmd, void *arg, uint32_t flags)
 549 {
 550         int             ret;
 551         nvlist_t        *in_nvl = (nvlist_t *)arg;
 552         nvlist_t        *nvl = NULL;
 553         nvlist_t        *hcalist;
 554         nvlist_t        *ctxt_nvl;
 555         boolean_t       defaultEnabled = B_TRUE;
 556         boolean_t       called_by_reg = B_TRUE;
 557 
 558         SRPT_DPRINTF_L2("srpt_pp_cb, invoked (%d)", cmd);
 559 
 560         if (cmd != STMF_PROVIDER_DATA_UPDATED) {
 561                 return;
 562         }
 563 
 564         /*
 565          * If STMF_PCB_PREG_COMPLETE is set in the flags, we're being
 566          * called back during provider registration with STMF.
 567          * (while we're calling stmf_register_port_provider()).
 568          * srpt_enable_service() already holds the sc_wrlock, and will
 569          * make sure the configuration is activated, so we just need to
 570          * set the config and get out.  If this function is called at any
 571          * time other than SRPT service start, need to grab the sc_wrlock
 572          * as WRITER.
 573          */
 574         if (!(flags & STMF_PCB_PREG_COMPLETE)) {
 575                 SRPT_DPRINTF_L2(
 576                     "srpt_pp_cb:  called after registration");
 577                 called_by_reg = B_FALSE;
 578                 rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 579         } else {
 580                 called_by_reg = B_TRUE;
 581                 SRPT_DPRINTF_L2(
 582                     "srpt_pp_cb:  called as part of registration");
 583         }
 584 
 585         if (in_nvl != NULL) {
 586                 /* copy nvlist */
 587                 ret = nvlist_lookup_nvlist(in_nvl, SRPT_PROP_HCALIST, &hcalist);
 588                 if (ret != 0) {
 589                         SRPT_DPRINTF_L1(
 590                             "srpt_pp_cb: Could not read hca config, err=%d",
 591                             ret);
 592                         return;
 593                 }
 594 
 595                 ret = nvlist_dup(hcalist, &nvl, 0);
 596                 if (ret != 0) {
 597                         SRPT_DPRINTF_L1(
 598                             "srpt_pp_cb: Could not copy hca config, err=%d",
 599                             ret);
 600                         return;
 601                 }
 602                 if (nvlist_lookup_boolean_value(in_nvl,
 603                     SRPT_PROP_DEFAULT_ENABLED, &defaultEnabled) == 0) {
 604                         /* set whether targets are created by default */
 605                         SRPT_DPRINTF_L2(
 606                             "srpt_pp_cb:  setting default enabled = %d\n",
 607                             (int)defaultEnabled);
 608                         srpt_enable_by_default = defaultEnabled;
 609                 }
 610         } else {
 611                 SRPT_DPRINTF_L2(
 612                     "srpt_pp_cb:  null config received");
 613         }
 614 
 615         /* put list in ctxt and set default state */
 616         ctxt_nvl = srpt_ctxt->sc_cfg_hca_nv;
 617 
 618         /* set new config, NULL is valid */
 619         srpt_ctxt->sc_cfg_hca_nv = nvl;
 620 
 621         /* free the old nvlist */
 622         if (ctxt_nvl != NULL) {
 623                 nvlist_free(ctxt_nvl);
 624         }
 625 
 626         if (called_by_reg) {
 627                 return;
 628         }
 629 
 630         /* Update the HCA based on the new config */
 631         srpt_ioc_update();
 632 
 633         rw_exit(&srpt_ctxt->sc_rwlock);
 634 }
 635 
 636 static int
 637 srpt_ibdma_ops_load(srpt_ibdma_ops_t *ops)
 638 {
 639         int                     ibdma_err = 0;
 640 
 641         ASSERT(ops != NULL);
 642 
 643         ops->ibdmah = ddi_modopen("ibdma", KRTLD_MODE_FIRST, &ibdma_err);
 644         if (ops->ibdmah == NULL) {
 645                 SRPT_DPRINTF_L0("failed to open ibdma driver, error = %d",
 646                     ibdma_err);
 647                 return (ibdma_err);
 648         }
 649 
 650         ops->ibdma_register = (ibdma_hdl_t (*)())ddi_modsym(ops->ibdmah,
 651             "ibdma_ioc_register", &ibdma_err);
 652         if (ops->ibdma_register == NULL) {
 653                 SRPT_DPRINTF_L0(
 654                     "failed to modsym ibdma_ioc_register, error = %d",
 655                     ibdma_err);
 656                 goto done;
 657         }
 658 
 659         ops->ibdma_unregister = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
 660             "ibdma_ioc_unregister", &ibdma_err);
 661         if (ops->ibdma_unregister == NULL) {
 662                 SRPT_DPRINTF_L0(
 663                     "failed to modsym ibdma_ioc_unregister, error = %d",
 664                     ibdma_err);
 665                 goto done;
 666         }
 667 
 668         ops->ibdma_update = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
 669             "ibdma_ioc_update", &ibdma_err);
 670         if (ops->ibdma_update == NULL) {
 671                 SRPT_DPRINTF_L0(
 672                     "failed to modsym ibdma_ioc_update, error = %d",
 673                     ibdma_err);
 674         }
 675 
 676 done:
 677         if (ibdma_err != 0) {
 678                 srpt_ibdma_ops_unload(ops);
 679         }
 680 
 681         return (ibdma_err);
 682 }
 683 
 684 static void
 685 srpt_ibdma_ops_unload(srpt_ibdma_ops_t *ops)
 686 {
 687         if (ops != NULL) {
 688                 if (ops->ibdmah != NULL) {
 689                         (void) ddi_modclose(ops->ibdmah);
 690                 }
 691                 bzero(ops, sizeof (srpt_ibdma_ops_t));
 692         }
 693 }