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,
 152         NULL,
 153 };
 154 
 155 static char srpt_pp_name[] = "srpt";
 156 
 157 /*
 158  * Prototypes
 159  */
 160 static void srpt_pp_cb(stmf_port_provider_t *, int, void *, uint32_t);
 161 
 162 /*
 163  * _init()
 164  */
 165 int
 166 _init(void)
 167 {
 168         int status;
 169 
 170         /*
 171          * Global one time initialization.
 172          */
 173         srpt_ctxt = kmem_zalloc(sizeof (srpt_ctxt_t), KM_SLEEP);
 174         ASSERT(srpt_ctxt != NULL);
 175         rw_init(&srpt_ctxt->sc_rwlock, NULL, RW_DRIVER, NULL);
 176 
 177         /* Start-up state is DISABLED.  SMF will tell us if we should enable. */
 178         srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
 179         list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
 180             offsetof(srpt_ioc_t, ioc_node));
 181 
 182         list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
 183             offsetof(srpt_ioc_t, ioc_node));
 184 
 185         status = mod_install(&srpt_modlinkage);
 186         if (status != DDI_SUCCESS) {
 187                 cmn_err(CE_CONT, "_init, failed mod_install %d", status);
 188                 rw_destroy(&srpt_ctxt->sc_rwlock);
 189                 kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
 190                 srpt_ctxt = NULL;
 191         }
 192 
 193         return (status);
 194 }
 195 
 196 /*
 197  * _info()
 198  */
 199 int
 200 _info(struct modinfo *modinfop)
 201 {
 202         return (mod_info(&srpt_modlinkage, modinfop));
 203 }
 204 
 205 /*
 206  * _fini()
 207  */
 208 int
 209 _fini(void)
 210 {
 211         int status;
 212 
 213         status = mod_remove(&srpt_modlinkage);
 214         if (status != DDI_SUCCESS) {
 215                 return (status);
 216         }
 217 
 218         list_destroy(&srpt_ctxt->sc_ioc_list);
 219 
 220         rw_destroy(&srpt_ctxt->sc_rwlock);
 221         kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
 222         srpt_ctxt = NULL;
 223 
 224         return (status);
 225 }
 226 
 227 /*
 228  * DDI entry points.
 229  */
 230 
 231 /*
 232  * srpt_getinfo()
 233  */
 234 /* ARGSUSED */
 235 static int
 236 srpt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 237 {
 238 
 239         switch (cmd) {
 240         case DDI_INFO_DEVT2DEVINFO:
 241                 *result = srpt_ctxt->sc_dip;
 242                 return (DDI_SUCCESS);
 243 
 244         case DDI_INFO_DEVT2INSTANCE:
 245                 *result = NULL;
 246                 return (DDI_SUCCESS);
 247 
 248         default:
 249                 break;
 250         }
 251         return (DDI_FAILURE);
 252 }
 253 
 254 /*
 255  * srpt_drv_attach()
 256  */
 257 static int
 258 srpt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 259 {
 260         int             status;
 261 
 262         switch (cmd) {
 263         case DDI_ATTACH:
 264                 break;
 265 
 266         case DDI_RESUME:
 267                 return (DDI_SUCCESS);
 268 
 269         default:
 270                 return (DDI_FAILURE);
 271         }
 272 
 273         /*
 274          * We only allow a single instance.
 275          */
 276         if (ddi_get_instance(dip) != 0) {
 277                 SRPT_DPRINTF_L1("drv_attach, error non-zero instance");
 278                 return (DDI_FAILURE);
 279         }
 280 
 281         /*
 282          * Create minor node that might ultimately be used to create
 283          * targets outside of srpt.
 284          */
 285         status = ddi_create_minor_node(dip, ddi_get_name(dip),
 286             S_IFCHR, 0, DDI_PSEUDO, 0);
 287         if (status != DDI_SUCCESS) {
 288                 SRPT_DPRINTF_L1("drv_attach, minor node creation error (%d)",
 289                     status);
 290                 return (DDI_FAILURE);
 291         }
 292 
 293         rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 294         srpt_ctxt->sc_dip = dip;
 295         rw_exit(&srpt_ctxt->sc_rwlock);
 296 
 297         return (DDI_SUCCESS);
 298 }
 299 
 300 /*
 301  * srpt_enable_srp_services()
 302  *
 303  * Caller must be holding the sc_rwlock as RW_WRITER.
 304  */
 305 static int
 306 srpt_enable_srp_services(void)
 307 {
 308         int             status;
 309 
 310         ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
 311 
 312         SRPT_DPRINTF_L3("srpt_enable_srp_services");
 313 
 314         /* Register the port provider */
 315         srpt_ctxt->sc_pp = (stmf_port_provider_t *)
 316             stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
 317         srpt_ctxt->sc_pp->pp_portif_rev = PORTIF_REV_1;
 318         srpt_ctxt->sc_pp->pp_name = srpt_pp_name;
 319         srpt_ctxt->sc_pp->pp_cb   = srpt_pp_cb;
 320         status = stmf_register_port_provider(srpt_ctxt->sc_pp);
 321         if (status != STMF_SUCCESS) {
 322                 SRPT_DPRINTF_L1("enable_srp: SRP port_provider registration"
 323                     " failed(%d)", status);
 324                 goto err_exit_1;
 325         }
 326 
 327         /*
 328          * Initialize IB resources, creating a list of SRP I/O Controllers
 329          * and for each controller, register the SCSI Target Port with STMF
 330          * and prepare profile and services.
 331          */
 332         status = srpt_ioc_attach();
 333         if (status != DDI_SUCCESS) {
 334                 SRPT_DPRINTF_L1("enable_srp: error attach I/O"
 335                     " Controllers (%d)", status);
 336                 goto err_exit_2;
 337         }
 338 
 339         /*
 340          * No configured controllers is not a fatal error.  This can happen
 341          * if all HCAs are currently disabled for use by SRP.  The service
 342          * should remain running in case the user changes their mind and
 343          * enables an HCA for SRP services.
 344          */
 345         if (srpt_ctxt->sc_num_iocs == 0) {
 346                 SRPT_DPRINTF_L2("enable_srp: no IB I/O Controllers found");
 347                 return (DDI_SUCCESS);
 348         }
 349 
 350         return (DDI_SUCCESS);
 351 
 352 err_exit_2:
 353         (void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
 354 
 355 err_exit_1:
 356         stmf_free(srpt_ctxt->sc_pp);
 357         srpt_ctxt->sc_pp = NULL;
 358 
 359         return (status);
 360 }
 361 
 362 /*
 363  * srpt_drv_detach()
 364  *
 365  * Refuse the detach request if we have channels open on
 366  * any IOC.  Users should use 'svcadm disable' to shutdown
 367  * active targets.
 368  */
 369 /*ARGSUSED*/
 370 static int
 371 srpt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 372 {
 373         switch (cmd) {
 374         case DDI_DETACH:
 375                 rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 376                 if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
 377                         rw_exit(&srpt_ctxt->sc_rwlock);
 378                         return (DDI_FAILURE);
 379                 }
 380 
 381                 ddi_remove_minor_node(dip, NULL);
 382                 srpt_ctxt->sc_dip = NULL;
 383 
 384                 if (srpt_ctxt->sc_cfg_hca_nv != NULL) {
 385                         nvlist_free(srpt_ctxt->sc_cfg_hca_nv);
 386                         srpt_ctxt->sc_cfg_hca_nv = NULL;
 387                 }
 388 
 389                 rw_exit(&srpt_ctxt->sc_rwlock);
 390 
 391                 break;
 392 
 393         case DDI_SUSPEND:
 394                 return (DDI_FAILURE);
 395 
 396         default:
 397                 return (DDI_FAILURE);
 398         }
 399 
 400         return (DDI_SUCCESS);
 401 }
 402 
 403 /*
 404  * srpt_disable_srp_services()
 405  *
 406  * Offlines all targets, deregisters all IOCs.  Caller must hold
 407  * the srpt_ctxt->sc_rwlock as RW_WRITER.
 408  */
 409 static int
 410 srpt_disable_srp_services(void)
 411 {
 412         stmf_status_t                   stmf_status;
 413         srpt_ioc_t                      *ioc;
 414         srpt_target_port_t              *tgt;
 415         int                             ret_status = 0;
 416 
 417         ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
 418 
 419         /*
 420          * For each I/O Controller remove all SRP services and de-register
 421          * with the associated I/O Unit's IB Device Management Agent.
 422          */
 423         ioc = list_head(&srpt_ctxt->sc_ioc_list);
 424 
 425         while (ioc != NULL) {
 426                 rw_enter(&ioc->ioc_rwlock, RW_WRITER);
 427 
 428                 tgt = ioc->ioc_tgt_port;
 429                 if (tgt != NULL) {
 430                         stmf_status = srpt_stp_destroy_port(tgt);
 431                         if (stmf_status == STMF_SUCCESS) {
 432                                 ioc->ioc_tgt_port = NULL;
 433                                 (void) srpt_stp_free_port(tgt);
 434                         } else {
 435                                 ret_status = DDI_FAILURE;
 436                                 break;
 437                         }
 438                 }
 439 
 440                 rw_exit(&ioc->ioc_rwlock);
 441                 ioc = list_next(&srpt_ctxt->sc_ioc_list, ioc);
 442         }
 443 
 444         /* don't release IOCs until all ports are deregistered */
 445         if (ret_status != 0) {
 446                 return (ret_status);
 447         }
 448 
 449         /*
 450          * Release I/O Controller(s) resources and detach.
 451          */
 452         srpt_ioc_detach();
 453 
 454         /* De-register ourselves as an STMF port provider */
 455         (void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
 456         stmf_free(srpt_ctxt->sc_pp);
 457         srpt_ctxt->sc_pp = NULL;
 458 
 459         return (0);
 460 }
 461 
 462 /*
 463  * srpt_drv_open()
 464  */
 465 /* ARGSUSED */
 466 static int
 467 srpt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
 468 {
 469         SRPT_DPRINTF_L3("drv_open, invoked");
 470         return (0);
 471 }
 472 
 473 /*
 474  * srpt_drv_close()
 475  */
 476 /* ARGSUSED */
 477 static int
 478 srpt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
 479 {
 480         SRPT_DPRINTF_L3("drv_close, invoked");
 481         return (0);
 482 }
 483 
 484 /*
 485  * srpt_drv_ioctl()
 486  */
 487 /* ARGSUSED */
 488 static int
 489 srpt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
 490     int *retval)
 491 {
 492         int             ret = 0;
 493 
 494         SRPT_DPRINTF_L3("drv_ioctl, invoked, cmd = %d", cmd);
 495 
 496         if (drv_priv(cred) != 0) {
 497                 return (EPERM);
 498         }
 499 
 500         rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 501 
 502         switch (cmd) {
 503                 case SRPT_IOC_ENABLE_SVC:
 504                         if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
 505                                 break;
 506                         }
 507 
 508                         ret = srpt_ibdma_ops_load(&srpt_ctxt->sc_ibdma_ops);
 509                         if (ret != 0) {
 510                                 break;
 511                         }
 512 
 513                         ret = srpt_enable_srp_services();
 514                         if (ret == 0) {
 515                                 srpt_ctxt->sc_svc_state = SRPT_SVC_ENABLED;
 516                         }
 517 
 518                         break;
 519 
 520                 case SRPT_IOC_DISABLE_SVC:
 521                         if (srpt_ctxt->sc_svc_state != SRPT_SVC_ENABLED) {
 522                                 break;
 523                         }
 524 
 525                         ret = srpt_disable_srp_services();
 526                         if (ret == 0) {
 527                                 srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
 528                         }
 529 
 530                         srpt_ibdma_ops_unload(&srpt_ctxt->sc_ibdma_ops);
 531 
 532                         break;
 533 
 534                 default:
 535                         ret = EFAULT;
 536                         break;
 537         }
 538 
 539         rw_exit(&srpt_ctxt->sc_rwlock);
 540 
 541         return (ret);
 542 }
 543 
 544 /*
 545  * srpt_pp_cb()
 546  */
 547 /* ARGSUSED */
 548 static void
 549 srpt_pp_cb(stmf_port_provider_t *pp, int cmd, void *arg, uint32_t flags)
 550 {
 551         int             ret;
 552         nvlist_t        *in_nvl = (nvlist_t *)arg;
 553         nvlist_t        *nvl = NULL;
 554         nvlist_t        *hcalist;
 555         nvlist_t        *ctxt_nvl;
 556         boolean_t       defaultEnabled = B_TRUE;
 557         boolean_t       called_by_reg = B_TRUE;
 558 
 559         SRPT_DPRINTF_L2("srpt_pp_cb, invoked (%d)", cmd);
 560 
 561         if (cmd != STMF_PROVIDER_DATA_UPDATED) {
 562                 return;
 563         }
 564 
 565         /*
 566          * If STMF_PCB_PREG_COMPLETE is set in the flags, we're being
 567          * called back during provider registration with STMF.
 568          * (while we're calling stmf_register_port_provider()).
 569          * srpt_enable_service() already holds the sc_wrlock, and will
 570          * make sure the configuration is activated, so we just need to
 571          * set the config and get out.  If this function is called at any
 572          * time other than SRPT service start, need to grab the sc_wrlock
 573          * as WRITER.
 574          */
 575         if (!(flags & STMF_PCB_PREG_COMPLETE)) {
 576                 SRPT_DPRINTF_L2(
 577                     "srpt_pp_cb:  called after registration");
 578                 called_by_reg = B_FALSE;
 579                 rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
 580         } else {
 581                 called_by_reg = B_TRUE;
 582                 SRPT_DPRINTF_L2(
 583                     "srpt_pp_cb:  called as part of registration");
 584         }
 585 
 586         if (in_nvl != NULL) {
 587                 /* copy nvlist */
 588                 ret = nvlist_lookup_nvlist(in_nvl, SRPT_PROP_HCALIST, &hcalist);
 589                 if (ret != 0) {
 590                         SRPT_DPRINTF_L1(
 591                             "srpt_pp_cb: Could not read hca config, err=%d",
 592                             ret);
 593                         return;
 594                 }
 595 
 596                 ret = nvlist_dup(hcalist, &nvl, 0);
 597                 if (ret != 0) {
 598                         SRPT_DPRINTF_L1(
 599                             "srpt_pp_cb: Could not copy hca config, err=%d",
 600                             ret);
 601                         return;
 602                 }
 603                 if (nvlist_lookup_boolean_value(in_nvl,
 604                     SRPT_PROP_DEFAULT_ENABLED, &defaultEnabled) == 0) {
 605                         /* set whether targets are created by default */
 606                         SRPT_DPRINTF_L2(
 607                             "srpt_pp_cb:  setting default enabled = %d\n",
 608                             (int)defaultEnabled);
 609                         srpt_enable_by_default = defaultEnabled;
 610                 }
 611         } else {
 612                 SRPT_DPRINTF_L2(
 613                     "srpt_pp_cb:  null config received");
 614         }
 615 
 616         /* put list in ctxt and set default state */
 617         ctxt_nvl = srpt_ctxt->sc_cfg_hca_nv;
 618 
 619         /* set new config, NULL is valid */
 620         srpt_ctxt->sc_cfg_hca_nv = nvl;
 621 
 622         /* free the old nvlist */
 623         if (ctxt_nvl != NULL) {
 624                 nvlist_free(ctxt_nvl);
 625         }
 626 
 627         if (called_by_reg) {
 628                 return;
 629         }
 630 
 631         /* Update the HCA based on the new config */
 632         srpt_ioc_update();
 633 
 634         rw_exit(&srpt_ctxt->sc_rwlock);
 635 }
 636 
 637 static int
 638 srpt_ibdma_ops_load(srpt_ibdma_ops_t *ops)
 639 {
 640         int                     ibdma_err = 0;
 641 
 642         ASSERT(ops != NULL);
 643 
 644         ops->ibdmah = ddi_modopen("ibdma", KRTLD_MODE_FIRST, &ibdma_err);
 645         if (ops->ibdmah == NULL) {
 646                 SRPT_DPRINTF_L0("failed to open ibdma driver, error = %d",
 647                     ibdma_err);
 648                 return (ibdma_err);
 649         }
 650 
 651         ops->ibdma_register = (ibdma_hdl_t (*)())ddi_modsym(ops->ibdmah,
 652             "ibdma_ioc_register", &ibdma_err);
 653         if (ops->ibdma_register == NULL) {
 654                 SRPT_DPRINTF_L0(
 655                     "failed to modsym ibdma_ioc_register, error = %d",
 656                     ibdma_err);
 657                 goto done;
 658         }
 659 
 660         ops->ibdma_unregister = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
 661             "ibdma_ioc_unregister", &ibdma_err);
 662         if (ops->ibdma_unregister == NULL) {
 663                 SRPT_DPRINTF_L0(
 664                     "failed to modsym ibdma_ioc_unregister, error = %d",
 665                     ibdma_err);
 666                 goto done;
 667         }
 668 
 669         ops->ibdma_update = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
 670             "ibdma_ioc_update", &ibdma_err);
 671         if (ops->ibdma_update == NULL) {
 672                 SRPT_DPRINTF_L0(
 673                     "failed to modsym ibdma_ioc_update, error = %d",
 674                     ibdma_err);
 675         }
 676 
 677 done:
 678         if (ibdma_err != 0) {
 679                 srpt_ibdma_ops_unload(ops);
 680         }
 681 
 682         return (ibdma_err);
 683 }
 684 
 685 static void
 686 srpt_ibdma_ops_unload(srpt_ibdma_ops_t *ops)
 687 {
 688         if (ops != NULL) {
 689                 if (ops->ibdmah != NULL) {
 690                         (void) ddi_modclose(ops->ibdmah);
 691                 }
 692                 bzero(ops, sizeof (srpt_ibdma_ops_t));
 693         }
 694 }