Home | History | Annotate | Download | only in io
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <sys/types.h>
     28 #include <sys/cmn_err.h>
     29 #include <sys/conf.h>
     30 #include <sys/ddi_impldefs.h>
     31 #include <sys/autoconf.h>
     32 #include <sys/systm.h>
     33 #include <sys/modctl.h>
     34 #include <sys/ddi.h>
     35 #include <sys/sunddi.h>
     36 #include <sys/ddi_subrdefs.h>
     37 #include <sys/promif.h>
     38 #include <sys/machsystm.h>
     39 #include <sys/ddi_intr_impl.h>
     40 #include <sys/hypervisor_api.h>
     41 #include <sys/intr.h>
     42 #include <sys/hsvc.h>
     43 
     44 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
     45 
     46 static kmutex_t vnex_id_lock;
     47 /*
     48  * Vnex name  to pil map
     49  */
     50 typedef struct vnex_regspec {
     51 	uint64_t physaddr;
     52 	uint64_t size;
     53 } vnex_regspec_t;
     54 
     55 struct vnex_pil_map {
     56 	caddr_t	name;
     57 	uint32_t pil;
     58 };
     59 
     60 /* vnex interrupt descriptor */
     61 typedef struct vnex_id {
     62 	dev_info_t *vid_dip;
     63 	uint32_t vid_ino;
     64 	uint64_t vid_ihdl;
     65 	uint_t	(*vid_handler)();
     66 	caddr_t	vid_arg1;
     67 	caddr_t	vid_arg2;
     68 	ddi_intr_handle_impl_t *vid_ddi_hdlp;
     69 	uint64_t vid_cfg_hdl;
     70 	struct vnex_id *vid_next;
     71 } vnex_id_t;
     72 
     73 /* vnex interrupt descriptor list */
     74 static vnex_id_t *vnex_id_list;
     75 
     76 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
     77 
     78 /*
     79  * vnex interrupt descriptor list manipulation functions
     80  */
     81 
     82 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino);
     83 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino,
     84 	uint64_t dhdl);
     85 static void vnex_add_id(vnex_id_t *vid_p);
     86 static void vnex_rem_id(vnex_id_t *vid_p);
     87 static void vnex_free_id(vnex_id_t *vid_p);
     88 
     89 uint_t vnex_intr_wrapper(caddr_t arg);
     90 
     91 static struct vnex_pil_map vnex_name_to_pil[] = {
     92 	{"console", 	PIL_12},
     93 	{"fma",		PIL_5},
     94 	{"echo", 	PIL_3},
     95 	{"loop", 	PIL_3},
     96 	{"sunmc", 	PIL_3},
     97 	{"sunvts", 	PIL_3},
     98 	{"explorer", 	PIL_3},
     99 	{"ncp", 	PIL_8},
    100 	{"crypto", 	PIL_8}
    101 };
    102 
    103 #define	VNEX_MAX_DEVS	(sizeof (vnex_name_to_pil) /	\
    104 			    sizeof (struct vnex_pil_map))
    105 
    106 /*
    107  * Config information
    108  */
    109 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
    110     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
    111 
    112 static int
    113 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
    114 
    115 static struct bus_ops vnex_bus_ops = {
    116 	BUSO_REV,
    117 	nullbusmap,
    118 	NULL,	/* NO OP */
    119 	NULL,	/* NO OP */
    120 	NULL,	/* NO OP */
    121 	i_ddi_map_fault,
    122 	ddi_no_dma_map,
    123 	ddi_no_dma_allochdl,
    124 	NULL,
    125 	NULL,
    126 	NULL,
    127 	NULL,
    128 	NULL,
    129 	NULL,
    130 	vnex_ctl,
    131 	ddi_bus_prop_op,
    132 	NULL,	/* (*bus_get_eventcookie)();    */
    133 	NULL,	/* (*bus_add_eventcall)();	*/
    134 	NULL,	/* (*bus_remove_eventcall)();   */
    135 	NULL,	/* (*bus_post_event)();		*/
    136 	NULL,	/* (*bus_intr_ctl)();		*/
    137 	NULL,	/* (*bus_config)();		*/
    138 	NULL,	/* (*bus_unconfig)();		*/
    139 	NULL,	/* (*bus_fm_init)();		*/
    140 	NULL,	/* (*bus_fm_fini)();		*/
    141 	NULL,	/* (*bus_fm_access_enter)();	*/
    142 	NULL,	/* (*bus_fm_access_fini)();	*/
    143 	NULL,	/* (*bus_power)();		*/
    144 	vnex_intr_ops	/* (*bus_intr_op)();	*/
    145 };
    146 
    147 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
    148 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
    149 
    150 static struct dev_ops pseudo_ops = {
    151 	DEVO_REV,		/* devo_rev, */
    152 	0,			/* refcnt  */
    153 	ddi_no_info,		/* info */
    154 	nulldev,		/* identify */
    155 	nulldev,		/* probe */
    156 	vnex_attach,		/* attach */
    157 	vnex_detach,		/* detach */
    158 	nodev,			/* reset */
    159 	(struct cb_ops *)0,	/* driver operations */
    160 	&vnex_bus_ops,		/* bus operations */
    161 	nulldev,		/* power */
    162 	ddi_quiesce_not_needed,		/* quiesce */
    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",
    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 = rdip;
    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