xref: /titanic_50/usr/src/uts/sun4v/io/vnex.c (revision 63602c90af2802f2ab666ab39bb4aea7d02500c3)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4 5.0 */
27 
28 #include <sys/types.h>
29 #include <sys/cmn_err.h>
30 #include <sys/conf.h>
31 #include <sys/ddi_impldefs.h>
32 #include <sys/autoconf.h>
33 #include <sys/systm.h>
34 #include <sys/modctl.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/ddi_subrdefs.h>
38 #include <sys/promif.h>
39 #include <sys/machsystm.h>
40 #include <sys/ddi_intr_impl.h>
41 #include <sys/hypervisor_api.h>
42 #include <sys/intr.h>
43 #include <sys/hsvc.h>
44 
45 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
46 
47 static kmutex_t vnex_id_lock;
48 /*
49  * Vnex name  to pil map
50  */
51 typedef struct vnex_regspec {
52 	uint64_t physaddr;
53 	uint64_t size;
54 } vnex_regspec_t;
55 
56 struct vnex_pil_map {
57 	caddr_t	name;
58 	uint32_t pil;
59 };
60 
61 /* vnex interrupt descriptor */
62 typedef struct vnex_id {
63 	dev_info_t *vid_dip;
64 	uint32_t vid_ino;
65 	uint64_t vid_ihdl;
66 	uint_t	(*vid_handler)();
67 	caddr_t	vid_arg1;
68 	caddr_t	vid_arg2;
69 	ddi_intr_handle_impl_t *vid_ddi_hdlp;
70 	uint64_t vid_cfg_hdl;
71 	struct vnex_id *vid_next;
72 } vnex_id_t;
73 
74 /* vnex interrupt descriptor list */
75 static vnex_id_t *vnex_id_list;
76 
77 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
78 
79 /*
80  * vnex interrupt descriptor list manipulation functions
81  */
82 
83 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino);
84 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino,
85 	uint64_t dhdl);
86 static void vnex_add_id(vnex_id_t *vid_p);
87 static void vnex_rem_id(vnex_id_t *vid_p);
88 static void vnex_free_id(vnex_id_t *vid_p);
89 
90 uint_t vnex_intr_wrapper(caddr_t arg);
91 
92 static struct vnex_pil_map vnex_name_to_pil[] = {
93 	{"console", 	PIL_12},
94 	{"fma",		PIL_5},
95 	{"echo", 	PIL_3},
96 	{"loop", 	PIL_3},
97 	{"sunmc", 	PIL_3},
98 	{"sunvts", 	PIL_3},
99 	{"explorer", 	PIL_3},
100 	{"ncp", 	PIL_8},
101 	{"crypto", 	PIL_8}
102 };
103 
104 #define	VNEX_MAX_DEVS	(sizeof (vnex_name_to_pil) /	\
105 			    sizeof (struct vnex_pil_map))
106 
107 /*
108  * Config information
109  */
110 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
111     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
112 
113 static int
114 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
115 
116 static struct bus_ops vnex_bus_ops = {
117 	BUSO_REV,
118 	nullbusmap,
119 	NULL,	/* NO OP */
120 	NULL,	/* NO OP */
121 	NULL,	/* NO OP */
122 	i_ddi_map_fault,
123 	ddi_no_dma_map,
124 	ddi_no_dma_allochdl,
125 	NULL,
126 	NULL,
127 	NULL,
128 	NULL,
129 	NULL,
130 	NULL,
131 	vnex_ctl,
132 	ddi_bus_prop_op,
133 	NULL,	/* (*bus_get_eventcookie)();    */
134 	NULL,	/* (*bus_add_eventcall)();	*/
135 	NULL,	/* (*bus_remove_eventcall)();   */
136 	NULL,	/* (*bus_post_event)();		*/
137 	NULL,	/* (*bus_intr_ctl)();		*/
138 	NULL,	/* (*bus_config)();		*/
139 	NULL,	/* (*bus_unconfig)();		*/
140 	NULL,	/* (*bus_fm_init)();		*/
141 	NULL,	/* (*bus_fm_fini)();		*/
142 	NULL,	/* (*bus_fm_access_enter)();	*/
143 	NULL,	/* (*bus_fm_access_fini)();	*/
144 	NULL,	/* (*bus_power)();		*/
145 	vnex_intr_ops	/* (*bus_intr_op)();	*/
146 };
147 
148 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
149 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
150 
151 static struct dev_ops pseudo_ops = {
152 	DEVO_REV,		/* devo_rev, */
153 	0,			/* refcnt  */
154 	ddi_no_info,		/* info */
155 	nulldev,		/* identify */
156 	nulldev,		/* probe */
157 	vnex_attach,		/* attach */
158 	vnex_detach,		/* detach */
159 	nodev,			/* reset */
160 	(struct cb_ops *)0,	/* driver operations */
161 	&vnex_bus_ops,	/* bus operations */
162 	nulldev			/* power */
163 };
164 
165 /*
166  * Module linkage information for the kernel.
167  */
168 
169 static struct modldrv modldrv = {
170 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
171 	"sun4v virtual-devices nexus driver v%I%",
172 	&pseudo_ops,	/* driver ops */
173 };
174 
175 static struct modlinkage modlinkage = {
176 	MODREV_1, (void *)&modldrv, NULL
177 };
178 
179 int
180 _init(void)
181 {
182 	uint64_t mjrnum;
183 	uint64_t mnrnum;
184 
185 	/*
186 	 * Check HV intr group api versioning.
187 	 * This driver uses the old interrupt routines which are supported
188 	 * in old firmware in the CORE API group and in newer firmware in
189 	 * the INTR API group.  Support for these calls will be dropped
190 	 * once the INTR API group major goes to 2.
191 	 */
192 
193 	if ((hsvc_version(HSVC_GROUP_INTR, &mjrnum, &mnrnum) == 0) &&
194 	    (mjrnum > 1)) {
195 		cmn_err(CE_WARN, "niumx: unsupported intr api group: "
196 		    "maj:0x%lx, min:0x%lx", mjrnum, mnrnum);
197 		return (ENOTSUP);
198 	}
199 
200 	return (mod_install(&modlinkage));
201 }
202 
203 int
204 _fini(void)
205 {
206 	return (mod_remove(&modlinkage));
207 }
208 
209 int
210 _info(struct modinfo *modinfop)
211 {
212 	return (mod_info(&modlinkage, modinfop));
213 }
214 
215 /*ARGSUSED*/
216 void
217 vnex_intr_dist(void *arg)
218 {
219 	vnex_id_t *vid_p;
220 	uint32_t cpuid;
221 	int	intr_state;
222 	hrtime_t start;
223 
224 	mutex_enter(&vnex_id_lock);
225 
226 	for (vid_p = vnex_id_list; vid_p != NULL;
227 	    vid_p = vid_p->vid_next) {
228 		/*
229 		 * Don't do anything for disabled interrupts.
230 		 * vnex_enable_intr takes care of redistributing interrupts.
231 		 */
232 		if ((hvio_intr_getvalid(vid_p->vid_ihdl,
233 		    &intr_state) == H_EOK) && (intr_state == HV_INTR_NOTVALID))
234 				continue;
235 
236 		cpuid = intr_dist_cpuid();
237 
238 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID);
239 		/*
240 		 * Make a best effort to wait for pending interrupts to finish.
241 		 * There is not much we can do if we timeout.
242 		 */
243 		start = gethrtime();
244 		while (!panicstr &&
245 		    (hvio_intr_getstate(vid_p->vid_ihdl, &intr_state) ==
246 		    H_EOK) && (intr_state == HV_INTR_DELIVERED_STATE)) {
247 			if (gethrtime() - start > vnex_pending_timeout) {
248 				cmn_err(CE_WARN, "vnex_intr_dist: %s%d "
249 				    "ino 0x%x pending: timedout\n",
250 				    ddi_driver_name(vid_p->vid_dip),
251 				    ddi_get_instance(vid_p->vid_dip),
252 				    vid_p->vid_ino);
253 				break;
254 			}
255 		}
256 		(void) hvio_intr_settarget(vid_p->vid_ihdl, cpuid);
257 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID);
258 	}
259 	mutex_exit(&vnex_id_lock);
260 }
261 
262 static int
263 vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
264 {
265 	switch (cmd) {
266 	case DDI_ATTACH:
267 		/*
268 		 * Intitialize interrupt descriptor list
269 		 * and mutex.
270 		 */
271 		vnex_id_list = NULL;
272 		mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL);
273 		/*
274 		 * Add interrupt redistribution callback.
275 		 */
276 		intr_dist_add(vnex_intr_dist, dip);
277 		return (DDI_SUCCESS);
278 
279 	case DDI_RESUME:
280 		return (DDI_SUCCESS);
281 
282 	default:
283 		return (DDI_FAILURE);
284 	}
285 }
286 
287 /*ARGSUSED*/
288 static int
289 vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
290 {
291 	switch (cmd) {
292 	case DDI_DETACH:
293 		return (DDI_FAILURE);
294 
295 	case DDI_SUSPEND:
296 		return (DDI_SUCCESS);
297 
298 	default:
299 		return (DDI_FAILURE);
300 	}
301 }
302 
303 static int
304 vnex_ctl(dev_info_t *dip, dev_info_t *rdip,
305 	ddi_ctl_enum_t ctlop, void *arg, void *result)
306 {
307 	char	name[12];	/* enough for a decimal integer */
308 	int		reglen;
309 	uint32_t	*vnex_regspec;
310 
311 	switch (ctlop) {
312 	case DDI_CTLOPS_REPORTDEV:
313 		if (rdip == NULL)
314 			return (DDI_FAILURE);
315 		cmn_err(CE_CONT, "?virtual-device: %s%d\n",
316 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
317 		return (DDI_SUCCESS);
318 
319 	case DDI_CTLOPS_INITCHILD:
320 	{
321 		dev_info_t *child = (dev_info_t *)arg;
322 
323 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
324 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
325 			return (DDI_FAILURE);
326 
327 		(void) sprintf(name, "%x", *vnex_regspec);
328 		ddi_set_name_addr(child, name);
329 		ddi_set_parent_data(child, NULL);
330 		kmem_free((caddr_t)vnex_regspec, reglen);
331 		return (DDI_SUCCESS);
332 
333 	}
334 
335 	case DDI_CTLOPS_UNINITCHILD:
336 	{
337 		dev_info_t *child = (dev_info_t *)arg;
338 
339 		ddi_set_name_addr(child, NULL);
340 		ddi_remove_minor_node(arg, NULL);
341 		return (DDI_SUCCESS);
342 	}
343 
344 	/*
345 	 * These ops correspond to functions that "shouldn't" be called
346 	 * by a pseudo driver.  So we whinge when we're called.
347 	 */
348 	case DDI_CTLOPS_DMAPMAPC:
349 	case DDI_CTLOPS_REPORTINT:
350 	case DDI_CTLOPS_REGSIZE:
351 	{
352 		*((off_t *)result) = 0;
353 		return (DDI_SUCCESS);
354 	}
355 	case DDI_CTLOPS_NREGS:
356 	{
357 		dev_info_t *child = (dev_info_t *)arg;
358 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
359 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
360 			return (DDI_FAILURE);
361 		*((uint_t *)result) = reglen / sizeof (uint32_t);
362 		kmem_free((caddr_t)vnex_regspec, reglen);
363 		return (DDI_SUCCESS);
364 	}
365 	case DDI_CTLOPS_SIDDEV:
366 	case DDI_CTLOPS_SLAVEONLY:
367 	case DDI_CTLOPS_AFFINITY:
368 	case DDI_CTLOPS_IOMIN:
369 	case DDI_CTLOPS_POKE:
370 	case DDI_CTLOPS_PEEK:
371 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
372 			ddi_get_name(dip), ddi_get_instance(dip),
373 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
374 		return (DDI_FAILURE);
375 
376 	/*
377 	 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
378 	 */
379 	default:
380 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
381 	}
382 }
383 
384 static int
385 vnex_get_pil(dev_info_t *rdip)
386 {
387 	int i;
388 	caddr_t	name;
389 
390 	name = ddi_node_name(rdip);
391 	for (i = 0; i < VNEX_MAX_DEVS; i++) {
392 		if (strcmp(vnex_name_to_pil[i].name,
393 		    name) == 0) {
394 			return (vnex_name_to_pil[i].pil);
395 		}
396 	}
397 	/*
398 	 * if not found pil is 0
399 	 */
400 	return (0);
401 }
402 
403 static int
404 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
405 {
406 	vnex_id_t *vid_p;
407 	uint32_t cpuid;
408 
409 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
410 
411 	ASSERT(vid_p != NULL);
412 
413 	cpuid = intr_dist_cpuid();
414 
415 	if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) {
416 		return (DDI_FAILURE);
417 	}
418 
419 	if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
420 		return (DDI_FAILURE);
421 	}
422 
423 	if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) {
424 		return (DDI_FAILURE);
425 	}
426 
427 	return (DDI_SUCCESS);
428 }
429 
430 static int
431 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
432 {
433 	vnex_id_t *vid_p;
434 
435 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
436 
437 	ASSERT(vid_p != NULL);
438 
439 	if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) {
440 		return (DDI_FAILURE);
441 	}
442 
443 	return (DDI_SUCCESS);
444 }
445 
446 int
447 vnex_ino_to_inum(dev_info_t *dip, uint32_t ino)
448 {
449 	vnex_id_t		*vid_p;
450 	ddi_intr_handle_impl_t	*hdlp;
451 
452 	if ((vid_p = vnex_locate_id(dip, ino)) == NULL)
453 		return (-1);
454 	else if ((hdlp = vid_p->vid_ddi_hdlp) == NULL)
455 		return (-1);
456 	else
457 		return (hdlp->ih_inum);
458 }
459 
460 static int
461 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip,
462     ddi_intr_handle_impl_t *hdlp)
463 {
464 	int reglen, ret = DDI_SUCCESS;
465 	vnex_id_t	*vid_p;
466 	uint64_t cfg;
467 	uint32_t ino;
468 	uint64_t ihdl;
469 	vnex_regspec_t *reg_p;
470 
471 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
472 	    DDI_PROP_DONTPASS, "reg", (caddr_t)&reg_p,
473 	    &reglen) != DDI_SUCCESS) {
474 		return (DDI_FAILURE);
475 	}
476 
477 	/*
478 	 * get the sun4v config handle for this device
479 	 */
480 
481 	cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
482 	kmem_free(reg_p, reglen);
483 	ino = hdlp->ih_vector;
484 
485 	/*
486 	 * call hv to get vihdl
487 	 */
488 	if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
489 		return (DDI_FAILURE);
490 
491 	hdlp->ih_vector = ihdl;
492 	/*
493 	 * Allocate a interrupt descriptor (id) with the
494 	 * the interrupt handler and append it to
495 	 * the id list.
496 	 */
497 
498 	vid_p = vnex_alloc_id(rdip, ino, cfg);
499 	vid_p->vid_ihdl = ihdl;
500 	vid_p->vid_handler =  hdlp->ih_cb_func;
501 	vid_p->vid_arg1 =  hdlp->ih_cb_arg1;
502 	vid_p->vid_arg2 =  hdlp->ih_cb_arg2;
503 	vid_p->vid_ddi_hdlp =  hdlp;
504 
505 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
506 	    (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
507 
508 	if (hdlp->ih_pri == 0) {
509 		hdlp->ih_pri = vnex_get_pil(rdip);
510 	}
511 
512 	ret = i_ddi_add_ivintr(hdlp);
513 	if (ret != DDI_SUCCESS) {
514 		return (ret);
515 	}
516 
517 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
518 	    vid_p->vid_arg1, vid_p->vid_arg2);
519 
520 	return (ret);
521 }
522 
523 static int
524 vnex_remove_intr(dev_info_t *rdip,
525 	ddi_intr_handle_impl_t *hdlp)
526 {
527 	vnex_id_t *vid_p;
528 	uint32_t ino;
529 	int ret = DDI_SUCCESS;
530 
531 	ino = hdlp->ih_vector;
532 	vid_p = vnex_locate_id(rdip, ino);
533 
534 	hdlp->ih_vector = vid_p->vid_ihdl;
535 	i_ddi_rem_ivintr(hdlp);
536 
537 	vnex_free_id(vid_p);
538 
539 	return (ret);
540 }
541 
542 static int
543 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
544     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
545 {
546 	int	ret = DDI_SUCCESS;
547 
548 	switch (intr_op) {
549 		case DDI_INTROP_GETCAP:
550 			*(int *)result = DDI_INTR_FLAG_LEVEL;
551 			break;
552 		case DDI_INTROP_ALLOC:
553 			*(int *)result = hdlp->ih_scratch1;
554 			break;
555 		case DDI_INTROP_GETPRI:
556 			*(int *)result = hdlp->ih_pri ?
557 			    hdlp->ih_pri : vnex_get_pil(rdip);
558 			break;
559 		case DDI_INTROP_FREE:
560 			break;
561 		case DDI_INTROP_SETPRI:
562 			break;
563 		case DDI_INTROP_ADDISR:
564 			ret = vnex_add_intr(dip, rdip, hdlp);
565 			break;
566 		case DDI_INTROP_REMISR:
567 			ret = vnex_remove_intr(rdip, hdlp);
568 			break;
569 		case DDI_INTROP_ENABLE:
570 			ret = vnex_enable_intr(rdip, hdlp);
571 			break;
572 		case DDI_INTROP_DISABLE:
573 			ret = vnex_disable_intr(rdip, hdlp);
574 			break;
575 		case DDI_INTROP_NINTRS:
576 		case DDI_INTROP_NAVAIL:
577 			*(int *)result = i_ddi_get_intx_nintrs(rdip);
578 			break;
579 		case DDI_INTROP_SUPPORTED_TYPES:
580 			*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
581 			    DDI_INTR_TYPE_FIXED : 0;
582 			break;
583 		default:
584 			ret = DDI_ENOTSUP;
585 			break;
586 	}
587 
588 	return (ret);
589 }
590 
591 vnex_id_t *
592 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
593 {
594 	vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
595 
596 	vid_p->vid_dip = dip;
597 	vid_p->vid_ino = ino;
598 	vid_p->vid_cfg_hdl = dhdl;
599 
600 	mutex_enter(&vnex_id_lock);
601 	vnex_add_id(vid_p);
602 	mutex_exit(&vnex_id_lock);
603 
604 	return (vid_p);
605 }
606 
607 vnex_id_t *
608 vnex_locate_id(dev_info_t *dip, uint32_t ino)
609 {
610 	vnex_id_t *vid_p;
611 
612 	mutex_enter(&vnex_id_lock);
613 	vid_p = vnex_id_list;
614 
615 	while (vid_p != NULL) {
616 		if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
617 			mutex_exit(&vnex_id_lock);
618 			return (vid_p);
619 		}
620 		vid_p = vid_p->vid_next;
621 	}
622 	mutex_exit(&vnex_id_lock);
623 	return (NULL);
624 }
625 
626 static void
627 vnex_free_id(vnex_id_t *vid_p)
628 {
629 	mutex_enter(&vnex_id_lock);
630 	vnex_rem_id(vid_p);
631 	mutex_exit(&vnex_id_lock);
632 
633 	kmem_free(vid_p, sizeof (*vid_p));
634 }
635 
636 static void
637 vnex_rem_id(vnex_id_t *vid_p)
638 {
639 	vnex_id_t *prev_p = vnex_id_list;
640 
641 	if (vnex_id_list == NULL)
642 		cmn_err(CE_PANIC, "vnex: interrupt list empty");
643 
644 	if (vid_p == NULL)
645 		cmn_err(CE_PANIC, "vnex: no element to remove");
646 
647 	if (vnex_id_list == vid_p) {
648 		vnex_id_list = vid_p->vid_next;
649 	} else {
650 		while (prev_p != NULL && prev_p->vid_next != vid_p)
651 			prev_p = prev_p->vid_next;
652 
653 		if (prev_p == NULL)
654 			cmn_err(CE_PANIC, "vnex: element %p not in list",
655 			    (void *) vid_p);
656 
657 		prev_p->vid_next = vid_p->vid_next;
658 	}
659 }
660 
661 static void
662 vnex_add_id(vnex_id_t *vid_p)
663 {
664 	vid_p->vid_next = vnex_id_list;
665 	vnex_id_list = vid_p;
666 }
667 
668 uint_t
669 vnex_intr_wrapper(caddr_t arg)
670 {
671 	vnex_id_t *vid_p = (vnex_id_t *)arg;
672 	int res;
673 	uint_t (*handler)();
674 	caddr_t handler_arg1;
675 	caddr_t handler_arg2;
676 
677 	handler = vid_p->vid_handler;
678 	handler_arg1 = vid_p->vid_arg1;
679 	handler_arg2 = vid_p->vid_arg2;
680 
681 	res = (*handler)(handler_arg1, handler_arg2);
682 
683 	(void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
684 
685 	return (res);
686 }
687