xref: /titanic_44/usr/src/uts/common/io/comstar/port/srpt/srpt_mod.c (revision 4558d122136f151d62acbbc02ddb42df89a5ef66)
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
_init(void)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
_info(struct modinfo * modinfop)200 _info(struct modinfo *modinfop)
201 {
202 	return (mod_info(&srpt_modlinkage, modinfop));
203 }
204 
205 /*
206  * _fini()
207  */
208 int
_fini(void)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
srpt_drv_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)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
srpt_drv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
srpt_enable_srp_services(void)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
srpt_drv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
srpt_disable_srp_services(void)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
srpt_drv_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
srpt_drv_close(dev_t dev,int flag,int otyp,cred_t * credp)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
srpt_drv_ioctl(dev_t drv,int cmd,intptr_t argp,int flag,cred_t * cred,int * retval)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
srpt_pp_cb(stmf_port_provider_t * pp,int cmd,void * arg,uint32_t flags)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
srpt_ibdma_ops_load(srpt_ibdma_ops_t * ops)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
srpt_ibdma_ops_unload(srpt_ibdma_ops_t * ops)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 }
695