xref: /onnv-gate/usr/src/uts/common/os/ddi_intr_irm.c (revision 9205:94a58d56d3b1)
18561SScott.Carter@Sun.COM /*
28561SScott.Carter@Sun.COM  * CDDL HEADER START
38561SScott.Carter@Sun.COM  *
48561SScott.Carter@Sun.COM  * The contents of this file are subject to the terms of the
58561SScott.Carter@Sun.COM  * Common Development and Distribution License (the "License").
68561SScott.Carter@Sun.COM  * You may not use this file except in compliance with the License.
78561SScott.Carter@Sun.COM  *
88561SScott.Carter@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98561SScott.Carter@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108561SScott.Carter@Sun.COM  * See the License for the specific language governing permissions
118561SScott.Carter@Sun.COM  * and limitations under the License.
128561SScott.Carter@Sun.COM  *
138561SScott.Carter@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148561SScott.Carter@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158561SScott.Carter@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168561SScott.Carter@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178561SScott.Carter@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188561SScott.Carter@Sun.COM  *
198561SScott.Carter@Sun.COM  * CDDL HEADER END
208561SScott.Carter@Sun.COM  */
218561SScott.Carter@Sun.COM /*
228561SScott.Carter@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238561SScott.Carter@Sun.COM  * Use is subject to license terms.
248561SScott.Carter@Sun.COM  */
258561SScott.Carter@Sun.COM 
268561SScott.Carter@Sun.COM #include <sys/note.h>
278561SScott.Carter@Sun.COM #include <sys/sysmacros.h>
288561SScott.Carter@Sun.COM #include <sys/types.h>
298561SScott.Carter@Sun.COM #include <sys/param.h>
308561SScott.Carter@Sun.COM #include <sys/systm.h>
318561SScott.Carter@Sun.COM #include <sys/kmem.h>
328561SScott.Carter@Sun.COM #include <sys/cmn_err.h>
338561SScott.Carter@Sun.COM #include <sys/debug.h>
348561SScott.Carter@Sun.COM #include <sys/ddi.h>
358561SScott.Carter@Sun.COM #include <sys/sunndi.h>
368561SScott.Carter@Sun.COM #include <sys/ndi_impldefs.h>	/* include prototypes */
378561SScott.Carter@Sun.COM 
388561SScott.Carter@Sun.COM /*
398561SScott.Carter@Sun.COM  * Interrupt Resource Management (IRM).
408561SScott.Carter@Sun.COM  */
418561SScott.Carter@Sun.COM 
428561SScott.Carter@Sun.COM #define	DDI_IRM_BALANCE_DELAY	(60)	/* In seconds */
438561SScott.Carter@Sun.COM 
448561SScott.Carter@Sun.COM #define	DDI_IRM_HAS_CB(c)	((c) && (c->cb_flags & DDI_CB_FLAG_INTR))
458561SScott.Carter@Sun.COM 
468561SScott.Carter@Sun.COM #define	DDI_IRM_IS_REDUCIBLE(r)	(((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \
478561SScott.Carter@Sun.COM 				(r->ireq_type == DDI_INTR_TYPE_MSIX)) || \
488561SScott.Carter@Sun.COM 				(r->ireq_flags & DDI_IRM_FLAG_NEW))
498561SScott.Carter@Sun.COM 
508561SScott.Carter@Sun.COM extern pri_t	minclsyspri;
518561SScott.Carter@Sun.COM 
528561SScott.Carter@Sun.COM /* Global policies */
538561SScott.Carter@Sun.COM int		irm_enable = 1;
548561SScott.Carter@Sun.COM boolean_t	irm_active = B_FALSE;
558561SScott.Carter@Sun.COM int		irm_default_policy = DDI_IRM_POLICY_LARGE;
568561SScott.Carter@Sun.COM uint_t		irm_balance_delay = DDI_IRM_BALANCE_DELAY;
578561SScott.Carter@Sun.COM 
588561SScott.Carter@Sun.COM /* Global list of interrupt pools */
598561SScott.Carter@Sun.COM kmutex_t	irm_pools_lock;
608561SScott.Carter@Sun.COM list_t		irm_pools_list;
618561SScott.Carter@Sun.COM 
628561SScott.Carter@Sun.COM /* Global debug tunables */
638561SScott.Carter@Sun.COM #ifdef	DEBUG
648561SScott.Carter@Sun.COM int		irm_debug_policy = 0;
658561SScott.Carter@Sun.COM uint_t		irm_debug_size = 0;
668561SScott.Carter@Sun.COM #endif	/* DEBUG */
678561SScott.Carter@Sun.COM 
688561SScott.Carter@Sun.COM static void	irm_balance_thread(ddi_irm_pool_t *);
698561SScott.Carter@Sun.COM static void	i_ddi_irm_balance(ddi_irm_pool_t *);
708561SScott.Carter@Sun.COM static void	i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t);
718561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce(ddi_irm_pool_t *pool);
728561SScott.Carter@Sun.COM static int	i_ddi_irm_reduce_large(ddi_irm_pool_t *, int);
738561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce_large_resort(ddi_irm_pool_t *);
748561SScott.Carter@Sun.COM static int	i_ddi_irm_reduce_even(ddi_irm_pool_t *, int);
758561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce_new(ddi_irm_pool_t *, int);
768561SScott.Carter@Sun.COM static void	i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *);
778561SScott.Carter@Sun.COM static int	i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *);
788561SScott.Carter@Sun.COM 
798561SScott.Carter@Sun.COM /*
808561SScott.Carter@Sun.COM  * OS Initialization Routines
818561SScott.Carter@Sun.COM  */
828561SScott.Carter@Sun.COM 
838561SScott.Carter@Sun.COM /*
848561SScott.Carter@Sun.COM  * irm_init()
858561SScott.Carter@Sun.COM  *
868561SScott.Carter@Sun.COM  *	Initialize IRM subsystem before any drivers are attached.
878561SScott.Carter@Sun.COM  */
888561SScott.Carter@Sun.COM void
898561SScott.Carter@Sun.COM irm_init(void)
908561SScott.Carter@Sun.COM {
918561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
928561SScott.Carter@Sun.COM 	if (!irm_enable)
938561SScott.Carter@Sun.COM 		return;
948561SScott.Carter@Sun.COM 
958561SScott.Carter@Sun.COM 	/* Verify that the default balancing policy is valid */
968561SScott.Carter@Sun.COM 	if (!DDI_IRM_POLICY_VALID(irm_default_policy))
978561SScott.Carter@Sun.COM 		irm_default_policy = DDI_IRM_POLICY_LARGE;
988561SScott.Carter@Sun.COM 
998561SScott.Carter@Sun.COM 	/* Initialize the global list of interrupt pools */
1008561SScott.Carter@Sun.COM 	mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL);
1018561SScott.Carter@Sun.COM 	list_create(&irm_pools_list, sizeof (ddi_irm_pool_t),
1028561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_pool_t, ipool_link));
1038561SScott.Carter@Sun.COM }
1048561SScott.Carter@Sun.COM 
1058561SScott.Carter@Sun.COM /*
1068561SScott.Carter@Sun.COM  * i_ddi_irm_poststartup()
1078561SScott.Carter@Sun.COM  *
1088561SScott.Carter@Sun.COM  *	IRM is not activated until after the IO subsystem is initialized.
1098561SScott.Carter@Sun.COM  *	When activated, per-pool balancing threads are spawned and a flag
1108561SScott.Carter@Sun.COM  *	is set so that all future pools will be activated when created.
1118561SScott.Carter@Sun.COM  *
1128561SScott.Carter@Sun.COM  *	NOTE: the global variable 'irm_enable' disables IRM if zero.
1138561SScott.Carter@Sun.COM  */
1148561SScott.Carter@Sun.COM void
1158561SScott.Carter@Sun.COM i_ddi_irm_poststartup(void)
1168561SScott.Carter@Sun.COM {
1178561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1188561SScott.Carter@Sun.COM 
1198561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
1208561SScott.Carter@Sun.COM 	if (!irm_enable)
1218561SScott.Carter@Sun.COM 		return;
1228561SScott.Carter@Sun.COM 
1238561SScott.Carter@Sun.COM 	/* Lock the global list */
1248561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1258561SScott.Carter@Sun.COM 
1268561SScott.Carter@Sun.COM 	/* Activate all defined pools */
1278561SScott.Carter@Sun.COM 	for (pool_p = list_head(&irm_pools_list); pool_p;
1288561SScott.Carter@Sun.COM 	    pool_p = list_next(&irm_pools_list, pool_p))
1298561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
1308561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1318561SScott.Carter@Sun.COM 
1328561SScott.Carter@Sun.COM 	/* Set future pools to be active */
1338561SScott.Carter@Sun.COM 	irm_active = B_TRUE;
1348561SScott.Carter@Sun.COM 
1358561SScott.Carter@Sun.COM 	/* Unlock the global list */
1368561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1378561SScott.Carter@Sun.COM }
1388561SScott.Carter@Sun.COM 
1398561SScott.Carter@Sun.COM /*
1408561SScott.Carter@Sun.COM  * NDI interfaces for creating/destroying IRM pools.
1418561SScott.Carter@Sun.COM  */
1428561SScott.Carter@Sun.COM 
1438561SScott.Carter@Sun.COM /*
1448561SScott.Carter@Sun.COM  * ndi_irm_create()
1458561SScott.Carter@Sun.COM  *
1468561SScott.Carter@Sun.COM  *	Nexus interface to create an IRM pool.  Create the new
1478561SScott.Carter@Sun.COM  *	pool and add it to the global list of interrupt pools.
1488561SScott.Carter@Sun.COM  */
1498561SScott.Carter@Sun.COM int
1508561SScott.Carter@Sun.COM ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp,
1518561SScott.Carter@Sun.COM     ddi_irm_pool_t **pool_retp)
1528561SScott.Carter@Sun.COM {
1538561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1548561SScott.Carter@Sun.COM 
1558561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
1568561SScott.Carter@Sun.COM 	ASSERT(paramsp != NULL);
1578561SScott.Carter@Sun.COM 	ASSERT(pool_retp != NULL);
1588561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_total >= 1);
1598561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_types != 0);
1608561SScott.Carter@Sun.COM 
1618561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip));
1628561SScott.Carter@Sun.COM 
1638561SScott.Carter@Sun.COM 	/* Check if IRM is enabled */
1648561SScott.Carter@Sun.COM 	if (!irm_enable)
1658561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1668561SScott.Carter@Sun.COM 
1678561SScott.Carter@Sun.COM 	/* Validate parameters */
1688561SScott.Carter@Sun.COM 	if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) ||
1698925SEvan.Yan@Sun.COM 	    (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0))
1708561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1718561SScott.Carter@Sun.COM 
1728561SScott.Carter@Sun.COM 	/* Allocate and initialize the pool */
1738561SScott.Carter@Sun.COM 	pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP);
1748561SScott.Carter@Sun.COM 	pool_p->ipool_owner = dip;
1758561SScott.Carter@Sun.COM 	pool_p->ipool_policy = irm_default_policy;
1768561SScott.Carter@Sun.COM 	pool_p->ipool_types = paramsp->iparams_types;
1778561SScott.Carter@Sun.COM 	pool_p->ipool_totsz = paramsp->iparams_total;
1788925SEvan.Yan@Sun.COM 	pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC,
1798925SEvan.Yan@Sun.COM 	    paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER));
1808561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t),
1818561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_link));
1828561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t),
1838561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_scratch_link));
1848561SScott.Carter@Sun.COM 	cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL);
1858561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL);
1868561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL);
1878561SScott.Carter@Sun.COM 
1888561SScott.Carter@Sun.COM 	/* Add to global list of pools */
1898561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1908561SScott.Carter@Sun.COM 	list_insert_tail(&irm_pools_list, pool_p);
1918561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1928561SScott.Carter@Sun.COM 
1938561SScott.Carter@Sun.COM 	/* If IRM is active, then activate the pool */
1948561SScott.Carter@Sun.COM 	if (irm_active)
1958561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
1968561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1978561SScott.Carter@Sun.COM 
1988561SScott.Carter@Sun.COM 	*pool_retp = pool_p;
1998561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
2008561SScott.Carter@Sun.COM }
2018561SScott.Carter@Sun.COM 
2028561SScott.Carter@Sun.COM /*
2038561SScott.Carter@Sun.COM  * ndi_irm_destroy()
2048561SScott.Carter@Sun.COM  *
2058561SScott.Carter@Sun.COM  *	Nexus interface to destroy an IRM pool.  Destroy the pool
2068561SScott.Carter@Sun.COM  *	and remove it from the global list of interrupt pools.
2078561SScott.Carter@Sun.COM  */
2088561SScott.Carter@Sun.COM int
2098561SScott.Carter@Sun.COM ndi_irm_destroy(ddi_irm_pool_t *pool_p)
2108561SScott.Carter@Sun.COM {
2118561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
2128561SScott.Carter@Sun.COM 	ASSERT(pool_p->ipool_resno == 0);
2138561SScott.Carter@Sun.COM 
2148561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n",
2158561SScott.Carter@Sun.COM 	    (void *)pool_p));
2168561SScott.Carter@Sun.COM 
2178561SScott.Carter@Sun.COM 	/* Validate parameters */
2188561SScott.Carter@Sun.COM 	if (pool_p == NULL)
2198561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
2208561SScott.Carter@Sun.COM 
2218561SScott.Carter@Sun.COM 	/* Validate that pool is empty */
2228561SScott.Carter@Sun.COM 	if (pool_p->ipool_resno != 0)
2238561SScott.Carter@Sun.COM 		return (NDI_BUSY);
2248561SScott.Carter@Sun.COM 
2258561SScott.Carter@Sun.COM 	/* Remove the pool from the global list */
2268561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
2278561SScott.Carter@Sun.COM 	list_remove(&irm_pools_list, pool_p);
2288561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
2298561SScott.Carter@Sun.COM 
2308561SScott.Carter@Sun.COM 	/* Terminate the balancing thread */
2318561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
2328561SScott.Carter@Sun.COM 	if (pool_p->ipool_thread &&
2338561SScott.Carter@Sun.COM 	    (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) {
2348561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT;
2358561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
2368885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
2378561SScott.Carter@Sun.COM 		thread_join(pool_p->ipool_thread->t_did);
2388885SJustin.Frank@Sun.COM 	} else
2398885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
2408561SScott.Carter@Sun.COM 
2418561SScott.Carter@Sun.COM 	/* Destroy the pool */
2428561SScott.Carter@Sun.COM 	cv_destroy(&pool_p->ipool_cv);
2438561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_lock);
2448561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_navail_lock);
2458561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_req_list);
2468561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_scratch_list);
2478561SScott.Carter@Sun.COM 	kmem_free(pool_p, sizeof (ddi_irm_pool_t));
2488561SScott.Carter@Sun.COM 
2498561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
2508561SScott.Carter@Sun.COM }
2518561SScott.Carter@Sun.COM 
2528561SScott.Carter@Sun.COM /*
2538561SScott.Carter@Sun.COM  * Insert/Modify/Remove Interrupt Requests
2548561SScott.Carter@Sun.COM  */
2558561SScott.Carter@Sun.COM 
2568561SScott.Carter@Sun.COM /*
2578561SScott.Carter@Sun.COM  * i_ddi_irm_insert()
2588561SScott.Carter@Sun.COM  *
2598561SScott.Carter@Sun.COM  *	Insert a new request into an interrupt pool, and balance the pool.
2608561SScott.Carter@Sun.COM  */
2618561SScott.Carter@Sun.COM int
2628561SScott.Carter@Sun.COM i_ddi_irm_insert(dev_info_t *dip, int type, int count)
2638561SScott.Carter@Sun.COM {
2648561SScott.Carter@Sun.COM 	ddi_cb_t	*cb_p;
2658561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
2668561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
2678561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
2688561SScott.Carter@Sun.COM 	uint_t		nreq, nmin, npartial;
2698561SScott.Carter@Sun.COM 	boolean_t	irm_flag = B_FALSE;
2708561SScott.Carter@Sun.COM 
2718561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
2728561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
2738561SScott.Carter@Sun.COM 	ASSERT(count > 0);
2748561SScott.Carter@Sun.COM 
2758561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n",
2768561SScott.Carter@Sun.COM 	    (void *)dip, type, count));
2778561SScott.Carter@Sun.COM 
2788561SScott.Carter@Sun.COM 	/* Validate parameters */
2798561SScott.Carter@Sun.COM 	if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) {
2808561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n"));
2818561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
2828561SScott.Carter@Sun.COM 	}
2838561SScott.Carter@Sun.COM 
2848561SScott.Carter@Sun.COM 	/* Check for an existing request */
2858561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
2868561SScott.Carter@Sun.COM 	    (intr_p->devi_irm_req_p != NULL))
2878561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
2888561SScott.Carter@Sun.COM 
2898561SScott.Carter@Sun.COM 	/* Check for IRM support from the system */
2908561SScott.Carter@Sun.COM 	if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) {
2918561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n"));
2928561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
2938561SScott.Carter@Sun.COM 	}
2948561SScott.Carter@Sun.COM 
2958561SScott.Carter@Sun.COM 	/* Check for IRM support from the driver */
2968561SScott.Carter@Sun.COM 	if (((cb_p = DEVI(dip)->devi_cb_p) != NULL) && DDI_IRM_HAS_CB(cb_p) &&
2978561SScott.Carter@Sun.COM 	    (type == DDI_INTR_TYPE_MSIX))
2988561SScott.Carter@Sun.COM 		irm_flag = B_TRUE;
2998561SScott.Carter@Sun.COM 
3008561SScott.Carter@Sun.COM 	/* Determine request size */
301*9205SEvan.Yan@Sun.COM 	nreq = (irm_flag) ? count :
302*9205SEvan.Yan@Sun.COM 	    MIN(count, i_ddi_intr_get_current_navail(dip, type));
3038561SScott.Carter@Sun.COM 	nmin = (irm_flag) ? 1 : nreq;
3048561SScott.Carter@Sun.COM 	npartial = MIN(nreq, pool_p->ipool_defsz);
3058561SScott.Carter@Sun.COM 
3068561SScott.Carter@Sun.COM 	/* Allocate and initialize the request */
3078561SScott.Carter@Sun.COM 	req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP);
3088561SScott.Carter@Sun.COM 	req_p->ireq_type = type;
3098561SScott.Carter@Sun.COM 	req_p->ireq_dip = dip;
3108561SScott.Carter@Sun.COM 	req_p->ireq_pool_p = pool_p;
3118561SScott.Carter@Sun.COM 	req_p->ireq_nreq = nreq;
3128561SScott.Carter@Sun.COM 	req_p->ireq_flags = DDI_IRM_FLAG_NEW;
3138561SScott.Carter@Sun.COM 	if (DDI_IRM_HAS_CB(cb_p))
3148561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
3158561SScott.Carter@Sun.COM 
3168561SScott.Carter@Sun.COM 	/* Lock the pool */
3178561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
3188561SScott.Carter@Sun.COM 
3198561SScott.Carter@Sun.COM 	/* Check for minimal fit before inserting */
3208561SScott.Carter@Sun.COM 	if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) {
3218561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
3228561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
3238561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3248561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
3258561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
3268561SScott.Carter@Sun.COM 	}
3278561SScott.Carter@Sun.COM 
3288561SScott.Carter@Sun.COM 	/* Insert the request into the pool */
3298561SScott.Carter@Sun.COM 	pool_p->ipool_reqno += nreq;
3308561SScott.Carter@Sun.COM 	pool_p->ipool_minno += nmin;
3318561SScott.Carter@Sun.COM 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
3328561SScott.Carter@Sun.COM 
3338561SScott.Carter@Sun.COM 	/*
3348561SScott.Carter@Sun.COM 	 * Try to fulfill the request.
3358561SScott.Carter@Sun.COM 	 *
3368561SScott.Carter@Sun.COM 	 * If all the interrupts are available, and either the request
3378561SScott.Carter@Sun.COM 	 * is static or the pool is active, then just take them directly.
3388561SScott.Carter@Sun.COM 	 *
3398561SScott.Carter@Sun.COM 	 * If only some of the interrupts are available, and the request
3408561SScott.Carter@Sun.COM 	 * can receive future callbacks, then take some now but queue the
3418561SScott.Carter@Sun.COM 	 * pool to be rebalanced later.
3428561SScott.Carter@Sun.COM 	 *
3438561SScott.Carter@Sun.COM 	 * Otherwise, immediately rebalance the pool and wait.
3448561SScott.Carter@Sun.COM 	 */
3458561SScott.Carter@Sun.COM 	if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) &&
3468561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) {
3478561SScott.Carter@Sun.COM 
3488561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3498561SScott.Carter@Sun.COM 		    "request completely fulfilled.\n"));
3508561SScott.Carter@Sun.COM 		pool_p->ipool_resno += nreq;
3518561SScott.Carter@Sun.COM 		req_p->ireq_navail = nreq;
3528561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3538561SScott.Carter@Sun.COM 
3548561SScott.Carter@Sun.COM 	} else if (irm_flag &&
3558561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) {
3568561SScott.Carter@Sun.COM 
3578561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3588561SScott.Carter@Sun.COM 		    "request partially fulfilled.\n"));
3598561SScott.Carter@Sun.COM 		pool_p->ipool_resno += npartial;
3608561SScott.Carter@Sun.COM 		req_p->ireq_navail = npartial;
3618561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3628561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
3638561SScott.Carter@Sun.COM 
3648561SScott.Carter@Sun.COM 	} else {
3658561SScott.Carter@Sun.COM 
3668561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3678561SScott.Carter@Sun.COM 		    "request needs immediate rebalance.\n"));
3688561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
3698561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3708561SScott.Carter@Sun.COM 	}
3718561SScott.Carter@Sun.COM 
3728561SScott.Carter@Sun.COM 	/* Fail if the request cannot be fulfilled at all */
3738561SScott.Carter@Sun.COM 	if (req_p->ireq_navail == 0) {
3748561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
3758561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
3768561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3778561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= nreq;
3788561SScott.Carter@Sun.COM 		pool_p->ipool_minno -= nmin;
3798561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
3808561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
3818561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
3828561SScott.Carter@Sun.COM 	}
3838561SScott.Carter@Sun.COM 
3848561SScott.Carter@Sun.COM 	/* Unlock the pool */
3858561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
3868561SScott.Carter@Sun.COM 
3878561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = req_p;
3888561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
3898561SScott.Carter@Sun.COM }
3908561SScott.Carter@Sun.COM 
3918561SScott.Carter@Sun.COM /*
3928561SScott.Carter@Sun.COM  * i_ddi_irm_modify()
3938561SScott.Carter@Sun.COM  *
3948561SScott.Carter@Sun.COM  *	Modify an existing request in an interrupt pool, and balance the pool.
3958561SScott.Carter@Sun.COM  */
3968561SScott.Carter@Sun.COM int
3978561SScott.Carter@Sun.COM i_ddi_irm_modify(dev_info_t *dip, int nreq)
3988561SScott.Carter@Sun.COM {
3998561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
4008561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
4018561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
4028561SScott.Carter@Sun.COM 
4038561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
4048561SScott.Carter@Sun.COM 
4058561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n",
4068561SScott.Carter@Sun.COM 	    (void *)dip, nreq));
4078561SScott.Carter@Sun.COM 
4088561SScott.Carter@Sun.COM 	/* Validate parameters */
4098561SScott.Carter@Sun.COM 	if ((dip == NULL) || (nreq < 1)) {
4108561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
4118561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4128561SScott.Carter@Sun.COM 	}
4138561SScott.Carter@Sun.COM 
4148561SScott.Carter@Sun.COM 	/* Check that the operation is supported */
4158561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
4168561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p) ||
4178561SScott.Carter@Sun.COM 	    !DDI_IRM_IS_REDUCIBLE(req_p)) {
4188561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not supported\n"));
4198561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
4208561SScott.Carter@Sun.COM 	}
4218561SScott.Carter@Sun.COM 
4228561SScott.Carter@Sun.COM 	/* Validate request size is not too large */
4238561SScott.Carter@Sun.COM 	if (nreq > intr_p->devi_intr_sup_nintrs) {
4248561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
4258561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4268561SScott.Carter@Sun.COM 	}
4278561SScott.Carter@Sun.COM 
4288561SScott.Carter@Sun.COM 	/*
4298561SScott.Carter@Sun.COM 	 * Modify request, but only if new size is different.
4308561SScott.Carter@Sun.COM 	 */
4318561SScott.Carter@Sun.COM 	if (nreq != req_p->ireq_nreq) {
4328561SScott.Carter@Sun.COM 
4338561SScott.Carter@Sun.COM 		/* Lock the pool */
4348561SScott.Carter@Sun.COM 		pool_p = req_p->ireq_pool_p;
4358561SScott.Carter@Sun.COM 		mutex_enter(&pool_p->ipool_lock);
4368561SScott.Carter@Sun.COM 
4378561SScott.Carter@Sun.COM 		/* Update pool and request */
4388561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
4398561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
4408561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
4418561SScott.Carter@Sun.COM 
4428561SScott.Carter@Sun.COM 		/* Re-sort request in the pool */
4438561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
4448561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
4458561SScott.Carter@Sun.COM 
4468561SScott.Carter@Sun.COM 		/* Queue pool to be rebalanced */
4478561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
4488561SScott.Carter@Sun.COM 
4498561SScott.Carter@Sun.COM 		/* Unlock the pool */
4508561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
4518561SScott.Carter@Sun.COM 	}
4528561SScott.Carter@Sun.COM 
4538561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
4548561SScott.Carter@Sun.COM }
4558561SScott.Carter@Sun.COM 
4568561SScott.Carter@Sun.COM /*
4578561SScott.Carter@Sun.COM  * i_ddi_irm_remove()
4588561SScott.Carter@Sun.COM  *
4598561SScott.Carter@Sun.COM  *	Remove a request from an interrupt pool, and balance the pool.
4608561SScott.Carter@Sun.COM  */
4618561SScott.Carter@Sun.COM int
4628561SScott.Carter@Sun.COM i_ddi_irm_remove(dev_info_t *dip)
4638561SScott.Carter@Sun.COM {
4648561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
4658561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
4668561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
4678561SScott.Carter@Sun.COM 	uint_t		nmin;
4688561SScott.Carter@Sun.COM 
4698561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
4708561SScott.Carter@Sun.COM 
4718561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip));
4728561SScott.Carter@Sun.COM 
4738561SScott.Carter@Sun.COM 	/* Validate parameters */
4748561SScott.Carter@Sun.COM 	if (dip == NULL) {
4758561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n"));
4768561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4778561SScott.Carter@Sun.COM 	}
4788561SScott.Carter@Sun.COM 
4798561SScott.Carter@Sun.COM 	/* Check if the device has a request */
4808561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
4818561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
4828561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n"));
4838561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4848561SScott.Carter@Sun.COM 	}
4858561SScott.Carter@Sun.COM 
4868561SScott.Carter@Sun.COM 	/* Lock the pool */
4878561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
4888561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
4898561SScott.Carter@Sun.COM 
4908561SScott.Carter@Sun.COM 	/* Remove request */
4918561SScott.Carter@Sun.COM 	nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq;
4928561SScott.Carter@Sun.COM 	pool_p->ipool_minno -= nmin;
4938561SScott.Carter@Sun.COM 	pool_p->ipool_reqno -= req_p->ireq_nreq;
4948561SScott.Carter@Sun.COM 	pool_p->ipool_resno -= req_p->ireq_navail;
4958561SScott.Carter@Sun.COM 	list_remove(&pool_p->ipool_req_list, req_p);
4968561SScott.Carter@Sun.COM 
4978561SScott.Carter@Sun.COM 	/* Queue pool to be rebalanced */
4988561SScott.Carter@Sun.COM 	i_ddi_irm_enqueue(pool_p, B_FALSE);
4998561SScott.Carter@Sun.COM 
5008561SScott.Carter@Sun.COM 	/* Unlock the pool */
5018561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
5028561SScott.Carter@Sun.COM 
5038561SScott.Carter@Sun.COM 	/* Destroy the request */
5048561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = NULL;
5058561SScott.Carter@Sun.COM 	kmem_free(req_p, sizeof (ddi_irm_req_t));
5068561SScott.Carter@Sun.COM 
5078561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
5088561SScott.Carter@Sun.COM }
5098561SScott.Carter@Sun.COM 
5108561SScott.Carter@Sun.COM /*
5118561SScott.Carter@Sun.COM  * i_ddi_irm_set_cb()
5128561SScott.Carter@Sun.COM  *
5138561SScott.Carter@Sun.COM  *	Change the callback flag for a request, in response to
5148561SScott.Carter@Sun.COM  *	a change in its callback registration.  Then rebalance
5158561SScott.Carter@Sun.COM  *	the interrupt pool.
5168561SScott.Carter@Sun.COM  *
5178561SScott.Carter@Sun.COM  *	NOTE: the request is not locked because the navail value
5188561SScott.Carter@Sun.COM  *	      is not directly affected.  The balancing thread may
5198561SScott.Carter@Sun.COM  *	      modify the navail value in the background after it
5208561SScott.Carter@Sun.COM  *	      locks the request itself.
5218561SScott.Carter@Sun.COM  */
5228561SScott.Carter@Sun.COM void
5238561SScott.Carter@Sun.COM i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag)
5248561SScott.Carter@Sun.COM {
5258561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
5268561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
5278561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
5288561SScott.Carter@Sun.COM 	uint_t		nreq;
5298561SScott.Carter@Sun.COM 
5308561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
5318561SScott.Carter@Sun.COM 
5328561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n",
5338561SScott.Carter@Sun.COM 	    (void *)dip, (int)has_cb_flag));
5348561SScott.Carter@Sun.COM 
5358561SScott.Carter@Sun.COM 	/* Validate parameters */
5368561SScott.Carter@Sun.COM 	if (dip == NULL)
5378561SScott.Carter@Sun.COM 		return;
5388561SScott.Carter@Sun.COM 
5398561SScott.Carter@Sun.COM 	/* Check for association with interrupt pool */
5408561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
5418561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
5428561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n"));
5438561SScott.Carter@Sun.COM 		return;
5448561SScott.Carter@Sun.COM 	}
5458561SScott.Carter@Sun.COM 
5468561SScott.Carter@Sun.COM 	/* Lock the pool */
5478561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
5488561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
5498561SScott.Carter@Sun.COM 
5508561SScott.Carter@Sun.COM 	/*
5518561SScott.Carter@Sun.COM 	 * Update the request and the pool
5528561SScott.Carter@Sun.COM 	 */
5538561SScott.Carter@Sun.COM 	if (has_cb_flag) {
5548561SScott.Carter@Sun.COM 
5558561SScott.Carter@Sun.COM 		/* Update pool statistics */
5568561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
5578561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= (req_p->ireq_nreq - 1);
5588561SScott.Carter@Sun.COM 
5598561SScott.Carter@Sun.COM 		/* Update request */
5608561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
5618561SScott.Carter@Sun.COM 
5628561SScott.Carter@Sun.COM 		/* Rebalance in background */
5638561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
5648561SScott.Carter@Sun.COM 
5658561SScott.Carter@Sun.COM 	} else {
5668561SScott.Carter@Sun.COM 
5678561SScott.Carter@Sun.COM 		/* Determine new request size */
5688561SScott.Carter@Sun.COM 		nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz);
5698561SScott.Carter@Sun.COM 
5708561SScott.Carter@Sun.COM 		/* Update pool statistics */
5718561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
5728561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
5738561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) {
5748561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= 1;
5758561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
5768561SScott.Carter@Sun.COM 		} else {
5778561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= req_p->ireq_nreq;
5788561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
5798561SScott.Carter@Sun.COM 		}
5808561SScott.Carter@Sun.COM 
5818561SScott.Carter@Sun.COM 		/* Update request size, and re-sort in pool */
5828561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
5838561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
5848561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
5858561SScott.Carter@Sun.COM 
5868561SScott.Carter@Sun.COM 		/* Rebalance synchronously, before losing callback */
5878561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
5888561SScott.Carter@Sun.COM 
5898561SScott.Carter@Sun.COM 		/* Remove callback flag */
5908561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK);
5918561SScott.Carter@Sun.COM 	}
5928561SScott.Carter@Sun.COM 
5938561SScott.Carter@Sun.COM 	/* Unlock the pool */
5948561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
5958561SScott.Carter@Sun.COM }
5968561SScott.Carter@Sun.COM 
5978561SScott.Carter@Sun.COM /*
5988561SScott.Carter@Sun.COM  * Interrupt Pool Balancing
5998561SScott.Carter@Sun.COM  */
6008561SScott.Carter@Sun.COM 
6018561SScott.Carter@Sun.COM /*
6028561SScott.Carter@Sun.COM  * irm_balance_thread()
6038561SScott.Carter@Sun.COM  *
6048561SScott.Carter@Sun.COM  *	One instance of this thread operates per each defined IRM pool.
6058561SScott.Carter@Sun.COM  *	It does the initial activation of the pool, as well as balancing
6068561SScott.Carter@Sun.COM  *	any requests that were queued up before the pool was active.
6078561SScott.Carter@Sun.COM  *	Once active, it waits forever to service balance operations.
6088561SScott.Carter@Sun.COM  */
6098561SScott.Carter@Sun.COM static void
6108561SScott.Carter@Sun.COM irm_balance_thread(ddi_irm_pool_t *pool_p)
6118561SScott.Carter@Sun.COM {
6128561SScott.Carter@Sun.COM 	clock_t		interval, wakeup;
6138561SScott.Carter@Sun.COM 
6148561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n",
6158561SScott.Carter@Sun.COM 	    (void *)pool_p));
6168561SScott.Carter@Sun.COM 
6178561SScott.Carter@Sun.COM 	/* Lock the pool */
6188561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
6198561SScott.Carter@Sun.COM 
6208561SScott.Carter@Sun.COM 	/* Perform initial balance if required */
6218561SScott.Carter@Sun.COM 	if (pool_p->ipool_reqno > pool_p->ipool_resno)
6228561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
6238561SScott.Carter@Sun.COM 
6248561SScott.Carter@Sun.COM 	/* Activate the pool */
6258561SScott.Carter@Sun.COM 	pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE;
6268561SScott.Carter@Sun.COM 
6278561SScott.Carter@Sun.COM 	/* Main loop */
6288561SScott.Carter@Sun.COM 	for (;;) {
6298561SScott.Carter@Sun.COM 
6308561SScott.Carter@Sun.COM 		/* Compute the delay interval */
6318561SScott.Carter@Sun.COM 		interval = drv_usectohz(irm_balance_delay * 1000000);
6328561SScott.Carter@Sun.COM 
6338561SScott.Carter@Sun.COM 		/* Sleep until queued */
6348561SScott.Carter@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
6358561SScott.Carter@Sun.COM 
6368561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n"));
6378561SScott.Carter@Sun.COM 
6388561SScott.Carter@Sun.COM 		/* Wait one interval, or until there are waiters */
6398561SScott.Carter@Sun.COM 		if ((interval > 0) &&
6408561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) &&
6418561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) {
6428561SScott.Carter@Sun.COM 			wakeup = ddi_get_lbolt() + interval;
6438561SScott.Carter@Sun.COM 			(void) cv_timedwait(&pool_p->ipool_cv,
6448561SScott.Carter@Sun.COM 			    &pool_p->ipool_lock, wakeup);
6458561SScott.Carter@Sun.COM 		}
6468561SScott.Carter@Sun.COM 
6478561SScott.Carter@Sun.COM 		/* Check if awakened to exit */
6488561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) {
6498561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
6508561SScott.Carter@Sun.COM 			    "irm_balance_thread: exiting...\n"));
6518561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_lock);
6528561SScott.Carter@Sun.COM 			thread_exit();
6538561SScott.Carter@Sun.COM 		}
6548561SScott.Carter@Sun.COM 
6558561SScott.Carter@Sun.COM 		/* Balance the pool */
6568561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
6578561SScott.Carter@Sun.COM 
6588561SScott.Carter@Sun.COM 		/* Notify waiters */
6598561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) {
6608561SScott.Carter@Sun.COM 			cv_broadcast(&pool_p->ipool_cv);
6618561SScott.Carter@Sun.COM 			pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS);
6628561SScott.Carter@Sun.COM 		}
6638561SScott.Carter@Sun.COM 
6648561SScott.Carter@Sun.COM 		/* Clear QUEUED condition */
6658561SScott.Carter@Sun.COM 		pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED);
6668561SScott.Carter@Sun.COM 	}
6678561SScott.Carter@Sun.COM }
6688561SScott.Carter@Sun.COM 
6698561SScott.Carter@Sun.COM /*
6708561SScott.Carter@Sun.COM  * i_ddi_irm_balance()
6718561SScott.Carter@Sun.COM  *
6728561SScott.Carter@Sun.COM  *	Balance a pool.  The general algorithm is to first reset all
6738561SScott.Carter@Sun.COM  *	requests to their maximum size, use reduction algorithms to
6748561SScott.Carter@Sun.COM  *	solve any imbalance, and then notify affected drivers.
6758561SScott.Carter@Sun.COM  */
6768561SScott.Carter@Sun.COM static void
6778561SScott.Carter@Sun.COM i_ddi_irm_balance(ddi_irm_pool_t *pool_p)
6788561SScott.Carter@Sun.COM {
6798561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
6808561SScott.Carter@Sun.COM 
6818561SScott.Carter@Sun.COM #ifdef	DEBUG
6828561SScott.Carter@Sun.COM 	uint_t		debug_totsz = 0;
6838561SScott.Carter@Sun.COM 	int		debug_policy = 0;
6848561SScott.Carter@Sun.COM #endif	/* DEBUG */
6858561SScott.Carter@Sun.COM 
6868561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
6878561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
6888561SScott.Carter@Sun.COM 
6898561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n",
6908561SScott.Carter@Sun.COM 	    (void *)pool_p));
6918561SScott.Carter@Sun.COM 
6928561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Adjust size and policy settings */
6938561SScott.Carter@Sun.COM 	if (irm_debug_size > pool_p->ipool_minno) {
6948561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n",
6958561SScott.Carter@Sun.COM 		    irm_debug_size));
6968561SScott.Carter@Sun.COM 		debug_totsz = pool_p->ipool_totsz;
6978561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = irm_debug_size;
6988561SScott.Carter@Sun.COM 	}
6998561SScott.Carter@Sun.COM 	if (DDI_IRM_POLICY_VALID(irm_debug_policy)) {
7008561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
7018561SScott.Carter@Sun.COM 		    "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy));
7028561SScott.Carter@Sun.COM 		debug_policy = pool_p->ipool_policy;
7038561SScott.Carter@Sun.COM 		pool_p->ipool_policy = irm_debug_policy;
7048561SScott.Carter@Sun.COM 	}
7058561SScott.Carter@Sun.COM #endif	/* DEBUG */
7068561SScott.Carter@Sun.COM 
7078561SScott.Carter@Sun.COM 	/* Lock the availability lock */
7088561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_navail_lock);
7098561SScott.Carter@Sun.COM 
7108561SScott.Carter@Sun.COM 	/*
7118561SScott.Carter@Sun.COM 	 * Put all of the reducible requests into a scratch list.
7128561SScott.Carter@Sun.COM 	 * Reset each one of them to their maximum availability.
7138561SScott.Carter@Sun.COM 	 */
7148561SScott.Carter@Sun.COM 	for (req_p = list_head(&pool_p->ipool_req_list); req_p;
7158561SScott.Carter@Sun.COM 	    req_p = list_next(&pool_p->ipool_req_list, req_p)) {
7168561SScott.Carter@Sun.COM 		if (DDI_IRM_IS_REDUCIBLE(req_p)) {
7178561SScott.Carter@Sun.COM 			pool_p->ipool_resno -= req_p->ireq_navail;
7188561SScott.Carter@Sun.COM 			req_p->ireq_scratch = req_p->ireq_navail;
7198561SScott.Carter@Sun.COM 			req_p->ireq_navail = req_p->ireq_nreq;
7208561SScott.Carter@Sun.COM 			pool_p->ipool_resno += req_p->ireq_navail;
7218561SScott.Carter@Sun.COM 			list_insert_tail(&pool_p->ipool_scratch_list, req_p);
7228561SScott.Carter@Sun.COM 		}
7238561SScott.Carter@Sun.COM 	}
7248561SScott.Carter@Sun.COM 
7258561SScott.Carter@Sun.COM 	/* Balance the requests */
7268561SScott.Carter@Sun.COM 	i_ddi_irm_reduce(pool_p);
7278561SScott.Carter@Sun.COM 
7288561SScott.Carter@Sun.COM 	/* Unlock the availability lock */
7298561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_navail_lock);
7308561SScott.Carter@Sun.COM 
7318561SScott.Carter@Sun.COM 	/*
7328561SScott.Carter@Sun.COM 	 * Process REMOVE notifications.
7338561SScott.Carter@Sun.COM 	 *
7348561SScott.Carter@Sun.COM 	 * If a driver fails to release interrupts: exclude it from
7358561SScott.Carter@Sun.COM 	 * further processing, correct the resulting imbalance, and
7368561SScott.Carter@Sun.COM 	 * start over again at the head of the scratch list.
7378561SScott.Carter@Sun.COM 	 */
7388561SScott.Carter@Sun.COM 	req_p = list_head(&pool_p->ipool_scratch_list);
7398561SScott.Carter@Sun.COM 	while (req_p) {
7408561SScott.Carter@Sun.COM 		if ((req_p->ireq_navail < req_p->ireq_scratch) &&
7418561SScott.Carter@Sun.COM 		    (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) {
7428561SScott.Carter@Sun.COM 			list_remove(&pool_p->ipool_scratch_list, req_p);
7438561SScott.Carter@Sun.COM 			mutex_enter(&pool_p->ipool_navail_lock);
7448561SScott.Carter@Sun.COM 			i_ddi_irm_reduce(pool_p);
7458561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_navail_lock);
7468561SScott.Carter@Sun.COM 			req_p = list_head(&pool_p->ipool_scratch_list);
7478561SScott.Carter@Sun.COM 		} else {
7488561SScott.Carter@Sun.COM 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
7498561SScott.Carter@Sun.COM 		}
7508561SScott.Carter@Sun.COM 	}
7518561SScott.Carter@Sun.COM 
7528561SScott.Carter@Sun.COM 	/*
7538561SScott.Carter@Sun.COM 	 * Process ADD notifications.
7548561SScott.Carter@Sun.COM 	 *
7558561SScott.Carter@Sun.COM 	 * This is the last use of the scratch list, so empty it.
7568561SScott.Carter@Sun.COM 	 */
7578561SScott.Carter@Sun.COM 	while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) {
7588561SScott.Carter@Sun.COM 		if (req_p->ireq_navail > req_p->ireq_scratch) {
7598561SScott.Carter@Sun.COM 			(void) i_ddi_irm_notify(pool_p, req_p);
7608561SScott.Carter@Sun.COM 		}
7618561SScott.Carter@Sun.COM 	}
7628561SScott.Carter@Sun.COM 
7638561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Restore size and policy settings */
7648561SScott.Carter@Sun.COM 	if (debug_totsz != 0)
7658561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = debug_totsz;
7668561SScott.Carter@Sun.COM 	if (debug_policy != 0)
7678561SScott.Carter@Sun.COM 		pool_p->ipool_policy = debug_policy;
7688561SScott.Carter@Sun.COM #endif	/* DEBUG */
7698561SScott.Carter@Sun.COM }
7708561SScott.Carter@Sun.COM 
7718561SScott.Carter@Sun.COM /*
7728561SScott.Carter@Sun.COM  * i_ddi_irm_reduce()
7738561SScott.Carter@Sun.COM  *
7748561SScott.Carter@Sun.COM  *	Use reduction algorithms to correct an imbalance in a pool.
7758561SScott.Carter@Sun.COM  */
7768561SScott.Carter@Sun.COM static void
7778561SScott.Carter@Sun.COM i_ddi_irm_reduce(ddi_irm_pool_t *pool_p)
7788561SScott.Carter@Sun.COM {
7798561SScott.Carter@Sun.COM 	int	ret, imbalance;
7808561SScott.Carter@Sun.COM 
7818561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
7828561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
7838561SScott.Carter@Sun.COM 	ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy));
7848561SScott.Carter@Sun.COM 
7858561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n",
7868561SScott.Carter@Sun.COM 	    (void *)pool_p));
7878561SScott.Carter@Sun.COM 
7888561SScott.Carter@Sun.COM 	/* Compute the imbalance.  Do nothing if already balanced. */
7898561SScott.Carter@Sun.COM 	if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0)
7908561SScott.Carter@Sun.COM 		return;
7918561SScott.Carter@Sun.COM 
7928561SScott.Carter@Sun.COM 	/* Reduce by policy */
7938561SScott.Carter@Sun.COM 	switch (pool_p->ipool_policy) {
7948561SScott.Carter@Sun.COM 	case DDI_IRM_POLICY_LARGE:
7958561SScott.Carter@Sun.COM 		ret = i_ddi_irm_reduce_large(pool_p, imbalance);
7968561SScott.Carter@Sun.COM 		break;
7978561SScott.Carter@Sun.COM 	case DDI_IRM_POLICY_EVEN:
7988561SScott.Carter@Sun.COM 		ret = i_ddi_irm_reduce_even(pool_p, imbalance);
7998561SScott.Carter@Sun.COM 		break;
8008561SScott.Carter@Sun.COM 	}
8018561SScott.Carter@Sun.COM 
8028561SScott.Carter@Sun.COM 	/*
8038561SScott.Carter@Sun.COM 	 * If the policy based reductions failed, then
8048561SScott.Carter@Sun.COM 	 * possibly reduce new requests as a last resort.
8058561SScott.Carter@Sun.COM 	 */
8068561SScott.Carter@Sun.COM 	if (ret != DDI_SUCCESS) {
8078561SScott.Carter@Sun.COM 
8088561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
8098561SScott.Carter@Sun.COM 		    "i_ddi_irm_reduce: policy reductions failed.\n"));
8108561SScott.Carter@Sun.COM 
8118561SScott.Carter@Sun.COM 		/* Compute remaining imbalance */
8128561SScott.Carter@Sun.COM 		imbalance = pool_p->ipool_resno - pool_p->ipool_totsz;
8138561SScott.Carter@Sun.COM 
8148561SScott.Carter@Sun.COM 		ASSERT(imbalance > 0);
8158561SScott.Carter@Sun.COM 
8168561SScott.Carter@Sun.COM 		i_ddi_irm_reduce_new(pool_p, imbalance);
8178561SScott.Carter@Sun.COM 	}
8188561SScott.Carter@Sun.COM }
8198561SScott.Carter@Sun.COM 
8208561SScott.Carter@Sun.COM /*
8218561SScott.Carter@Sun.COM  * i_ddi_irm_enqueue()
8228561SScott.Carter@Sun.COM  *
8238561SScott.Carter@Sun.COM  *	Queue a pool to be balanced.  Signals the balancing thread to wake
8248561SScott.Carter@Sun.COM  *	up and process the pool.  If 'wait_flag' is true, then the current
8258561SScott.Carter@Sun.COM  *	thread becomes a waiter and blocks until the balance is completed.
8268561SScott.Carter@Sun.COM  */
8278561SScott.Carter@Sun.COM static void
8288561SScott.Carter@Sun.COM i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag)
8298561SScott.Carter@Sun.COM {
8308561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
8318561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
8328561SScott.Carter@Sun.COM 
8338561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n",
8348561SScott.Carter@Sun.COM 	    (void *)pool_p, (int)wait_flag));
8358561SScott.Carter@Sun.COM 
8368561SScott.Carter@Sun.COM 	/* Do nothing if pool is already balanced */
8378561SScott.Carter@Sun.COM #ifndef	DEBUG
8388561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
8398561SScott.Carter@Sun.COM #else
8408561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
8418561SScott.Carter@Sun.COM #endif	/* DEBUG */
8428561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
8438561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool already balanced\n"));
8448561SScott.Carter@Sun.COM 		return;
8458561SScott.Carter@Sun.COM 	}
8468561SScott.Carter@Sun.COM 
8478561SScott.Carter@Sun.COM 	/* Avoid deadlocks when IRM is not active */
8488561SScott.Carter@Sun.COM 	if (!irm_active && wait_flag) {
8498561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
8508561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool not active.\n"));
8518561SScott.Carter@Sun.COM 		return;
8528561SScott.Carter@Sun.COM 	}
8538561SScott.Carter@Sun.COM 
8548561SScott.Carter@Sun.COM 	if (wait_flag)
8558561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS;
8568561SScott.Carter@Sun.COM 
8578561SScott.Carter@Sun.COM 	if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) {
8588561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED;
8598561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
8608561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n"));
8618561SScott.Carter@Sun.COM 	}
8628561SScott.Carter@Sun.COM 
8638561SScott.Carter@Sun.COM 	if (wait_flag) {
8648561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n"));
8658561SScott.Carter@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
8668561SScott.Carter@Sun.COM 	}
8678561SScott.Carter@Sun.COM }
8688561SScott.Carter@Sun.COM 
8698561SScott.Carter@Sun.COM /*
8708561SScott.Carter@Sun.COM  * Reduction Algorithms, Used For Balancing
8718561SScott.Carter@Sun.COM  */
8728561SScott.Carter@Sun.COM 
8738561SScott.Carter@Sun.COM /*
8748561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_large()
8758561SScott.Carter@Sun.COM  *
8768561SScott.Carter@Sun.COM  *	Algorithm for the DDI_IRM_POLICY_LARGE reduction policy.
8778561SScott.Carter@Sun.COM  *
8788561SScott.Carter@Sun.COM  *	This algorithm generally reduces larger requests first, before
8798561SScott.Carter@Sun.COM  *	advancing to smaller requests.  The scratch list is initially
8808561SScott.Carter@Sun.COM  *	sorted in descending order by current navail values, which are
8818561SScott.Carter@Sun.COM  *	maximized prior to reduction.  This sorted order is preserved,
8828561SScott.Carter@Sun.COM  *	but within a range of equally sized requests they are secondarily
8838561SScott.Carter@Sun.COM  *	sorted in ascending order by initial nreq value.  The head of the
8848561SScott.Carter@Sun.COM  *	list is always selected for reduction, since it is the current
8858561SScott.Carter@Sun.COM  *	largest request.  After being reduced, it is sorted further into
8868561SScott.Carter@Sun.COM  *	the list before the next iteration.
8878561SScott.Carter@Sun.COM  *
8888561SScott.Carter@Sun.COM  *	Optimizations in this algorithm include trying to reduce multiple
8898561SScott.Carter@Sun.COM  *	requests together if they are equally sized.  And the algorithm
8908561SScott.Carter@Sun.COM  *	attempts to reduce in larger increments when possible to minimize
8918561SScott.Carter@Sun.COM  *	the total number of iterations.
8928561SScott.Carter@Sun.COM  */
8938561SScott.Carter@Sun.COM static int
8948561SScott.Carter@Sun.COM i_ddi_irm_reduce_large(ddi_irm_pool_t *pool_p, int imbalance)
8958561SScott.Carter@Sun.COM {
8968925SEvan.Yan@Sun.COM 	ddi_irm_req_t	*head_p, *next_p;
8978925SEvan.Yan@Sun.COM 	int		next_navail, nreqs, reduction;
8988561SScott.Carter@Sun.COM 
8998561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
9008561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
9018561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
9028561SScott.Carter@Sun.COM 
9038561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT,
9048561SScott.Carter@Sun.COM 	    "i_ddi_irm_reduce_large: pool_p %p imbalance %d\n", (void *)pool_p,
9058561SScott.Carter@Sun.COM 	    imbalance));
9068561SScott.Carter@Sun.COM 
9078561SScott.Carter@Sun.COM 	while (imbalance > 0) {
9088561SScott.Carter@Sun.COM 
9098925SEvan.Yan@Sun.COM 		head_p = list_head(&pool_p->ipool_scratch_list);
9108561SScott.Carter@Sun.COM 
9118561SScott.Carter@Sun.COM 		/* Fail if nothing is reducible */
9128925SEvan.Yan@Sun.COM 		if (head_p->ireq_navail <= pool_p->ipool_defsz) {
9138561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
9148925SEvan.Yan@Sun.COM 			    "i_ddi_irm_reduce_large: Failure. "
9158925SEvan.Yan@Sun.COM 			    "All requests have downsized to low limit.\n"));
9168561SScott.Carter@Sun.COM 			return (DDI_FAILURE);
9178561SScott.Carter@Sun.COM 		}
9188561SScott.Carter@Sun.COM 
9198561SScott.Carter@Sun.COM 		/* Count the number of equally sized requests */
9208925SEvan.Yan@Sun.COM 		for (nreqs = 1, next_p = head_p;
9218925SEvan.Yan@Sun.COM 		    (next_p = list_next(&pool_p->ipool_scratch_list, next_p)) !=
9228925SEvan.Yan@Sun.COM 		    NULL && (head_p->ireq_navail == next_p->ireq_navail);
9238925SEvan.Yan@Sun.COM 		    nreqs++)
9248925SEvan.Yan@Sun.COM 			;
9258561SScott.Carter@Sun.COM 
9268925SEvan.Yan@Sun.COM 		next_navail = next_p ? next_p->ireq_navail : 0;
9278925SEvan.Yan@Sun.COM 		reduction = head_p->ireq_navail -
9288925SEvan.Yan@Sun.COM 		    MAX(next_navail, pool_p->ipool_defsz);
9298561SScott.Carter@Sun.COM 
9308925SEvan.Yan@Sun.COM 		if ((reduction * nreqs) > imbalance) {
9318925SEvan.Yan@Sun.COM 			reduction = imbalance / nreqs;
9328925SEvan.Yan@Sun.COM 
9338925SEvan.Yan@Sun.COM 			if (reduction == 0) {
9348925SEvan.Yan@Sun.COM 				reduction = 1;
9358925SEvan.Yan@Sun.COM 				nreqs = imbalance;
9368561SScott.Carter@Sun.COM 			}
9378561SScott.Carter@Sun.COM 		}
9388561SScott.Carter@Sun.COM 
9398925SEvan.Yan@Sun.COM 		next_p = head_p;
9408925SEvan.Yan@Sun.COM 		while (nreqs--) {
9418925SEvan.Yan@Sun.COM 			imbalance -= reduction;
9428925SEvan.Yan@Sun.COM 			next_p->ireq_navail -= reduction;
9438925SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= reduction;
9448925SEvan.Yan@Sun.COM 			next_p = list_next(&pool_p->ipool_scratch_list, next_p);
9458561SScott.Carter@Sun.COM 		}
9468561SScott.Carter@Sun.COM 
9478925SEvan.Yan@Sun.COM 		if (next_p && next_p->ireq_navail > head_p->ireq_navail) {
9488925SEvan.Yan@Sun.COM 			ASSERT(imbalance == 0);
9498561SScott.Carter@Sun.COM 			i_ddi_irm_reduce_large_resort(pool_p);
9508561SScott.Carter@Sun.COM 		}
9518561SScott.Carter@Sun.COM 	}
9528561SScott.Carter@Sun.COM 
9538561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
9548561SScott.Carter@Sun.COM }
9558561SScott.Carter@Sun.COM 
9568561SScott.Carter@Sun.COM /*
9578561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_large_resort()
9588561SScott.Carter@Sun.COM  *
9598561SScott.Carter@Sun.COM  *	Helper function for i_ddi_irm_reduce_large().  Once a request
9608561SScott.Carter@Sun.COM  *	is reduced, this resorts it further down into the list as necessary.
9618561SScott.Carter@Sun.COM  */
9628561SScott.Carter@Sun.COM static void
9638561SScott.Carter@Sun.COM i_ddi_irm_reduce_large_resort(ddi_irm_pool_t *pool_p)
9648561SScott.Carter@Sun.COM {
9658925SEvan.Yan@Sun.COM 	ddi_irm_req_t	*start_p, *end_p, *next_p;
9668561SScott.Carter@Sun.COM 
9678561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
9688561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
9698561SScott.Carter@Sun.COM 
9708925SEvan.Yan@Sun.COM 	start_p = list_head(&pool_p->ipool_scratch_list);
9718925SEvan.Yan@Sun.COM 	end_p = list_next(&pool_p->ipool_scratch_list, start_p);
9728925SEvan.Yan@Sun.COM 	while (end_p && start_p->ireq_navail == end_p->ireq_navail)
9738925SEvan.Yan@Sun.COM 		end_p = list_next(&pool_p->ipool_scratch_list, end_p);
9748561SScott.Carter@Sun.COM 
9758925SEvan.Yan@Sun.COM 	next_p = end_p;
9768925SEvan.Yan@Sun.COM 	while (next_p && (next_p->ireq_navail > start_p->ireq_navail))
9778561SScott.Carter@Sun.COM 		next_p = list_next(&pool_p->ipool_scratch_list, next_p);
9788561SScott.Carter@Sun.COM 
9798925SEvan.Yan@Sun.COM 	while (start_p != end_p) {
9808925SEvan.Yan@Sun.COM 		list_remove(&pool_p->ipool_scratch_list, start_p);
9818925SEvan.Yan@Sun.COM 		list_insert_before(&pool_p->ipool_scratch_list, next_p,
9828925SEvan.Yan@Sun.COM 		    start_p);
9838925SEvan.Yan@Sun.COM 		start_p = list_head(&pool_p->ipool_scratch_list);
9848925SEvan.Yan@Sun.COM 	}
9858561SScott.Carter@Sun.COM }
9868561SScott.Carter@Sun.COM 
9878561SScott.Carter@Sun.COM /*
9888561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_even()
9898561SScott.Carter@Sun.COM  *
9908561SScott.Carter@Sun.COM  *	Algorithm for the DDI_IRM_POLICY_EVEN reduction policy.
9918561SScott.Carter@Sun.COM  *
9928561SScott.Carter@Sun.COM  *	This algorithm reduces requests evenly, without giving a
9938561SScott.Carter@Sun.COM  *	specific preference to smaller or larger requests.  Each
9948561SScott.Carter@Sun.COM  *	iteration reduces all reducible requests by the same amount
9958561SScott.Carter@Sun.COM  *	until the imbalance is corrected.  Although when possible,
9968561SScott.Carter@Sun.COM  *	it tries to avoid reducing requests below the threshold of
9978561SScott.Carter@Sun.COM  *	the interrupt pool's default allocation size.
9988561SScott.Carter@Sun.COM  *
9998561SScott.Carter@Sun.COM  *	An optimization in this algorithm is to reduce the requests
10008561SScott.Carter@Sun.COM  *	in larger increments during each iteration, to minimize the
10018561SScott.Carter@Sun.COM  *	total number of iterations required.
10028561SScott.Carter@Sun.COM  */
10038561SScott.Carter@Sun.COM static int
10048561SScott.Carter@Sun.COM i_ddi_irm_reduce_even(ddi_irm_pool_t *pool_p, int imbalance)
10058561SScott.Carter@Sun.COM {
10068561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p, *last_p;
10078561SScott.Carter@Sun.COM 	uint_t		nmin = pool_p->ipool_defsz;
10088561SScott.Carter@Sun.COM 	uint_t		nreduce, reduction;
10098561SScott.Carter@Sun.COM 
10108561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
10118561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
10128561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10138561SScott.Carter@Sun.COM 
10148561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT,
10158561SScott.Carter@Sun.COM 	    "i_ddi_irm_reduce_even: pool_p %p imbalance %d\n",
10168561SScott.Carter@Sun.COM 	    (void *)pool_p, imbalance));
10178561SScott.Carter@Sun.COM 
10188925SEvan.Yan@Sun.COM 	while (imbalance > 0) {
10198561SScott.Carter@Sun.COM 
10208561SScott.Carter@Sun.COM 		/* Count reducible requests */
10218561SScott.Carter@Sun.COM 		nreduce = 0;
10228561SScott.Carter@Sun.COM 		for (req_p = list_head(&pool_p->ipool_scratch_list); req_p;
10238561SScott.Carter@Sun.COM 		    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
10248561SScott.Carter@Sun.COM 			if (req_p->ireq_navail <= nmin)
10258561SScott.Carter@Sun.COM 				break;
10268561SScott.Carter@Sun.COM 			last_p = req_p;
10278561SScott.Carter@Sun.COM 			nreduce++;
10288561SScott.Carter@Sun.COM 		}
10298561SScott.Carter@Sun.COM 
10308925SEvan.Yan@Sun.COM 		/* Fail if none are reducible */
10318561SScott.Carter@Sun.COM 		if (nreduce == 0) {
10328925SEvan.Yan@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
10338925SEvan.Yan@Sun.COM 			    "i_ddi_irm_reduce_even: Failure. "
10348925SEvan.Yan@Sun.COM 			    "All requests have downsized to low limit.\n"));
10358925SEvan.Yan@Sun.COM 			return (DDI_FAILURE);
10368561SScott.Carter@Sun.COM 		}
10378561SScott.Carter@Sun.COM 
10388561SScott.Carter@Sun.COM 		/* Compute reduction */
10398561SScott.Carter@Sun.COM 		if (nreduce < imbalance) {
10408561SScott.Carter@Sun.COM 			reduction = last_p->ireq_navail - nmin;
10418561SScott.Carter@Sun.COM 			if ((reduction * nreduce) > imbalance) {
10428561SScott.Carter@Sun.COM 				reduction = imbalance / nreduce;
10438561SScott.Carter@Sun.COM 			}
10448561SScott.Carter@Sun.COM 		} else {
10458561SScott.Carter@Sun.COM 			reduction = 1;
10468561SScott.Carter@Sun.COM 		}
10478561SScott.Carter@Sun.COM 
10488561SScott.Carter@Sun.COM 		/* Start at head of list, but skip excess */
10498561SScott.Carter@Sun.COM 		req_p = list_head(&pool_p->ipool_scratch_list);
10508561SScott.Carter@Sun.COM 		while (nreduce > imbalance) {
10518561SScott.Carter@Sun.COM 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
10528561SScott.Carter@Sun.COM 			nreduce--;
10538561SScott.Carter@Sun.COM 		}
10548561SScott.Carter@Sun.COM 
10558561SScott.Carter@Sun.COM 		/* Do reductions */
10568561SScott.Carter@Sun.COM 		while (req_p && (nreduce > 0)) {
10578561SScott.Carter@Sun.COM 			imbalance -= reduction;
10588561SScott.Carter@Sun.COM 			req_p->ireq_navail -= reduction;
10598561SScott.Carter@Sun.COM 			pool_p->ipool_resno -= reduction;
10608561SScott.Carter@Sun.COM 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
10618561SScott.Carter@Sun.COM 			nreduce--;
10628561SScott.Carter@Sun.COM 		}
10638561SScott.Carter@Sun.COM 	}
10648561SScott.Carter@Sun.COM 
10658561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
10668561SScott.Carter@Sun.COM }
10678561SScott.Carter@Sun.COM 
10688561SScott.Carter@Sun.COM /*
10698561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_new()
10708561SScott.Carter@Sun.COM  *
10718925SEvan.Yan@Sun.COM  *	Reduces new requests.  This is only used as a last resort
10728925SEvan.Yan@Sun.COM  *	after another reduction algorithm failed.
10738561SScott.Carter@Sun.COM  */
10748561SScott.Carter@Sun.COM static void
10758561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance)
10768561SScott.Carter@Sun.COM {
10778561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
10788925SEvan.Yan@Sun.COM 	uint_t		nreduce;
10798561SScott.Carter@Sun.COM 
10808561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
10818561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
10828561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10838561SScott.Carter@Sun.COM 
10848925SEvan.Yan@Sun.COM 	while (imbalance > 0) {
10858925SEvan.Yan@Sun.COM 		nreduce = 0;
10868925SEvan.Yan@Sun.COM 		for (req_p = list_head(&pool_p->ipool_scratch_list);
10878925SEvan.Yan@Sun.COM 		    req_p && (imbalance > 0);
10888925SEvan.Yan@Sun.COM 		    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
10898925SEvan.Yan@Sun.COM 			if (req_p->ireq_flags & DDI_IRM_FLAG_NEW &&
10908925SEvan.Yan@Sun.COM 			    req_p->ireq_navail > 1) {
10918925SEvan.Yan@Sun.COM 				req_p->ireq_navail--;
10928925SEvan.Yan@Sun.COM 				pool_p->ipool_resno--;
10938925SEvan.Yan@Sun.COM 				imbalance--;
10948925SEvan.Yan@Sun.COM 				nreduce++;
10958925SEvan.Yan@Sun.COM 			}
10968925SEvan.Yan@Sun.COM 		}
10978925SEvan.Yan@Sun.COM 
10988925SEvan.Yan@Sun.COM 		if (nreduce == 0)
10998925SEvan.Yan@Sun.COM 			break;
11008925SEvan.Yan@Sun.COM 	}
11018925SEvan.Yan@Sun.COM 
11028561SScott.Carter@Sun.COM 	for (req_p = list_head(&pool_p->ipool_scratch_list);
11038561SScott.Carter@Sun.COM 	    req_p && (imbalance > 0);
11048561SScott.Carter@Sun.COM 	    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
11058561SScott.Carter@Sun.COM 		if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) {
11068925SEvan.Yan@Sun.COM 			ASSERT(req_p->ireq_navail == 1);
11078561SScott.Carter@Sun.COM 			req_p->ireq_navail--;
11088561SScott.Carter@Sun.COM 			pool_p->ipool_resno--;
11098561SScott.Carter@Sun.COM 			imbalance--;
11108561SScott.Carter@Sun.COM 		}
11118561SScott.Carter@Sun.COM 	}
11128561SScott.Carter@Sun.COM }
11138561SScott.Carter@Sun.COM 
11148561SScott.Carter@Sun.COM /*
11158561SScott.Carter@Sun.COM  * Miscellaneous Helper Functions
11168561SScott.Carter@Sun.COM  */
11178561SScott.Carter@Sun.COM 
11188561SScott.Carter@Sun.COM /*
11198561SScott.Carter@Sun.COM  * i_ddi_intr_get_pool()
11208561SScott.Carter@Sun.COM  *
11218561SScott.Carter@Sun.COM  *	Get an IRM pool that supplies interrupts of a specified type.
11228561SScott.Carter@Sun.COM  *	Invokes a DDI_INTROP_GETPOOL to the bus nexus driver.  Fails
11238561SScott.Carter@Sun.COM  *	if no pool exists.
11248561SScott.Carter@Sun.COM  */
11258561SScott.Carter@Sun.COM ddi_irm_pool_t *
11268561SScott.Carter@Sun.COM i_ddi_intr_get_pool(dev_info_t *dip, int type)
11278561SScott.Carter@Sun.COM {
11288561SScott.Carter@Sun.COM 	devinfo_intr_t		*intr_p;
11298561SScott.Carter@Sun.COM 	ddi_irm_pool_t		*pool_p;
11308561SScott.Carter@Sun.COM 	ddi_irm_req_t		*req_p;
11318561SScott.Carter@Sun.COM 	ddi_intr_handle_impl_t	hdl;
11328561SScott.Carter@Sun.COM 
11338561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
11348561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
11358561SScott.Carter@Sun.COM 
11368561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
11378561SScott.Carter@Sun.COM 	    ((req_p = intr_p->devi_irm_req_p) != NULL) &&
11388561SScott.Carter@Sun.COM 	    ((pool_p = req_p->ireq_pool_p) != NULL) &&
11398561SScott.Carter@Sun.COM 	    (pool_p->ipool_types & type)) {
11408561SScott.Carter@Sun.COM 		return (pool_p);
11418561SScott.Carter@Sun.COM 	}
11428561SScott.Carter@Sun.COM 
11438561SScott.Carter@Sun.COM 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
11448561SScott.Carter@Sun.COM 	hdl.ih_dip = dip;
11458561SScott.Carter@Sun.COM 	hdl.ih_type = type;
11468561SScott.Carter@Sun.COM 
11478561SScott.Carter@Sun.COM 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL,
11488561SScott.Carter@Sun.COM 	    &hdl, (void *)&pool_p) == DDI_SUCCESS)
11498561SScott.Carter@Sun.COM 		return (pool_p);
11508561SScott.Carter@Sun.COM 
11518561SScott.Carter@Sun.COM 	return (NULL);
11528561SScott.Carter@Sun.COM }
11538561SScott.Carter@Sun.COM 
11548561SScott.Carter@Sun.COM /*
11558561SScott.Carter@Sun.COM  * i_ddi_irm_insertion_sort()
11568561SScott.Carter@Sun.COM  *
11578561SScott.Carter@Sun.COM  *	Use the insertion sort method to insert a request into a list.
11588561SScott.Carter@Sun.COM  *	The list is sorted in descending order by request size.
11598561SScott.Carter@Sun.COM  */
11608561SScott.Carter@Sun.COM static void
11618561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p)
11628561SScott.Carter@Sun.COM {
11638561SScott.Carter@Sun.COM 	ddi_irm_req_t	*next_p;
11648561SScott.Carter@Sun.COM 
11658561SScott.Carter@Sun.COM 	next_p = list_head(req_list);
11668561SScott.Carter@Sun.COM 
11678561SScott.Carter@Sun.COM 	while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq))
11688561SScott.Carter@Sun.COM 		next_p = list_next(req_list, next_p);
11698561SScott.Carter@Sun.COM 
11708561SScott.Carter@Sun.COM 	list_insert_before(req_list, next_p, req_p);
11718561SScott.Carter@Sun.COM }
11728561SScott.Carter@Sun.COM 
11738561SScott.Carter@Sun.COM /*
11748561SScott.Carter@Sun.COM  * i_ddi_irm_notify()
11758561SScott.Carter@Sun.COM  *
11768561SScott.Carter@Sun.COM  *	Notify a driver of changes to its interrupt request using the
11778561SScott.Carter@Sun.COM  *	generic callback mechanism.  Checks for errors in processing.
11788561SScott.Carter@Sun.COM  */
11798561SScott.Carter@Sun.COM static int
11808561SScott.Carter@Sun.COM i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
11818561SScott.Carter@Sun.COM {
11828561SScott.Carter@Sun.COM 	ddi_cb_action_t	action;
11838561SScott.Carter@Sun.COM 	ddi_cb_t	*cb_p;
11848561SScott.Carter@Sun.COM 	uint_t		nintrs;
11858561SScott.Carter@Sun.COM 	int		ret, count;
11868561SScott.Carter@Sun.COM 
11878561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n",
11888561SScott.Carter@Sun.COM 	    (void *)pool_p, (void *)req_p));
11898561SScott.Carter@Sun.COM 
11908561SScott.Carter@Sun.COM 	/* Do not notify new or unchanged requests */
11918561SScott.Carter@Sun.COM 	if ((req_p->ireq_navail == req_p->ireq_scratch) ||
11928561SScott.Carter@Sun.COM 	    (req_p->ireq_flags & DDI_IRM_FLAG_NEW))
11938561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
11948561SScott.Carter@Sun.COM 
11958561SScott.Carter@Sun.COM 	/* Determine action and count */
11968561SScott.Carter@Sun.COM 	if (req_p->ireq_navail > req_p->ireq_scratch) {
11978561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_ADD;
11988561SScott.Carter@Sun.COM 		count = req_p->ireq_navail - req_p->ireq_scratch;
11998561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n",
12008561SScott.Carter@Sun.COM 		    count));
12018561SScott.Carter@Sun.COM 	} else {
12028561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_REMOVE;
12038561SScott.Carter@Sun.COM 		count = req_p->ireq_scratch - req_p->ireq_navail;
12048561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n",
12058561SScott.Carter@Sun.COM 		    count));
12068561SScott.Carter@Sun.COM 	}
12078561SScott.Carter@Sun.COM 
12088561SScott.Carter@Sun.COM 	/* Lookup driver callback */
12098561SScott.Carter@Sun.COM 	if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) {
12108561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n"));
12118561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
12128561SScott.Carter@Sun.COM 	}
12138561SScott.Carter@Sun.COM 
12148561SScott.Carter@Sun.COM 	/* Do callback */
12158561SScott.Carter@Sun.COM 	ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count,
12168561SScott.Carter@Sun.COM 	    cb_p->cb_arg1, cb_p->cb_arg2);
12178561SScott.Carter@Sun.COM 
12188561SScott.Carter@Sun.COM 	/* Log callback errors */
12198561SScott.Carter@Sun.COM 	if (ret != DDI_SUCCESS) {
12208561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
12218561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
12228561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), (int)action, ret);
12238561SScott.Carter@Sun.COM 	}
12248561SScott.Carter@Sun.COM 
12258561SScott.Carter@Sun.COM 	/* Check if the driver exceeds its availability */
12268561SScott.Carter@Sun.COM 	nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip);
12278561SScott.Carter@Sun.COM 	if (nintrs > req_p->ireq_navail) {
12288561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed to release interrupts "
12298561SScott.Carter@Sun.COM 		    "(nintrs=%d, navail=%d).\n",
12308561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
12318561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), nintrs,
12328561SScott.Carter@Sun.COM 		    req_p->ireq_navail);
12338561SScott.Carter@Sun.COM 		pool_p->ipool_resno += (nintrs - req_p->ireq_navail);
12348561SScott.Carter@Sun.COM 		req_p->ireq_navail = nintrs;
12358561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
12368561SScott.Carter@Sun.COM 	}
12378561SScott.Carter@Sun.COM 
12388561SScott.Carter@Sun.COM 	/* Update request */
12398561SScott.Carter@Sun.COM 	req_p->ireq_scratch = req_p->ireq_navail;
12408561SScott.Carter@Sun.COM 
12418561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
12428561SScott.Carter@Sun.COM }
12438561SScott.Carter@Sun.COM 
12448561SScott.Carter@Sun.COM /*
12458561SScott.Carter@Sun.COM  * i_ddi_irm_debug_balance()
12468561SScott.Carter@Sun.COM  *
12478561SScott.Carter@Sun.COM  *	A debug/test only routine to force the immediate,
12488561SScott.Carter@Sun.COM  *	synchronous rebalancing of an interrupt pool.
12498561SScott.Carter@Sun.COM  */
12508561SScott.Carter@Sun.COM #ifdef	DEBUG
12518561SScott.Carter@Sun.COM void
12528561SScott.Carter@Sun.COM i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag)
12538561SScott.Carter@Sun.COM {
12548561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
12558561SScott.Carter@Sun.COM 	int		type;
12568561SScott.Carter@Sun.COM 
12578561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n",
12588561SScott.Carter@Sun.COM 	    (void *)dip, (int)wait_flag));
12598561SScott.Carter@Sun.COM 
12608561SScott.Carter@Sun.COM 	if (((type = i_ddi_intr_get_current_type(dip)) != 0) &&
12618561SScott.Carter@Sun.COM 	    ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) {
12628561SScott.Carter@Sun.COM 		mutex_enter(&pool_p->ipool_lock);
12638561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, wait_flag);
12648561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
12658561SScott.Carter@Sun.COM 	}
12668561SScott.Carter@Sun.COM }
12678561SScott.Carter@Sun.COM #endif
1268