xref: /illumos-gate/usr/src/uts/sun4/io/px/px_ib.c (revision f8d2de6b)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * PX Interrupt Block implementation
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
357c478bd9Sstevel@tonic-gate #include <sys/async.h>
367c478bd9Sstevel@tonic-gate #include <sys/systm.h>		/* panicstr */
377c478bd9Sstevel@tonic-gate #include <sys/spl.h>
387c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
397c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>	/* intr_dist_add */
407c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
417c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
427c478bd9Sstevel@tonic-gate #include "px_obj.h"
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight);
477c478bd9Sstevel@tonic-gate static void px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
487c478bd9Sstevel@tonic-gate     boolean_t wait_flag);
497c478bd9Sstevel@tonic-gate static uint_t px_ib_intr_reset(void *arg);
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate int
527c478bd9Sstevel@tonic-gate px_ib_attach(px_t *px_p)
537c478bd9Sstevel@tonic-gate {
547c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
557c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p;
567c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
577c478bd9Sstevel@tonic-gate 	px_fault_t	*fault_p = &px_p->px_fault;
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_attach\n");
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(px_p->px_dip,
62*f8d2de6bSjchu 	    px_p->px_inos[PX_INTR_PEC], &sysino) != DDI_SUCCESS)
637c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 	/*
667c478bd9Sstevel@tonic-gate 	 * Allocate interrupt block state structure and link it to
677c478bd9Sstevel@tonic-gate 	 * the px state structure.
687c478bd9Sstevel@tonic-gate 	 */
697c478bd9Sstevel@tonic-gate 	ib_p = kmem_zalloc(sizeof (px_ib_t), KM_SLEEP);
707c478bd9Sstevel@tonic-gate 	px_p->px_ib_p = ib_p;
717c478bd9Sstevel@tonic-gate 	ib_p->ib_px_p = px_p;
727c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = (px_ib_ino_info_t *)NULL;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL);
757c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL);
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 	bus_func_register(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	intr_dist_add_weighted(px_ib_intr_redist, ib_p);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	/*
827c478bd9Sstevel@tonic-gate 	 * Initialize PEC fault data structure
837c478bd9Sstevel@tonic-gate 	 */
847c478bd9Sstevel@tonic-gate 	fault_p->px_fh_dip = dip;
857c478bd9Sstevel@tonic-gate 	fault_p->px_fh_sysino = sysino;
86*f8d2de6bSjchu 	fault_p->px_err_func = px_err_dmc_pec_intr;
87*f8d2de6bSjchu 	fault_p->px_intr_ino = px_p->px_inos[PX_INTR_PEC];
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate void
937c478bd9Sstevel@tonic-gate px_ib_detach(px_t *px_p)
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
967c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_detach\n");
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	bus_func_unregister(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
1017c478bd9Sstevel@tonic-gate 	intr_dist_rem_weighted(px_ib_intr_redist, ib_p);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_ino_lst_mutex);
1047c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_intr_lock);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	px_ib_free_ino_all(ib_p);
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 	px_p->px_ib_p = NULL;
1097c478bd9Sstevel@tonic-gate 	kmem_free(ib_p, sizeof (px_ib_t));
1107c478bd9Sstevel@tonic-gate }
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate static struct {
1137c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_name;
1147c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_type;
1157c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_cpu;
1167c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_pil;
1177c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_time;
1187c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_ino;
1197c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_cookie;
1207c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_devpath;
1217c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_buspath;
1227c478bd9Sstevel@tonic-gate } px_ih_ks_template = {
1237c478bd9Sstevel@tonic-gate 	{ "name",	KSTAT_DATA_CHAR },
1247c478bd9Sstevel@tonic-gate 	{ "type",	KSTAT_DATA_CHAR },
1257c478bd9Sstevel@tonic-gate 	{ "cpu",	KSTAT_DATA_UINT64 },
1267c478bd9Sstevel@tonic-gate 	{ "pil",	KSTAT_DATA_UINT64 },
1277c478bd9Sstevel@tonic-gate 	{ "time",	KSTAT_DATA_UINT64 },
1287c478bd9Sstevel@tonic-gate 	{ "ino",	KSTAT_DATA_UINT64 },
1297c478bd9Sstevel@tonic-gate 	{ "cookie",	KSTAT_DATA_UINT64 },
1307c478bd9Sstevel@tonic-gate 	{ "devpath",	KSTAT_DATA_STRING },
1317c478bd9Sstevel@tonic-gate 	{ "buspath",	KSTAT_DATA_STRING },
1327c478bd9Sstevel@tonic-gate };
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate static uint32_t ih_instance;
1357c478bd9Sstevel@tonic-gate static kmutex_t ih_ks_template_lock;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate int
1387c478bd9Sstevel@tonic-gate ih_ks_update(kstat_t *ksp, int rw)
1397c478bd9Sstevel@tonic-gate {
1407c478bd9Sstevel@tonic-gate 	px_ih_t *ih_p = ksp->ks_private;
1417c478bd9Sstevel@tonic-gate 	int maxlen = sizeof (px_ih_ks_template.ihks_name.value.c);
1427c478bd9Sstevel@tonic-gate 	px_ib_t *ib_p = ih_p->ih_ino_p->ino_ib_p;
1437c478bd9Sstevel@tonic-gate 	px_t *px_p = ib_p->ib_px_p;
1447c478bd9Sstevel@tonic-gate 	devino_t ino;
1457c478bd9Sstevel@tonic-gate 	sysino_t sysino;
1467c478bd9Sstevel@tonic-gate 	char ih_devpath[MAXPATHLEN];
1477c478bd9Sstevel@tonic-gate 	char ih_buspath[MAXPATHLEN];
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	ino = ih_p->ih_ino_p->ino_ino;
1507c478bd9Sstevel@tonic-gate 	(void) px_lib_intr_devino_to_sysino(px_p->px_dip, ino, &sysino);
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	(void) snprintf(px_ih_ks_template.ihks_name.value.c, maxlen, "%s%d",
1537c478bd9Sstevel@tonic-gate 	    ddi_driver_name(ih_p->ih_dip),
1547c478bd9Sstevel@tonic-gate 	    ddi_get_instance(ih_p->ih_dip));
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	(void) strcpy(px_ih_ks_template.ihks_type.value.c,
1577c478bd9Sstevel@tonic-gate 	    (ih_p->ih_rec_type == 0) ? "fixed" : "msi");
1587c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_cpu.value.ui64 = ih_p->ih_ino_p->ino_cpuid;
1597c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_pil.value.ui64 = ih_p->ih_ino_p->ino_pil;
1607c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_time.value.ui64 = ih_p->ih_nsec + (uint64_t)
1617c478bd9Sstevel@tonic-gate 	    tick2ns((hrtime_t)ih_p->ih_ticks, ih_p->ih_ino_p->ino_cpuid);
1627c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_ino.value.ui64 = ino;
1637c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_cookie.value.ui64 = sysino;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(ih_p->ih_dip, ih_devpath);
1667c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(px_p->px_dip, ih_buspath);
1677c478bd9Sstevel@tonic-gate 	kstat_named_setstr(&px_ih_ks_template.ihks_devpath, ih_devpath);
1687c478bd9Sstevel@tonic-gate 	kstat_named_setstr(&px_ih_ks_template.ihks_buspath, ih_buspath);
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	return (0);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate void
1747c478bd9Sstevel@tonic-gate px_ib_intr_enable(px_t *px_p, cpuid_t cpu_id, devino_t ino)
1757c478bd9Sstevel@tonic-gate {
1767c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
1777c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 	/*
1807c478bd9Sstevel@tonic-gate 	 * Determine the cpu for the interrupt
1817c478bd9Sstevel@tonic-gate 	 */
1827c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip,
1857c478bd9Sstevel@tonic-gate 	    "px_ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id);
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino,
1887c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS) {
1897c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, px_p->px_dip,
1907c478bd9Sstevel@tonic-gate 		    "px_ib_intr_enable: px_intr_devino_to_sysino() failed\n");
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
1937c478bd9Sstevel@tonic-gate 		return;
1947c478bd9Sstevel@tonic-gate 	}
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(px_p->px_dip, sysino, cpu_id);
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2027c478bd9Sstevel@tonic-gate void
2037c478bd9Sstevel@tonic-gate px_ib_intr_disable(px_ib_t *ib_p, devino_t ino, int wait)
2047c478bd9Sstevel@tonic-gate {
2057c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_disable: ino=%x\n", ino);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
2127c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino,
2137c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS) {
2147c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, ib_p->ib_px_p->px_dip,
2157c478bd9Sstevel@tonic-gate 		    "px_ib_intr_disable: px_intr_devino_to_sysino() failed\n");
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
2187c478bd9Sstevel@tonic-gate 		return;
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(ib_p->ib_px_p->px_dip, sysino);
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate static void
2287c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
2297c478bd9Sstevel@tonic-gate     boolean_t wait_flag)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate 	uint32_t	old_cpu_id;
2327c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
2337c478bd9Sstevel@tonic-gate 	intr_valid_state_t	enabled = 0;
2347c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
2357c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
236*f8d2de6bSjchu 	int		e = DDI_SUCCESS;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) {
2417c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
2427c478bd9Sstevel@tonic-gate 		    "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino);
2437c478bd9Sstevel@tonic-gate 		return;
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	/* Skip enabling disabled interrupts */
2477c478bd9Sstevel@tonic-gate 	if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) {
2487c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() "
2497c478bd9Sstevel@tonic-gate 		    "failed, sysino 0x%x\n", sysino);
2507c478bd9Sstevel@tonic-gate 		return;
2517c478bd9Sstevel@tonic-gate 	}
2527c478bd9Sstevel@tonic-gate 	if (!enabled)
2537c478bd9Sstevel@tonic-gate 		return;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	/* Done if redistributed onto the same cpuid */
2567c478bd9Sstevel@tonic-gate 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) {
2577c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
2587c478bd9Sstevel@tonic-gate 		    "px_intr_gettarget() failed\n");
2597c478bd9Sstevel@tonic-gate 		return;
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 	if (cpu_id == old_cpu_id)
2627c478bd9Sstevel@tonic-gate 		return;
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	if (!wait_flag)
2657c478bd9Sstevel@tonic-gate 		goto done;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupts */
2687c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(dip, sysino);
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
2717c478bd9Sstevel@tonic-gate 	    ((e = px_lib_intr_getstate(dip, sysino, &intr_state)) ==
2727c478bd9Sstevel@tonic-gate 		DDI_SUCCESS) &&
2737c478bd9Sstevel@tonic-gate 	    (intr_state == INTR_DELIVERED_STATE); /* */) {
2747c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
2757c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
2767c478bd9Sstevel@tonic-gate 			    "%s%d: px_ib_intr_dist_en: sysino 0x%x(ino 0x%x) "
2777c478bd9Sstevel@tonic-gate 			    "from cpu id 0x%x to 0x%x timeout",
2787c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
2797c478bd9Sstevel@tonic-gate 			    sysino, ino, old_cpu_id, cpu_id);
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 			e = DDI_FAILURE;
2827c478bd9Sstevel@tonic-gate 			break;
2837c478bd9Sstevel@tonic-gate 		}
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	if (e != DDI_SUCCESS)
2877c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, "
2887c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate done:
2917c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(dip, sysino, cpu_id);
2927c478bd9Sstevel@tonic-gate }
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate /*
2967c478bd9Sstevel@tonic-gate  * Redistribute interrupts of the specified weight. The first call has a weight
2977c478bd9Sstevel@tonic-gate  * of weight_max, which can be used to trigger initialization for
2987c478bd9Sstevel@tonic-gate  * redistribution. The inos with weight [weight_max, inf.) should be processed
2997c478bd9Sstevel@tonic-gate  * on the "weight == weight_max" call.  This first call is followed by calls
3007c478bd9Sstevel@tonic-gate  * of decreasing weights, inos of that weight should be processed.  The final
3017c478bd9Sstevel@tonic-gate  * call specifies a weight of zero, this can be used to trigger processing of
3027c478bd9Sstevel@tonic-gate  * stragglers.
3037c478bd9Sstevel@tonic-gate  */
3047c478bd9Sstevel@tonic-gate static void
3057c478bd9Sstevel@tonic-gate px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight)
3067c478bd9Sstevel@tonic-gate {
3077c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = (px_ib_t *)arg;
3087c478bd9Sstevel@tonic-gate 	px_t		*px_p = ib_p->ib_px_p;
3097c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
3107c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t *ino_p;
3117c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_lst;
3127c478bd9Sstevel@tonic-gate 	int32_t		dweight = 0;
3137c478bd9Sstevel@tonic-gate 	int		i;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	/* Redistribute internal interrupts */
3167c478bd9Sstevel@tonic-gate 	if (weight == 0) {
3177c478bd9Sstevel@tonic-gate 		devino_t	ino_pec = px_p->px_inos[PX_INTR_PEC];
318*f8d2de6bSjchu 
3197c478bd9Sstevel@tonic-gate 		mutex_enter(&ib_p->ib_intr_lock);
3207c478bd9Sstevel@tonic-gate 		px_ib_intr_dist_en(dip, intr_dist_cpuid(), ino_pec, B_FALSE);
3217c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	/* Redistribute device interrupts */
3257c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next) {
3287c478bd9Sstevel@tonic-gate 		uint32_t orig_cpuid;
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 		/*
3317c478bd9Sstevel@tonic-gate 		 * Recomputes the sum of interrupt weights of devices that
3327c478bd9Sstevel@tonic-gate 		 * share the same ino upon first call marked by
3337c478bd9Sstevel@tonic-gate 		 * (weight == weight_max).
3347c478bd9Sstevel@tonic-gate 		 */
3357c478bd9Sstevel@tonic-gate 		if (weight == weight_max) {
3367c478bd9Sstevel@tonic-gate 			ino_p->ino_intr_weight = 0;
3377c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
3387c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
3397c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
3407c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
3417c478bd9Sstevel@tonic-gate 				if (dweight > 0)
3427c478bd9Sstevel@tonic-gate 					ino_p->ino_intr_weight += dweight;
3437c478bd9Sstevel@tonic-gate 			}
3447c478bd9Sstevel@tonic-gate 		}
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 		/*
3477c478bd9Sstevel@tonic-gate 		 * As part of redistributing weighted interrupts over cpus,
3487c478bd9Sstevel@tonic-gate 		 * nexus redistributes device interrupts and updates
3497c478bd9Sstevel@tonic-gate 		 * cpu weight. The purpose is for the most light weighted
3507c478bd9Sstevel@tonic-gate 		 * cpu to take the next interrupt and gain weight, therefore
3517c478bd9Sstevel@tonic-gate 		 * attention demanding device gains more cpu attention by
3527c478bd9Sstevel@tonic-gate 		 * making itself heavy.
3537c478bd9Sstevel@tonic-gate 		 */
3547c478bd9Sstevel@tonic-gate 		if ((weight == ino_p->ino_intr_weight) ||
3557c478bd9Sstevel@tonic-gate 		    ((weight >= weight_max) &&
3567c478bd9Sstevel@tonic-gate 		    (ino_p->ino_intr_weight >= weight_max))) {
3577c478bd9Sstevel@tonic-gate 			orig_cpuid = ino_p->ino_cpuid;
3587c478bd9Sstevel@tonic-gate 			if (cpu[orig_cpuid] == NULL)
3597c478bd9Sstevel@tonic-gate 				orig_cpuid = CPU->cpu_id;
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 			/* select cpuid to target and mark ino established */
3627c478bd9Sstevel@tonic-gate 			ino_p->ino_cpuid = intr_dist_cpuid();
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 			/* Add device weight to targeted cpu. */
3657c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
3667c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
3677c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
3687c478bd9Sstevel@tonic-gate 				hrtime_t ticks;
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
3717c478bd9Sstevel@tonic-gate 				intr_dist_cpuid_add_device_weight(
3727c478bd9Sstevel@tonic-gate 				    ino_p->ino_cpuid, ih_lst->ih_dip, dweight);
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 				/*
3757c478bd9Sstevel@tonic-gate 				 * different cpus may have different clock
3767c478bd9Sstevel@tonic-gate 				 * speeds. to account for this, whenever an
3777c478bd9Sstevel@tonic-gate 				 * interrupt is moved to a new CPU, we
3787c478bd9Sstevel@tonic-gate 				 * convert the accumulated ticks into nsec,
3797c478bd9Sstevel@tonic-gate 				 * based upon the clock rate of the prior
3807c478bd9Sstevel@tonic-gate 				 * CPU.
3817c478bd9Sstevel@tonic-gate 				 *
3827c478bd9Sstevel@tonic-gate 				 * It is possible that the prior CPU no longer
3837c478bd9Sstevel@tonic-gate 				 * exists. In this case, fall back to using
3847c478bd9Sstevel@tonic-gate 				 * this CPU's clock rate.
3857c478bd9Sstevel@tonic-gate 				 *
3867c478bd9Sstevel@tonic-gate 				 * Note that the value in ih_ticks has already
3877c478bd9Sstevel@tonic-gate 				 * been corrected for any power savings mode
3887c478bd9Sstevel@tonic-gate 				 * which might have been in effect.
3897c478bd9Sstevel@tonic-gate 				 *
3907c478bd9Sstevel@tonic-gate 				 * because we are updating two fields in
3917c478bd9Sstevel@tonic-gate 				 * ih_t we must lock ih_ks_template_lock to
3927c478bd9Sstevel@tonic-gate 				 * prevent someone from reading the kstats
3937c478bd9Sstevel@tonic-gate 				 * after we set ih_ticks to 0 and before we
3947c478bd9Sstevel@tonic-gate 				 * increment ih_nsec to compensate.
3957c478bd9Sstevel@tonic-gate 				 *
3967c478bd9Sstevel@tonic-gate 				 * we must also protect against the interrupt
3977c478bd9Sstevel@tonic-gate 				 * arriving and incrementing ih_ticks between
3987c478bd9Sstevel@tonic-gate 				 * the time we read it and when we reset it
3997c478bd9Sstevel@tonic-gate 				 * to 0. To do this we use atomic_swap.
4007c478bd9Sstevel@tonic-gate 				 */
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 				mutex_enter(&ih_ks_template_lock);
4037c478bd9Sstevel@tonic-gate 				ticks = atomic_swap_64(&ih_lst->ih_ticks, 0);
4047c478bd9Sstevel@tonic-gate 				ih_lst->ih_nsec += (uint64_t)
4057c478bd9Sstevel@tonic-gate 				    tick2ns(ticks, orig_cpuid);
4067c478bd9Sstevel@tonic-gate 				mutex_exit(&ih_ks_template_lock);
4077c478bd9Sstevel@tonic-gate 			}
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 			/* enable interrupt on new targeted cpu */
4107c478bd9Sstevel@tonic-gate 			px_ib_intr_dist_en(dip, ino_p->ino_cpuid,
4117c478bd9Sstevel@tonic-gate 			    ino_p->ino_ino, B_TRUE);
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * Reset interrupts to IDLE.  This function is called during
4197c478bd9Sstevel@tonic-gate  * panic handling after redistributing interrupts; it's needed to
4207c478bd9Sstevel@tonic-gate  * support dumping to network devices after 'sync' from OBP.
4217c478bd9Sstevel@tonic-gate  *
4227c478bd9Sstevel@tonic-gate  * N.B.  This routine runs in a context where all other threads
4237c478bd9Sstevel@tonic-gate  * are permanently suspended.
4247c478bd9Sstevel@tonic-gate  */
4257c478bd9Sstevel@tonic-gate static uint_t
4267c478bd9Sstevel@tonic-gate px_ib_intr_reset(void *arg)
4277c478bd9Sstevel@tonic-gate {
4287c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = (px_ib_t *)arg;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n");
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS)
4337c478bd9Sstevel@tonic-gate 		return (BF_FATAL);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	return (BF_NONE);
4367c478bd9Sstevel@tonic-gate }
4377c478bd9Sstevel@tonic-gate 
4387c478bd9Sstevel@tonic-gate /*
4397c478bd9Sstevel@tonic-gate  * Locate ino_info structure on ib_p->ib_ino_lst according to ino#
4407c478bd9Sstevel@tonic-gate  * returns NULL if not found.
4417c478bd9Sstevel@tonic-gate  */
4427c478bd9Sstevel@tonic-gate px_ib_ino_info_t *
4437c478bd9Sstevel@tonic-gate px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num)
4447c478bd9Sstevel@tonic-gate {
4457c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*ino_p = ib_p->ib_ino_lst;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	return (ino_p);
4527c478bd9Sstevel@tonic-gate }
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate px_ib_ino_info_t *
4557c478bd9Sstevel@tonic-gate px_ib_new_ino(px_ib_t *ib_p, devino_t ino_num, px_ih_t *ih_p)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*ino_p = kmem_alloc(sizeof (px_ib_ino_info_t),
4587c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
4597c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	ino_p->ino_ino = ino_num;
4627c478bd9Sstevel@tonic-gate 	ino_p->ino_ib_p = ib_p;
4637c478bd9Sstevel@tonic-gate 	ino_p->ino_unclaimed = 0;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino_p->ino_ino,
4667c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS)
4677c478bd9Sstevel@tonic-gate 		return (NULL);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	ino_p->ino_sysino = sysino;
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	/*
4727c478bd9Sstevel@tonic-gate 	 * Cannot disable interrupt since we might share slot
4737c478bd9Sstevel@tonic-gate 	 */
4747c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ih_p;
4757c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_head = ih_p;
4767c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
4777c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ih_p;
4787c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size = 1;
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	ino_p->ino_next = ib_p->ib_ino_lst;
4817c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = ino_p;
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	return (ino_p);
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate /*
4877c478bd9Sstevel@tonic-gate  * The ino_p is retrieved by previous call to px_ib_locate_ino().
4887c478bd9Sstevel@tonic-gate  */
4897c478bd9Sstevel@tonic-gate void
4907c478bd9Sstevel@tonic-gate px_ib_delete_ino(px_ib_t *ib_p, px_ib_ino_info_t *ino_p)
4917c478bd9Sstevel@tonic-gate {
4927c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*list = ib_p->ib_ino_lst;
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	if (list == ino_p)
4977c478bd9Sstevel@tonic-gate 		ib_p->ib_ino_lst = list->ino_next;
4987c478bd9Sstevel@tonic-gate 	else {
4997c478bd9Sstevel@tonic-gate 		for (; list->ino_next != ino_p; list = list->ino_next);
5007c478bd9Sstevel@tonic-gate 		list->ino_next = ino_p->ino_next;
5017c478bd9Sstevel@tonic-gate 	}
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate /*
5057c478bd9Sstevel@tonic-gate  * Free all ino when we are detaching.
5067c478bd9Sstevel@tonic-gate  */
5077c478bd9Sstevel@tonic-gate void
5087c478bd9Sstevel@tonic-gate px_ib_free_ino_all(px_ib_t *ib_p)
5097c478bd9Sstevel@tonic-gate {
5107c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*tmp = ib_p->ib_ino_lst;
5117c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*next = NULL;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	while (tmp) {
5147c478bd9Sstevel@tonic-gate 		next = tmp->ino_next;
5157c478bd9Sstevel@tonic-gate 		kmem_free(tmp, sizeof (px_ib_ino_info_t));
5167c478bd9Sstevel@tonic-gate 		tmp = next;
5177c478bd9Sstevel@tonic-gate 	}
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate int
5217c478bd9Sstevel@tonic-gate px_ib_ino_add_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
5227c478bd9Sstevel@tonic-gate {
5237c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = ino_p->ino_ib_p;
5247c478bd9Sstevel@tonic-gate 	devino_t	ino = ino_p->ino_ino;
5257c478bd9Sstevel@tonic-gate 	sysino_t	sysino = ino_p->ino_sysino;
5267c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
5277c478bd9Sstevel@tonic-gate 	cpuid_t		curr_cpu;
5287c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
5297c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
5307c478bd9Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
5337c478bd9Sstevel@tonic-gate 	ASSERT(ib_p == px_p->px_ib_p);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
5387c478bd9Sstevel@tonic-gate 	if ((ret = px_lib_intr_gettarget(dip, sysino,
5397c478bd9Sstevel@tonic-gate 	    &curr_cpu)) != DDI_SUCCESS) {
5407c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip,
5417c478bd9Sstevel@tonic-gate 		    "px_ib_ino_add_intr px_intr_gettarget() failed\n");
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 		return (ret);
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(dip, sysino);
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupt */
5497c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
5507c478bd9Sstevel@tonic-gate 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
5517c478bd9Sstevel@tonic-gate 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
5527c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
5537c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending "
5547c478bd9Sstevel@tonic-gate 			    "sysino 0x%x(ino 0x%x) timeout",
5557c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
5567c478bd9Sstevel@tonic-gate 			    sysino, ino);
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
5597c478bd9Sstevel@tonic-gate 			break;
5607c478bd9Sstevel@tonic-gate 		}
5617c478bd9Sstevel@tonic-gate 	}
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
5647c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, "
5657c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 		return (ret);
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	/* Link up px_ispec_t portion of the ppd */
5717c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ino_p->ino_ih_head;
5727c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail->ih_next = ih_p;
5737c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
5767c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size++;
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/*
5797c478bd9Sstevel@tonic-gate 	 * If the interrupt was previously blocked (left in pending state)
5807c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
5817c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
5827c478bd9Sstevel@tonic-gate 	 */
5837c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
5847c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
5857c478bd9Sstevel@tonic-gate 		    "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked",
5867c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
5897c478bd9Sstevel@tonic-gate 		if ((ret = px_lib_intr_setstate(dip, sysino,
5907c478bd9Sstevel@tonic-gate 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
5917c478bd9Sstevel@tonic-gate 			DBG(DBG_IB, px_p->px_dip,
5927c478bd9Sstevel@tonic-gate 			    "px_ib_ino_add_intr px_intr_setstate failed\n");
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 			return (ret);
5957c478bd9Sstevel@tonic-gate 		}
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/* Re-enable interrupt */
5997c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(dip, sysino, curr_cpu);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	return (ret);
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate /*
6057c478bd9Sstevel@tonic-gate  * Removes px_ispec_t from the ino's link list.
6067c478bd9Sstevel@tonic-gate  * uses hardware mutex to lock out interrupt threads.
6077c478bd9Sstevel@tonic-gate  * Side effects: interrupt belongs to that ino is turned off on return.
6087c478bd9Sstevel@tonic-gate  * if we are sharing PX slot with other inos, the caller needs
6097c478bd9Sstevel@tonic-gate  * to turn it back on.
6107c478bd9Sstevel@tonic-gate  */
6117c478bd9Sstevel@tonic-gate int
6127c478bd9Sstevel@tonic-gate px_ib_ino_rem_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	devino_t	ino = ino_p->ino_ino;
6157c478bd9Sstevel@tonic-gate 	sysino_t	sysino = ino_p->ino_sysino;
6167c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
6177c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_lst = ino_p->ino_ih_head;
6187c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
6197c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
6207c478bd9Sstevel@tonic-gate 	int		i, ret = DDI_SUCCESS;
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex));
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n",
6257c478bd9Sstevel@tonic-gate 	    ino_p->ino_ino);
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
6287c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(px_p->px_dip, sysino);
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_size == 1) {
6317c478bd9Sstevel@tonic-gate 		if (ih_lst != ih_p)
6327c478bd9Sstevel@tonic-gate 			goto not_found;
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 		/* No need to set head/tail as ino_p will be freed */
6357c478bd9Sstevel@tonic-gate 		goto reset;
6367c478bd9Sstevel@tonic-gate 	}
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupt */
6397c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
6407c478bd9Sstevel@tonic-gate 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
6417c478bd9Sstevel@tonic-gate 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
6427c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
6437c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending "
6447c478bd9Sstevel@tonic-gate 			    "sysino 0x%x(ino 0x%x) timeout",
6457c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
6467c478bd9Sstevel@tonic-gate 			    sysino, ino);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
6497c478bd9Sstevel@tonic-gate 			break;
6507c478bd9Sstevel@tonic-gate 		}
6517c478bd9Sstevel@tonic-gate 	}
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
6547c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, "
6557c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 		return (ret);
6587c478bd9Sstevel@tonic-gate 	}
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	/*
6617c478bd9Sstevel@tonic-gate 	 * If the interrupt was previously blocked (left in pending state)
6627c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
6637c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
6647c478bd9Sstevel@tonic-gate 	 */
6657c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
6667c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: "
6677c478bd9Sstevel@tonic-gate 		    "ino 0x%x has been unblocked",
6687c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
6717c478bd9Sstevel@tonic-gate 		if ((ret = px_lib_intr_setstate(dip, sysino,
6727c478bd9Sstevel@tonic-gate 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
6737c478bd9Sstevel@tonic-gate 			DBG(DBG_IB, px_p->px_dip,
6747c478bd9Sstevel@tonic-gate 			    "px_ib_ino_rem_intr px_intr_setstate failed\n");
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 			return (ret);
6777c478bd9Sstevel@tonic-gate 		}
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	/* Search the link list for ih_p */
6817c478bd9Sstevel@tonic-gate 	for (i = 0; (i < ino_p->ino_ih_size) &&
6827c478bd9Sstevel@tonic-gate 	    (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	if (ih_lst->ih_next != ih_p)
6857c478bd9Sstevel@tonic-gate 		goto not_found;
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	/* Remove ih_p from the link list and maintain the head/tail */
6887c478bd9Sstevel@tonic-gate 	ih_lst->ih_next = ih_p->ih_next;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_head == ih_p)
6917c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_head = ih_p->ih_next;
6927c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_tail == ih_p)
6937c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_tail = ih_lst;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate reset:
6987c478bd9Sstevel@tonic-gate 	if (ih_p->ih_config_handle)
6997c478bd9Sstevel@tonic-gate 		pci_config_teardown(&ih_p->ih_config_handle);
7007c478bd9Sstevel@tonic-gate 	if (ih_p->ih_ksp != NULL)
7017c478bd9Sstevel@tonic-gate 		kstat_delete(ih_p->ih_ksp);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	kmem_free(ih_p, sizeof (px_ih_t));
7047c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size--;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	return (ret);
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate not_found:
7097c478bd9Sstevel@tonic-gate 	DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip,
7107c478bd9Sstevel@tonic-gate 		"ino_p=%x does not have ih_p=%x\n", ino_p, ih_p);
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
7137c478bd9Sstevel@tonic-gate }
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate px_ih_t *
7167c478bd9Sstevel@tonic-gate px_ib_ino_locate_intr(px_ib_ino_info_t *ino_p, dev_info_t *rdip,
7177c478bd9Sstevel@tonic-gate     uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code)
7187c478bd9Sstevel@tonic-gate {
7197c478bd9Sstevel@tonic-gate 	px_ih_t	*ih_lst = ino_p->ino_ih_head;
7207c478bd9Sstevel@tonic-gate 	int	i;
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	for (i = 0; i < ino_p->ino_ih_size; i++, ih_lst = ih_lst->ih_next) {
7237c478bd9Sstevel@tonic-gate 		if ((ih_lst->ih_dip == rdip) && (ih_lst->ih_inum == inum) &&
7247c478bd9Sstevel@tonic-gate 		    (ih_lst->ih_rec_type == rec_type) &&
7257c478bd9Sstevel@tonic-gate 		    (ih_lst->ih_msg_code == msg_code))
7267c478bd9Sstevel@tonic-gate 			return (ih_lst);
7277c478bd9Sstevel@tonic-gate 	}
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	return ((px_ih_t *)NULL);
7307c478bd9Sstevel@tonic-gate }
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate px_ih_t *
7337c478bd9Sstevel@tonic-gate px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum,
7347c478bd9Sstevel@tonic-gate     uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2),
7357c478bd9Sstevel@tonic-gate     caddr_t int_handler_arg1, caddr_t int_handler_arg2,
7367c478bd9Sstevel@tonic-gate     msiq_rec_type_t rec_type, msgcode_t msg_code)
7377c478bd9Sstevel@tonic-gate {
7387c478bd9Sstevel@tonic-gate 	px_ih_t	*ih_p;
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 	ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP);
7417c478bd9Sstevel@tonic-gate 	ih_p->ih_dip = rdip;
7427c478bd9Sstevel@tonic-gate 	ih_p->ih_inum = inum;
7437c478bd9Sstevel@tonic-gate 	ih_p->ih_intr_state = PX_INTR_STATE_DISABLE;
7447c478bd9Sstevel@tonic-gate 	ih_p->ih_handler = int_handler;
7457c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg1 = int_handler_arg1;
7467c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg2 = int_handler_arg2;
7477c478bd9Sstevel@tonic-gate 	ih_p->ih_config_handle = NULL;
7487c478bd9Sstevel@tonic-gate 	ih_p->ih_rec_type = rec_type;
7497c478bd9Sstevel@tonic-gate 	ih_p->ih_msg_code = msg_code;
7507c478bd9Sstevel@tonic-gate 	ih_p->ih_nsec = 0;
7517c478bd9Sstevel@tonic-gate 	ih_p->ih_ticks = 0;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	/*
7547c478bd9Sstevel@tonic-gate 	 * Create pci_intrs::: kstats for all ih types except messages,
7557c478bd9Sstevel@tonic-gate 	 * which represent unusual conditions and don't need to be tracked.
7567c478bd9Sstevel@tonic-gate 	 */
7577c478bd9Sstevel@tonic-gate 	ih_p->ih_ksp = NULL;
7587c478bd9Sstevel@tonic-gate 	if (rec_type == 0 || rec_type == MSI32_REC || rec_type == MSI64_REC) {
7597c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp = kstat_create("pci_intrs",
7607c478bd9Sstevel@tonic-gate 		    atomic_inc_32_nv(&ih_instance), "config", "interrupts",
7617c478bd9Sstevel@tonic-gate 		    KSTAT_TYPE_NAMED,
7627c478bd9Sstevel@tonic-gate 		    sizeof (px_ih_ks_template) / sizeof (kstat_named_t),
7637c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_VIRTUAL);
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 	if (ih_p->ih_ksp != NULL) {
7667c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_data_size += MAXPATHLEN * 2;
7677c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_lock = &ih_ks_template_lock;
7687c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_data = &px_ih_ks_template;
7697c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_private = ih_p;
7707c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_update = ih_ks_update;
7717c478bd9Sstevel@tonic-gate 	}
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	return (ih_p);
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate /*
7777c478bd9Sstevel@tonic-gate  * Only used for fixed or legacy interrupts.
7787c478bd9Sstevel@tonic-gate  */
7797c478bd9Sstevel@tonic-gate int
7807c478bd9Sstevel@tonic-gate px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip,
7817c478bd9Sstevel@tonic-gate     uint_t inum, devino_t ino, uint_t new_intr_state)
7827c478bd9Sstevel@tonic-gate {
7837c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
7847c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t *ino_p;
7857c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_p;
7867c478bd9Sstevel@tonic-gate 	int		ret = DDI_FAILURE;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip, "ib_update_intr_state: %s%d "
7897c478bd9Sstevel@tonic-gate 	    "inum %x devino %x state %x\n", ddi_driver_name(rdip),
7907c478bd9Sstevel@tonic-gate 	    ddi_get_instance(rdip), inum, ino, new_intr_state);
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	if (ino_p = px_ib_locate_ino(ib_p, ino)) {
7957c478bd9Sstevel@tonic-gate 		if (ih_p = px_ib_ino_locate_intr(ino_p, rdip, inum, 0, 0)) {
7967c478bd9Sstevel@tonic-gate 			ih_p->ih_intr_state = new_intr_state;
7977c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
7987c478bd9Sstevel@tonic-gate 		}
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
8027c478bd9Sstevel@tonic-gate 	return (ret);
8037c478bd9Sstevel@tonic-gate }
804