xref: /illumos-gate/usr/src/uts/common/os/ddi_intr_irm.c (revision 86ef0a63e1cfa5dc98606efef379365acca98063)
15febcb4aSScott Carter, SD IOSW /*
25febcb4aSScott Carter, SD IOSW  * CDDL HEADER START
35febcb4aSScott Carter, SD IOSW  *
45febcb4aSScott Carter, SD IOSW  * The contents of this file are subject to the terms of the
55febcb4aSScott Carter, SD IOSW  * Common Development and Distribution License (the "License").
65febcb4aSScott Carter, SD IOSW  * You may not use this file except in compliance with the License.
75febcb4aSScott Carter, SD IOSW  *
85febcb4aSScott Carter, SD IOSW  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95febcb4aSScott Carter, SD IOSW  * or http://www.opensolaris.org/os/licensing.
105febcb4aSScott Carter, SD IOSW  * See the License for the specific language governing permissions
115febcb4aSScott Carter, SD IOSW  * and limitations under the License.
125febcb4aSScott Carter, SD IOSW  *
135febcb4aSScott Carter, SD IOSW  * When distributing Covered Code, include this CDDL HEADER in each
145febcb4aSScott Carter, SD IOSW  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155febcb4aSScott Carter, SD IOSW  * If applicable, add the following below this CDDL HEADER, with the
165febcb4aSScott Carter, SD IOSW  * fields enclosed by brackets "[]" replaced with your own identifying
175febcb4aSScott Carter, SD IOSW  * information: Portions Copyright [yyyy] [name of copyright owner]
185febcb4aSScott Carter, SD IOSW  *
195febcb4aSScott Carter, SD IOSW  * CDDL HEADER END
205febcb4aSScott Carter, SD IOSW  */
215febcb4aSScott Carter, SD IOSW /*
22a120541cSScott M. Carter  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
235febcb4aSScott Carter, SD IOSW  */
245febcb4aSScott Carter, SD IOSW 
255febcb4aSScott Carter, SD IOSW #include <sys/note.h>
265febcb4aSScott Carter, SD IOSW #include <sys/sysmacros.h>
275febcb4aSScott Carter, SD IOSW #include <sys/types.h>
285febcb4aSScott Carter, SD IOSW #include <sys/param.h>
295febcb4aSScott Carter, SD IOSW #include <sys/systm.h>
305febcb4aSScott Carter, SD IOSW #include <sys/kmem.h>
315febcb4aSScott Carter, SD IOSW #include <sys/cmn_err.h>
325febcb4aSScott Carter, SD IOSW #include <sys/debug.h>
335febcb4aSScott Carter, SD IOSW #include <sys/ddi.h>
345febcb4aSScott Carter, SD IOSW #include <sys/sunndi.h>
355febcb4aSScott Carter, SD IOSW #include <sys/ndi_impldefs.h>	/* include prototypes */
365febcb4aSScott Carter, SD IOSW 
37*86ef0a63SRichard Lowe #if defined(__x86)
387ff178cdSJimmy Vetayases /*
397ff178cdSJimmy Vetayases  * MSI-X allocation limit.
407ff178cdSJimmy Vetayases  */
417ff178cdSJimmy Vetayases extern uint_t		ddi_msix_alloc_limit;
427ff178cdSJimmy Vetayases #endif
437ff178cdSJimmy Vetayases 
445febcb4aSScott Carter, SD IOSW /*
455febcb4aSScott Carter, SD IOSW  * Interrupt Resource Management (IRM).
465febcb4aSScott Carter, SD IOSW  */
475febcb4aSScott Carter, SD IOSW 
485febcb4aSScott Carter, SD IOSW #define	DDI_IRM_BALANCE_DELAY	(60)	/* In seconds */
495febcb4aSScott Carter, SD IOSW 
505febcb4aSScott Carter, SD IOSW #define	DDI_IRM_HAS_CB(c)	((c) && (c->cb_flags & DDI_CB_FLAG_INTR))
515febcb4aSScott Carter, SD IOSW 
525febcb4aSScott Carter, SD IOSW #define	DDI_IRM_IS_REDUCIBLE(r)	(((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \
535febcb4aSScott Carter, SD IOSW 				(r->ireq_type == DDI_INTR_TYPE_MSIX)) || \
545febcb4aSScott Carter, SD IOSW 				(r->ireq_flags & DDI_IRM_FLAG_NEW))
555febcb4aSScott Carter, SD IOSW 
565febcb4aSScott Carter, SD IOSW extern pri_t	minclsyspri;
575febcb4aSScott Carter, SD IOSW 
585febcb4aSScott Carter, SD IOSW /* Global policies */
595febcb4aSScott Carter, SD IOSW int		irm_enable = 1;
605febcb4aSScott Carter, SD IOSW boolean_t	irm_active = B_FALSE;
615febcb4aSScott Carter, SD IOSW int		irm_default_policy = DDI_IRM_POLICY_LARGE;
625febcb4aSScott Carter, SD IOSW uint_t		irm_balance_delay = DDI_IRM_BALANCE_DELAY;
635febcb4aSScott Carter, SD IOSW 
645febcb4aSScott Carter, SD IOSW /* Global list of interrupt pools */
655febcb4aSScott Carter, SD IOSW kmutex_t	irm_pools_lock;
665febcb4aSScott Carter, SD IOSW list_t		irm_pools_list;
675febcb4aSScott Carter, SD IOSW 
685febcb4aSScott Carter, SD IOSW /* Global debug tunables */
695febcb4aSScott Carter, SD IOSW #ifdef	DEBUG
705febcb4aSScott Carter, SD IOSW int		irm_debug_policy = 0;
715febcb4aSScott Carter, SD IOSW uint_t		irm_debug_size = 0;
725febcb4aSScott Carter, SD IOSW #endif	/* DEBUG */
735febcb4aSScott Carter, SD IOSW 
745febcb4aSScott Carter, SD IOSW static void	irm_balance_thread(ddi_irm_pool_t *);
755febcb4aSScott Carter, SD IOSW static void	i_ddi_irm_balance(ddi_irm_pool_t *);
765febcb4aSScott Carter, SD IOSW static void	i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t);
775febcb4aSScott Carter, SD IOSW static void	i_ddi_irm_reduce(ddi_irm_pool_t *pool);
786a43bfaaSEvan Yan static int	i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *, int, int);
795febcb4aSScott Carter, SD IOSW static void	i_ddi_irm_reduce_new(ddi_irm_pool_t *, int);
805febcb4aSScott Carter, SD IOSW static void	i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *);
815febcb4aSScott Carter, SD IOSW static int	i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *);
82a120541cSScott M. Carter static int	i_ddi_irm_modify_increase(ddi_irm_req_t *, int);
835febcb4aSScott Carter, SD IOSW 
845febcb4aSScott Carter, SD IOSW /*
855febcb4aSScott Carter, SD IOSW  * OS Initialization Routines
865febcb4aSScott Carter, SD IOSW  */
875febcb4aSScott Carter, SD IOSW 
885febcb4aSScott Carter, SD IOSW /*
895febcb4aSScott Carter, SD IOSW  * irm_init()
905febcb4aSScott Carter, SD IOSW  *
915febcb4aSScott Carter, SD IOSW  *	Initialize IRM subsystem before any drivers are attached.
925febcb4aSScott Carter, SD IOSW  */
935febcb4aSScott Carter, SD IOSW void
irm_init(void)945febcb4aSScott Carter, SD IOSW irm_init(void)
955febcb4aSScott Carter, SD IOSW {
965febcb4aSScott Carter, SD IOSW 	/* Do nothing if IRM is disabled */
975febcb4aSScott Carter, SD IOSW 	if (!irm_enable)
985febcb4aSScott Carter, SD IOSW 		return;
995febcb4aSScott Carter, SD IOSW 
1005febcb4aSScott Carter, SD IOSW 	/* Verify that the default balancing policy is valid */
1015febcb4aSScott Carter, SD IOSW 	if (!DDI_IRM_POLICY_VALID(irm_default_policy))
1025febcb4aSScott Carter, SD IOSW 		irm_default_policy = DDI_IRM_POLICY_LARGE;
1035febcb4aSScott Carter, SD IOSW 
1045febcb4aSScott Carter, SD IOSW 	/* Initialize the global list of interrupt pools */
1055febcb4aSScott Carter, SD IOSW 	mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL);
1065febcb4aSScott Carter, SD IOSW 	list_create(&irm_pools_list, sizeof (ddi_irm_pool_t),
1075febcb4aSScott Carter, SD IOSW 	    offsetof(ddi_irm_pool_t, ipool_link));
1085febcb4aSScott Carter, SD IOSW }
1095febcb4aSScott Carter, SD IOSW 
1105febcb4aSScott Carter, SD IOSW /*
1115febcb4aSScott Carter, SD IOSW  * i_ddi_irm_poststartup()
1125febcb4aSScott Carter, SD IOSW  *
1135febcb4aSScott Carter, SD IOSW  *	IRM is not activated until after the IO subsystem is initialized.
1145febcb4aSScott Carter, SD IOSW  *	When activated, per-pool balancing threads are spawned and a flag
1155febcb4aSScott Carter, SD IOSW  *	is set so that all future pools will be activated when created.
1165febcb4aSScott Carter, SD IOSW  *
1175febcb4aSScott Carter, SD IOSW  *	NOTE: the global variable 'irm_enable' disables IRM if zero.
1185febcb4aSScott Carter, SD IOSW  */
1195febcb4aSScott Carter, SD IOSW void
i_ddi_irm_poststartup(void)1205febcb4aSScott Carter, SD IOSW i_ddi_irm_poststartup(void)
1215febcb4aSScott Carter, SD IOSW {
1225febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
1235febcb4aSScott Carter, SD IOSW 
1245febcb4aSScott Carter, SD IOSW 	/* Do nothing if IRM is disabled */
1255febcb4aSScott Carter, SD IOSW 	if (!irm_enable)
1265febcb4aSScott Carter, SD IOSW 		return;
1275febcb4aSScott Carter, SD IOSW 
1285febcb4aSScott Carter, SD IOSW 	/* Lock the global list */
1295febcb4aSScott Carter, SD IOSW 	mutex_enter(&irm_pools_lock);
1305febcb4aSScott Carter, SD IOSW 
1315febcb4aSScott Carter, SD IOSW 	/* Activate all defined pools */
1325febcb4aSScott Carter, SD IOSW 	for (pool_p = list_head(&irm_pools_list); pool_p;
1335febcb4aSScott Carter, SD IOSW 	    pool_p = list_next(&irm_pools_list, pool_p))
1345febcb4aSScott Carter, SD IOSW 		pool_p->ipool_thread = thread_create(NULL, 0,
1355febcb4aSScott Carter, SD IOSW 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1365febcb4aSScott Carter, SD IOSW 
1375febcb4aSScott Carter, SD IOSW 	/* Set future pools to be active */
1385febcb4aSScott Carter, SD IOSW 	irm_active = B_TRUE;
1395febcb4aSScott Carter, SD IOSW 
1405febcb4aSScott Carter, SD IOSW 	/* Unlock the global list */
1415febcb4aSScott Carter, SD IOSW 	mutex_exit(&irm_pools_lock);
1425febcb4aSScott Carter, SD IOSW }
1435febcb4aSScott Carter, SD IOSW 
1445febcb4aSScott Carter, SD IOSW /*
1455febcb4aSScott Carter, SD IOSW  * NDI interfaces for creating/destroying IRM pools.
1465febcb4aSScott Carter, SD IOSW  */
1475febcb4aSScott Carter, SD IOSW 
1485febcb4aSScott Carter, SD IOSW /*
1495febcb4aSScott Carter, SD IOSW  * ndi_irm_create()
1505febcb4aSScott Carter, SD IOSW  *
1515febcb4aSScott Carter, SD IOSW  *	Nexus interface to create an IRM pool.  Create the new
1525febcb4aSScott Carter, SD IOSW  *	pool and add it to the global list of interrupt pools.
1535febcb4aSScott Carter, SD IOSW  */
1545febcb4aSScott Carter, SD IOSW int
ndi_irm_create(dev_info_t * dip,ddi_irm_params_t * paramsp,ddi_irm_pool_t ** pool_retp)1555febcb4aSScott Carter, SD IOSW ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp,
1565febcb4aSScott Carter, SD IOSW     ddi_irm_pool_t **pool_retp)
1575febcb4aSScott Carter, SD IOSW {
1585febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
1595febcb4aSScott Carter, SD IOSW 
1605febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
1615febcb4aSScott Carter, SD IOSW 	ASSERT(paramsp != NULL);
1625febcb4aSScott Carter, SD IOSW 	ASSERT(pool_retp != NULL);
1635febcb4aSScott Carter, SD IOSW 	ASSERT(paramsp->iparams_total >= 1);
1645febcb4aSScott Carter, SD IOSW 	ASSERT(paramsp->iparams_types != 0);
1655febcb4aSScott Carter, SD IOSW 
1665febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip));
1675febcb4aSScott Carter, SD IOSW 
1685febcb4aSScott Carter, SD IOSW 	/* Check if IRM is enabled */
1695febcb4aSScott Carter, SD IOSW 	if (!irm_enable)
1705febcb4aSScott Carter, SD IOSW 		return (NDI_FAILURE);
1715febcb4aSScott Carter, SD IOSW 
1725febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
1735febcb4aSScott Carter, SD IOSW 	if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) ||
17463ea9ad2SEvan Yan 	    (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0))
1755febcb4aSScott Carter, SD IOSW 		return (NDI_FAILURE);
1765febcb4aSScott Carter, SD IOSW 
1775febcb4aSScott Carter, SD IOSW 	/* Allocate and initialize the pool */
1785febcb4aSScott Carter, SD IOSW 	pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP);
1795febcb4aSScott Carter, SD IOSW 	pool_p->ipool_owner = dip;
1805febcb4aSScott Carter, SD IOSW 	pool_p->ipool_policy = irm_default_policy;
1815febcb4aSScott Carter, SD IOSW 	pool_p->ipool_types = paramsp->iparams_types;
1825febcb4aSScott Carter, SD IOSW 	pool_p->ipool_totsz = paramsp->iparams_total;
18363ea9ad2SEvan Yan 	pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC,
18463ea9ad2SEvan Yan 	    paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER));
1855febcb4aSScott Carter, SD IOSW 	list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t),
1865febcb4aSScott Carter, SD IOSW 	    offsetof(ddi_irm_req_t, ireq_link));
1875febcb4aSScott Carter, SD IOSW 	list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t),
1885febcb4aSScott Carter, SD IOSW 	    offsetof(ddi_irm_req_t, ireq_scratch_link));
1895febcb4aSScott Carter, SD IOSW 	cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL);
1905febcb4aSScott Carter, SD IOSW 	mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL);
1915febcb4aSScott Carter, SD IOSW 	mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL);
1925febcb4aSScott Carter, SD IOSW 
1935febcb4aSScott Carter, SD IOSW 	/* Add to global list of pools */
1945febcb4aSScott Carter, SD IOSW 	mutex_enter(&irm_pools_lock);
1955febcb4aSScott Carter, SD IOSW 	list_insert_tail(&irm_pools_list, pool_p);
1965febcb4aSScott Carter, SD IOSW 	mutex_exit(&irm_pools_lock);
1975febcb4aSScott Carter, SD IOSW 
1985febcb4aSScott Carter, SD IOSW 	/* If IRM is active, then activate the pool */
1995febcb4aSScott Carter, SD IOSW 	if (irm_active)
2005febcb4aSScott Carter, SD IOSW 		pool_p->ipool_thread = thread_create(NULL, 0,
2015febcb4aSScott Carter, SD IOSW 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
2025febcb4aSScott Carter, SD IOSW 
2035febcb4aSScott Carter, SD IOSW 	*pool_retp = pool_p;
2045febcb4aSScott Carter, SD IOSW 	return (NDI_SUCCESS);
2055febcb4aSScott Carter, SD IOSW }
2065febcb4aSScott Carter, SD IOSW 
2075febcb4aSScott Carter, SD IOSW /*
2087ff178cdSJimmy Vetayases  * ndi_irm_resize_pool()
2097ff178cdSJimmy Vetayases  *
2107ff178cdSJimmy Vetayases  *	Nexus interface to resize IRM pool. If the pool size drops
2117ff178cdSJimmy Vetayases  *	below  the allocated number of vectors then initiate rebalance
2127ff178cdSJimmy Vetayases  *	operation before resizing the pool. If rebalance operation fails
2137ff178cdSJimmy Vetayases  *	then return NDI_FAILURE.
2147ff178cdSJimmy Vetayases  */
2157ff178cdSJimmy Vetayases int
ndi_irm_resize_pool(ddi_irm_pool_t * pool_p,uint_t new_size)2167ff178cdSJimmy Vetayases ndi_irm_resize_pool(ddi_irm_pool_t *pool_p, uint_t new_size)
2177ff178cdSJimmy Vetayases {
2187ff178cdSJimmy Vetayases 	uint_t prev_size;
2197ff178cdSJimmy Vetayases 
2207ff178cdSJimmy Vetayases 	ASSERT(pool_p != NULL);
2217ff178cdSJimmy Vetayases 
2227ff178cdSJimmy Vetayases 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
2237ff178cdSJimmy Vetayases 	    " current-size 0x%x new-size 0x%x\n",
2247ff178cdSJimmy Vetayases 	    (void *)pool_p, pool_p->ipool_totsz, new_size));
2257ff178cdSJimmy Vetayases 
2267ff178cdSJimmy Vetayases 	if (pool_p == NULL)
2277ff178cdSJimmy Vetayases 		return (NDI_EINVAL);
2287ff178cdSJimmy Vetayases 
2297ff178cdSJimmy Vetayases 	/* Check if IRM is enabled */
2307ff178cdSJimmy Vetayases 	if (!irm_enable)
2317ff178cdSJimmy Vetayases 		return (NDI_FAILURE);
2327ff178cdSJimmy Vetayases 
2337ff178cdSJimmy Vetayases 	mutex_enter(&pool_p->ipool_lock);
2347ff178cdSJimmy Vetayases 
2357ff178cdSJimmy Vetayases 	/*
2367ff178cdSJimmy Vetayases 	 * If we are increasing the pool size or if the reserved
2377ff178cdSJimmy Vetayases 	 * number of vectors is <= the new pool size then simply
2387ff178cdSJimmy Vetayases 	 * update the pool size and enqueue a reblance operation
2397ff178cdSJimmy Vetayases 	 * if necessary to use the new vectors.
2407ff178cdSJimmy Vetayases 	 */
2417ff178cdSJimmy Vetayases 	if ((pool_p->ipool_totsz < new_size) ||
2427ff178cdSJimmy Vetayases 	    (pool_p->ipool_resno <= new_size)) {
2437ff178cdSJimmy Vetayases 		/* set new pool size */
2447ff178cdSJimmy Vetayases 		pool_p->ipool_totsz = new_size;
2457ff178cdSJimmy Vetayases 		/* adjust the default allocation limit */
2467ff178cdSJimmy Vetayases 		pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC,
2477ff178cdSJimmy Vetayases 		    MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER));
2487ff178cdSJimmy Vetayases 		/* queue a rebalance operation to use the new vectors */
2497ff178cdSJimmy Vetayases 		if (pool_p->ipool_reqno > pool_p->ipool_resno)
2507ff178cdSJimmy Vetayases 			i_ddi_irm_enqueue(pool_p, B_FALSE);
2517ff178cdSJimmy Vetayases 		mutex_exit(&pool_p->ipool_lock);
2527ff178cdSJimmy Vetayases 		return (NDI_SUCCESS);
2537ff178cdSJimmy Vetayases 	}
2547ff178cdSJimmy Vetayases 
2557ff178cdSJimmy Vetayases 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
2567ff178cdSJimmy Vetayases 	    " needs a rebalance operation\n", (void *)pool_p));
2577ff178cdSJimmy Vetayases 
2587ff178cdSJimmy Vetayases 	/*
2597ff178cdSJimmy Vetayases 	 * requires a rebalance operation
2607ff178cdSJimmy Vetayases 	 */
2617ff178cdSJimmy Vetayases 	/* save the current pool size */
2627ff178cdSJimmy Vetayases 	prev_size = pool_p->ipool_totsz;
2637ff178cdSJimmy Vetayases 	/* set the pool size to the desired new value */
2647ff178cdSJimmy Vetayases 	pool_p->ipool_totsz = new_size;
2657ff178cdSJimmy Vetayases 	/* perform the rebalance operation */
2667ff178cdSJimmy Vetayases 	i_ddi_irm_enqueue(pool_p, B_TRUE);
2677ff178cdSJimmy Vetayases 
2687ff178cdSJimmy Vetayases 	/*
2697ff178cdSJimmy Vetayases 	 * If rebalance operation couldn't free up enough
2707ff178cdSJimmy Vetayases 	 * vectors then fail the resize operation.
2717ff178cdSJimmy Vetayases 	 */
2727ff178cdSJimmy Vetayases 	if (pool_p->ipool_resno > new_size) { /* rebalance failed */
2737ff178cdSJimmy Vetayases 		/* restore the pool size to the previous value */
2747ff178cdSJimmy Vetayases 		pool_p->ipool_totsz = prev_size;
2757ff178cdSJimmy Vetayases 		/* enqueue a rebalance operation for the original pool size */
2767ff178cdSJimmy Vetayases 		i_ddi_irm_enqueue(pool_p, B_FALSE);
2777ff178cdSJimmy Vetayases 		mutex_exit(&pool_p->ipool_lock);
2787ff178cdSJimmy Vetayases 		return (NDI_FAILURE);
2797ff178cdSJimmy Vetayases 	} else { /* rebalance worked */
2807ff178cdSJimmy Vetayases 		/* adjust the default allocation limit */
2817ff178cdSJimmy Vetayases 		pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC,
2827ff178cdSJimmy Vetayases 		    MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER));
2837ff178cdSJimmy Vetayases 		mutex_exit(&pool_p->ipool_lock);
2847ff178cdSJimmy Vetayases 		DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
2857ff178cdSJimmy Vetayases 		    " resized from %x to %x\n",
2867ff178cdSJimmy Vetayases 		    (void *)pool_p, prev_size, pool_p->ipool_totsz));
2877ff178cdSJimmy Vetayases 		return (NDI_SUCCESS);
2887ff178cdSJimmy Vetayases 	}
2897ff178cdSJimmy Vetayases }
2907ff178cdSJimmy Vetayases 
2917ff178cdSJimmy Vetayases /*
2925febcb4aSScott Carter, SD IOSW  * ndi_irm_destroy()
2935febcb4aSScott Carter, SD IOSW  *
2945febcb4aSScott Carter, SD IOSW  *	Nexus interface to destroy an IRM pool.  Destroy the pool
2955febcb4aSScott Carter, SD IOSW  *	and remove it from the global list of interrupt pools.
2965febcb4aSScott Carter, SD IOSW  */
2975febcb4aSScott Carter, SD IOSW int
ndi_irm_destroy(ddi_irm_pool_t * pool_p)2985febcb4aSScott Carter, SD IOSW ndi_irm_destroy(ddi_irm_pool_t *pool_p)
2995febcb4aSScott Carter, SD IOSW {
3005febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
3015febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p->ipool_resno == 0);
3025febcb4aSScott Carter, SD IOSW 
3035febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n",
3045febcb4aSScott Carter, SD IOSW 	    (void *)pool_p));
3055febcb4aSScott Carter, SD IOSW 
3065febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
3075febcb4aSScott Carter, SD IOSW 	if (pool_p == NULL)
3085febcb4aSScott Carter, SD IOSW 		return (NDI_FAILURE);
3095febcb4aSScott Carter, SD IOSW 
3105febcb4aSScott Carter, SD IOSW 	/* Validate that pool is empty */
3115febcb4aSScott Carter, SD IOSW 	if (pool_p->ipool_resno != 0)
3125febcb4aSScott Carter, SD IOSW 		return (NDI_BUSY);
3135febcb4aSScott Carter, SD IOSW 
3145febcb4aSScott Carter, SD IOSW 	/* Remove the pool from the global list */
3155febcb4aSScott Carter, SD IOSW 	mutex_enter(&irm_pools_lock);
3165febcb4aSScott Carter, SD IOSW 	list_remove(&irm_pools_list, pool_p);
3175febcb4aSScott Carter, SD IOSW 	mutex_exit(&irm_pools_lock);
3185febcb4aSScott Carter, SD IOSW 
3195febcb4aSScott Carter, SD IOSW 	/* Terminate the balancing thread */
3205febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
3215febcb4aSScott Carter, SD IOSW 	if (pool_p->ipool_thread &&
3225febcb4aSScott Carter, SD IOSW 	    (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) {
3235febcb4aSScott Carter, SD IOSW 		pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT;
3245febcb4aSScott Carter, SD IOSW 		cv_signal(&pool_p->ipool_cv);
325d1b019ceSJustin Frank 		mutex_exit(&pool_p->ipool_lock);
3265febcb4aSScott Carter, SD IOSW 		thread_join(pool_p->ipool_thread->t_did);
327d1b019ceSJustin Frank 	} else
3285febcb4aSScott Carter, SD IOSW 		mutex_exit(&pool_p->ipool_lock);
3295febcb4aSScott Carter, SD IOSW 
3305febcb4aSScott Carter, SD IOSW 	/* Destroy the pool */
3315febcb4aSScott Carter, SD IOSW 	cv_destroy(&pool_p->ipool_cv);
3325febcb4aSScott Carter, SD IOSW 	mutex_destroy(&pool_p->ipool_lock);
3335febcb4aSScott Carter, SD IOSW 	mutex_destroy(&pool_p->ipool_navail_lock);
3345febcb4aSScott Carter, SD IOSW 	list_destroy(&pool_p->ipool_req_list);
3355febcb4aSScott Carter, SD IOSW 	list_destroy(&pool_p->ipool_scratch_list);
3365febcb4aSScott Carter, SD IOSW 	kmem_free(pool_p, sizeof (ddi_irm_pool_t));
3375febcb4aSScott Carter, SD IOSW 
3385febcb4aSScott Carter, SD IOSW 	return (NDI_SUCCESS);
3395febcb4aSScott Carter, SD IOSW }
3405febcb4aSScott Carter, SD IOSW 
3415febcb4aSScott Carter, SD IOSW /*
3425febcb4aSScott Carter, SD IOSW  * Insert/Modify/Remove Interrupt Requests
3435febcb4aSScott Carter, SD IOSW  */
3445febcb4aSScott Carter, SD IOSW 
3455febcb4aSScott Carter, SD IOSW /*
3465febcb4aSScott Carter, SD IOSW  * i_ddi_irm_insert()
3475febcb4aSScott Carter, SD IOSW  *
3485febcb4aSScott Carter, SD IOSW  *	Insert a new request into an interrupt pool, and balance the pool.
3495febcb4aSScott Carter, SD IOSW  */
3505febcb4aSScott Carter, SD IOSW int
i_ddi_irm_insert(dev_info_t * dip,int type,int count)3515febcb4aSScott Carter, SD IOSW i_ddi_irm_insert(dev_info_t *dip, int type, int count)
3525febcb4aSScott Carter, SD IOSW {
3535febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
3545febcb4aSScott Carter, SD IOSW 	devinfo_intr_t	*intr_p;
3555febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
3565febcb4aSScott Carter, SD IOSW 	uint_t		nreq, nmin, npartial;
3575febcb4aSScott Carter, SD IOSW 	boolean_t	irm_flag = B_FALSE;
3585febcb4aSScott Carter, SD IOSW 
3595febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
3605febcb4aSScott Carter, SD IOSW 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
3615febcb4aSScott Carter, SD IOSW 	ASSERT(count > 0);
3625febcb4aSScott Carter, SD IOSW 
3635febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n",
3645febcb4aSScott Carter, SD IOSW 	    (void *)dip, type, count));
3655febcb4aSScott Carter, SD IOSW 
3665febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
3675febcb4aSScott Carter, SD IOSW 	if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) {
3685febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n"));
3695febcb4aSScott Carter, SD IOSW 		return (DDI_EINVAL);
3705febcb4aSScott Carter, SD IOSW 	}
3715febcb4aSScott Carter, SD IOSW 
3725febcb4aSScott Carter, SD IOSW 	/* Check for an existing request */
3735febcb4aSScott Carter, SD IOSW 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
3745febcb4aSScott Carter, SD IOSW 	    (intr_p->devi_irm_req_p != NULL))
3755febcb4aSScott Carter, SD IOSW 		return (DDI_SUCCESS);
3765febcb4aSScott Carter, SD IOSW 
3775febcb4aSScott Carter, SD IOSW 	/* Check for IRM support from the system */
3785febcb4aSScott Carter, SD IOSW 	if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) {
3795febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n"));
3805febcb4aSScott Carter, SD IOSW 		return (DDI_ENOTSUP);
3815febcb4aSScott Carter, SD IOSW 	}
3825febcb4aSScott Carter, SD IOSW 
3835febcb4aSScott Carter, SD IOSW 	/* Check for IRM support from the driver */
384a120541cSScott M. Carter 	if (i_ddi_irm_supported(dip, type) == DDI_SUCCESS)
3855febcb4aSScott Carter, SD IOSW 		irm_flag = B_TRUE;
3865febcb4aSScott Carter, SD IOSW 
3875febcb4aSScott Carter, SD IOSW 	/* Determine request size */
388d88aab48SEvan Yan 	nreq = (irm_flag) ? count :
389a120541cSScott M. Carter 	    MIN(count, i_ddi_intr_get_limit(dip, type, pool_p));
3905febcb4aSScott Carter, SD IOSW 	nmin = (irm_flag) ? 1 : nreq;
3915febcb4aSScott Carter, SD IOSW 	npartial = MIN(nreq, pool_p->ipool_defsz);
3925febcb4aSScott Carter, SD IOSW 
3935febcb4aSScott Carter, SD IOSW 	/* Allocate and initialize the request */
3945febcb4aSScott Carter, SD IOSW 	req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP);
3955febcb4aSScott Carter, SD IOSW 	req_p->ireq_type = type;
3965febcb4aSScott Carter, SD IOSW 	req_p->ireq_dip = dip;
3975febcb4aSScott Carter, SD IOSW 	req_p->ireq_pool_p = pool_p;
3985febcb4aSScott Carter, SD IOSW 	req_p->ireq_nreq = nreq;
3995febcb4aSScott Carter, SD IOSW 	req_p->ireq_flags = DDI_IRM_FLAG_NEW;
400a120541cSScott M. Carter 	if (irm_flag)
4015febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
4025febcb4aSScott Carter, SD IOSW 
4035febcb4aSScott Carter, SD IOSW 	/* Lock the pool */
4045febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
4055febcb4aSScott Carter, SD IOSW 
4065febcb4aSScott Carter, SD IOSW 	/* Check for minimal fit before inserting */
4075febcb4aSScott Carter, SD IOSW 	if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) {
4085febcb4aSScott Carter, SD IOSW 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
4095febcb4aSScott Carter, SD IOSW 		    ddi_driver_name(dip), ddi_get_instance(dip));
4105febcb4aSScott Carter, SD IOSW 		mutex_exit(&pool_p->ipool_lock);
4115febcb4aSScott Carter, SD IOSW 		kmem_free(req_p, sizeof (ddi_irm_req_t));
4125febcb4aSScott Carter, SD IOSW 		return (DDI_EAGAIN);
4135febcb4aSScott Carter, SD IOSW 	}
4145febcb4aSScott Carter, SD IOSW 
4155febcb4aSScott Carter, SD IOSW 	/* Insert the request into the pool */
4165febcb4aSScott Carter, SD IOSW 	pool_p->ipool_reqno += nreq;
4175febcb4aSScott Carter, SD IOSW 	pool_p->ipool_minno += nmin;
4185febcb4aSScott Carter, SD IOSW 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
4195febcb4aSScott Carter, SD IOSW 
4205febcb4aSScott Carter, SD IOSW 	/*
4215febcb4aSScott Carter, SD IOSW 	 * Try to fulfill the request.
4225febcb4aSScott Carter, SD IOSW 	 *
4235febcb4aSScott Carter, SD IOSW 	 * If all the interrupts are available, and either the request
4245febcb4aSScott Carter, SD IOSW 	 * is static or the pool is active, then just take them directly.
4255febcb4aSScott Carter, SD IOSW 	 *
4265febcb4aSScott Carter, SD IOSW 	 * If only some of the interrupts are available, and the request
4275febcb4aSScott Carter, SD IOSW 	 * can receive future callbacks, then take some now but queue the
4285febcb4aSScott Carter, SD IOSW 	 * pool to be rebalanced later.
4295febcb4aSScott Carter, SD IOSW 	 *
4305febcb4aSScott Carter, SD IOSW 	 * Otherwise, immediately rebalance the pool and wait.
4315febcb4aSScott Carter, SD IOSW 	 */
4325febcb4aSScott Carter, SD IOSW 	if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) &&
4335febcb4aSScott Carter, SD IOSW 	    ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) {
4345febcb4aSScott Carter, SD IOSW 
4355febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4365febcb4aSScott Carter, SD IOSW 		    "request completely fulfilled.\n"));
4375febcb4aSScott Carter, SD IOSW 		pool_p->ipool_resno += nreq;
4385febcb4aSScott Carter, SD IOSW 		req_p->ireq_navail = nreq;
4395febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4405febcb4aSScott Carter, SD IOSW 
4415febcb4aSScott Carter, SD IOSW 	} else if (irm_flag &&
4425febcb4aSScott Carter, SD IOSW 	    ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) {
4435febcb4aSScott Carter, SD IOSW 
4445febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4455febcb4aSScott Carter, SD IOSW 		    "request partially fulfilled.\n"));
4465febcb4aSScott Carter, SD IOSW 		pool_p->ipool_resno += npartial;
4475febcb4aSScott Carter, SD IOSW 		req_p->ireq_navail = npartial;
4485febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4495febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, B_FALSE);
4505febcb4aSScott Carter, SD IOSW 
4515febcb4aSScott Carter, SD IOSW 	} else {
4525febcb4aSScott Carter, SD IOSW 
4535febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4545febcb4aSScott Carter, SD IOSW 		    "request needs immediate rebalance.\n"));
4555febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, B_TRUE);
4565febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4575febcb4aSScott Carter, SD IOSW 	}
4585febcb4aSScott Carter, SD IOSW 
4595febcb4aSScott Carter, SD IOSW 	/* Fail if the request cannot be fulfilled at all */
4605febcb4aSScott Carter, SD IOSW 	if (req_p->ireq_navail == 0) {
4615febcb4aSScott Carter, SD IOSW 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
4625febcb4aSScott Carter, SD IOSW 		    ddi_driver_name(dip), ddi_get_instance(dip));
4635febcb4aSScott Carter, SD IOSW 		pool_p->ipool_reqno -= nreq;
4645febcb4aSScott Carter, SD IOSW 		pool_p->ipool_minno -= nmin;
4655febcb4aSScott Carter, SD IOSW 		list_remove(&pool_p->ipool_req_list, req_p);
4666a43bfaaSEvan Yan 		mutex_exit(&pool_p->ipool_lock);
4675febcb4aSScott Carter, SD IOSW 		kmem_free(req_p, sizeof (ddi_irm_req_t));
4685febcb4aSScott Carter, SD IOSW 		return (DDI_EAGAIN);
4695febcb4aSScott Carter, SD IOSW 	}
4705febcb4aSScott Carter, SD IOSW 
4715febcb4aSScott Carter, SD IOSW 	/* Unlock the pool */
4725febcb4aSScott Carter, SD IOSW 	mutex_exit(&pool_p->ipool_lock);
4735febcb4aSScott Carter, SD IOSW 
4745febcb4aSScott Carter, SD IOSW 	intr_p->devi_irm_req_p = req_p;
4755febcb4aSScott Carter, SD IOSW 	return (DDI_SUCCESS);
4765febcb4aSScott Carter, SD IOSW }
4775febcb4aSScott Carter, SD IOSW 
4785febcb4aSScott Carter, SD IOSW /*
4795febcb4aSScott Carter, SD IOSW  * i_ddi_irm_modify()
4805febcb4aSScott Carter, SD IOSW  *
4815febcb4aSScott Carter, SD IOSW  *	Modify an existing request in an interrupt pool, and balance the pool.
4825febcb4aSScott Carter, SD IOSW  */
4835febcb4aSScott Carter, SD IOSW int
i_ddi_irm_modify(dev_info_t * dip,int nreq)4845febcb4aSScott Carter, SD IOSW i_ddi_irm_modify(dev_info_t *dip, int nreq)
4855febcb4aSScott Carter, SD IOSW {
4865febcb4aSScott Carter, SD IOSW 	devinfo_intr_t	*intr_p;
4875febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
4885febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
489a120541cSScott M. Carter 	int		type;
490a120541cSScott M. Carter 	int		retval = DDI_SUCCESS;
4915febcb4aSScott Carter, SD IOSW 
4925febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
493a120541cSScott M. Carter 	ASSERT(nreq > 0);
4945febcb4aSScott Carter, SD IOSW 
4955febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n",
4965febcb4aSScott Carter, SD IOSW 	    (void *)dip, nreq));
4975febcb4aSScott Carter, SD IOSW 
4985febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
4995febcb4aSScott Carter, SD IOSW 	if ((dip == NULL) || (nreq < 1)) {
5005febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
5015febcb4aSScott Carter, SD IOSW 		return (DDI_EINVAL);
5025febcb4aSScott Carter, SD IOSW 	}
5035febcb4aSScott Carter, SD IOSW 
504a120541cSScott M. Carter 	/* Do nothing if not mapped to an IRM pool */
505a120541cSScott M. Carter 	if (((intr_p = DEVI(dip)->devi_intr_p) == NULL) ||
506a120541cSScott M. Carter 	    ((req_p = intr_p->devi_irm_req_p) == NULL))
507a120541cSScott M. Carter 		return (DDI_SUCCESS);
508a120541cSScott M. Carter 
509a120541cSScott M. Carter 	/* Do nothing if new size is the same */
510a120541cSScott M. Carter 	if (nreq == req_p->ireq_nreq)
511a120541cSScott M. Carter 		return (DDI_SUCCESS);
512a120541cSScott M. Carter 
513a120541cSScott M. Carter 	/* Do not allow MSI requests to be resized */
514a120541cSScott M. Carter 	if ((type = req_p->ireq_type) == DDI_INTR_TYPE_MSI) {
515a120541cSScott M. Carter 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid type\n"));
5165febcb4aSScott Carter, SD IOSW 		return (DDI_ENOTSUP);
5175febcb4aSScott Carter, SD IOSW 	}
5185febcb4aSScott Carter, SD IOSW 
519a120541cSScott M. Carter 	/* Select the pool */
520a120541cSScott M. Carter 	if ((pool_p = req_p->ireq_pool_p) == NULL) {
521a120541cSScott M. Carter 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: missing pool\n"));
522a120541cSScott M. Carter 		return (DDI_FAILURE);
523a120541cSScott M. Carter 	}
524a120541cSScott M. Carter 
5255febcb4aSScott Carter, SD IOSW 	/* Validate request size is not too large */
526a120541cSScott M. Carter 	if (nreq > i_ddi_intr_get_limit(dip, type, pool_p)) {
5275febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
5285febcb4aSScott Carter, SD IOSW 		return (DDI_EINVAL);
5295febcb4aSScott Carter, SD IOSW 	}
5305febcb4aSScott Carter, SD IOSW 
5315febcb4aSScott Carter, SD IOSW 	/* Lock the pool */
5325febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
5335febcb4aSScott Carter, SD IOSW 
534a120541cSScott M. Carter 	/*
535a120541cSScott M. Carter 	 * Process the modification.
536a120541cSScott M. Carter 	 *
537a120541cSScott M. Carter 	 *	- To increase a non-IRM request, call the implementation in
538a120541cSScott M. Carter 	 *	  i_ddi_irm_modify_increase().
539a120541cSScott M. Carter 	 *
540a120541cSScott M. Carter 	 *	- To decrease a non-IRM request, directly update the pool and
541a120541cSScott M. Carter 	 *	  request, then queue the pool for later rebalancing.
542a120541cSScott M. Carter 	 *
543a120541cSScott M. Carter 	 *	- To modify an IRM request, always queue the pool for later
544a120541cSScott M. Carter 	 *	  rebalancing.  IRM consumers rely upon callbacks for changes.
545a120541cSScott M. Carter 	 */
546a120541cSScott M. Carter 	if ((nreq > req_p->ireq_nreq) &&
547a120541cSScott M. Carter 	    (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)) {
548a120541cSScott M. Carter 
549a120541cSScott M. Carter 		retval = i_ddi_irm_modify_increase(req_p, nreq);
550a120541cSScott M. Carter 
551a120541cSScott M. Carter 	} else {
552a120541cSScott M. Carter 
5535febcb4aSScott Carter, SD IOSW 		/* Update pool and request */
5545febcb4aSScott Carter, SD IOSW 		pool_p->ipool_reqno -= req_p->ireq_nreq;
5555febcb4aSScott Carter, SD IOSW 		pool_p->ipool_reqno += nreq;
556a120541cSScott M. Carter 		if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS) {
557a120541cSScott M. Carter 			pool_p->ipool_minno -= req_p->ireq_navail;
558a120541cSScott M. Carter 			pool_p->ipool_resno -= req_p->ireq_navail;
559a120541cSScott M. Carter 			pool_p->ipool_minno += nreq;
560a120541cSScott M. Carter 			pool_p->ipool_resno += nreq;
561a120541cSScott M. Carter 			req_p->ireq_navail = nreq;
562a120541cSScott M. Carter 		}
5635febcb4aSScott Carter, SD IOSW 		req_p->ireq_nreq = nreq;
5645febcb4aSScott Carter, SD IOSW 
565a120541cSScott M. Carter 		/* Re-sort request into the pool */
5665febcb4aSScott Carter, SD IOSW 		list_remove(&pool_p->ipool_req_list, req_p);
5675febcb4aSScott Carter, SD IOSW 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
5685febcb4aSScott Carter, SD IOSW 
569a120541cSScott M. Carter 		/* Queue pool for asynchronous rebalance */
5705febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, B_FALSE);
571a120541cSScott M. Carter 	}
5725febcb4aSScott Carter, SD IOSW 
5735febcb4aSScott Carter, SD IOSW 	/* Unlock the pool */
5745febcb4aSScott Carter, SD IOSW 	mutex_exit(&pool_p->ipool_lock);
575a120541cSScott M. Carter 
576a120541cSScott M. Carter 	return (retval);
5775febcb4aSScott Carter, SD IOSW }
5785febcb4aSScott Carter, SD IOSW 
579a120541cSScott M. Carter /*
580a120541cSScott M. Carter  * i_ddi_irm_modify_increase()
581a120541cSScott M. Carter  *
582a120541cSScott M. Carter  *	Increase a non-IRM request.  The additional interrupts are
583a120541cSScott M. Carter  *	directly taken from the pool when possible.  Otherwise, an
584a120541cSScott M. Carter  *	immediate, synchronous rebalance is performed.  A temporary
585a120541cSScott M. Carter  *	proxy request is used for any rebalance operation to ensure
586a120541cSScott M. Carter  *	the request is not reduced below its current allocation.
587a120541cSScott M. Carter  *
588a120541cSScott M. Carter  *	NOTE: pool must already be locked.
589a120541cSScott M. Carter  */
590a120541cSScott M. Carter static int
i_ddi_irm_modify_increase(ddi_irm_req_t * req_p,int nreq)591a120541cSScott M. Carter i_ddi_irm_modify_increase(ddi_irm_req_t *req_p, int nreq)
592a120541cSScott M. Carter {
593a120541cSScott M. Carter 	dev_info_t	*dip = req_p->ireq_dip;
594a120541cSScott M. Carter 	ddi_irm_pool_t	*pool_p = req_p->ireq_pool_p;
595a120541cSScott M. Carter 	ddi_irm_req_t	new_req;
596a120541cSScott M. Carter 	int		count, delta;
597a120541cSScott M. Carter 
598a120541cSScott M. Carter 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
599a120541cSScott M. Carter 
600a120541cSScott M. Carter 	/* Compute number of additional vectors */
601a120541cSScott M. Carter 	count = nreq - req_p->ireq_nreq;
602a120541cSScott M. Carter 
603a120541cSScott M. Carter 	/* Check for minimal fit */
604a120541cSScott M. Carter 	if ((pool_p->ipool_minno + count) > pool_p->ipool_totsz) {
605a120541cSScott M. Carter 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
606a120541cSScott M. Carter 		    ddi_driver_name(dip), ddi_get_instance(dip));
607a120541cSScott M. Carter 		return (DDI_EAGAIN);
608a120541cSScott M. Carter 	}
609a120541cSScott M. Carter 
610a120541cSScott M. Carter 	/* Update the pool */
611a120541cSScott M. Carter 	pool_p->ipool_reqno += count;
612a120541cSScott M. Carter 	pool_p->ipool_minno += count;
613a120541cSScott M. Carter 
614a120541cSScott M. Carter 	/* Attempt direct implementation */
615a120541cSScott M. Carter 	if ((pool_p->ipool_resno + count) <= pool_p->ipool_totsz) {
616a120541cSScott M. Carter 		req_p->ireq_nreq += count;
617a120541cSScott M. Carter 		req_p->ireq_navail += count;
618a120541cSScott M. Carter 		pool_p->ipool_resno += count;
619a120541cSScott M. Carter 		return (DDI_SUCCESS);
620a120541cSScott M. Carter 	}
621a120541cSScott M. Carter 
622a120541cSScott M. Carter 	/* Rebalance required: fail if pool is not active */
623a120541cSScott M. Carter 	if ((pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE) == 0) {
624a120541cSScott M. Carter 		pool_p->ipool_reqno -= count;
625a120541cSScott M. Carter 		pool_p->ipool_minno -= count;
626a120541cSScott M. Carter 		return (DDI_EAGAIN);
627a120541cSScott M. Carter 	}
628a120541cSScott M. Carter 
629a120541cSScott M. Carter 	/* Insert temporary proxy request */
630a120541cSScott M. Carter 	bzero(&new_req, sizeof (ddi_irm_req_t));
631a120541cSScott M. Carter 	new_req.ireq_dip = dip;
632a120541cSScott M. Carter 	new_req.ireq_nreq = count;
633a120541cSScott M. Carter 	new_req.ireq_pool_p = pool_p;
634a120541cSScott M. Carter 	new_req.ireq_type = req_p->ireq_type;
635a120541cSScott M. Carter 	new_req.ireq_flags = DDI_IRM_FLAG_NEW;
636a120541cSScott M. Carter 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, &new_req);
637a120541cSScott M. Carter 
638a120541cSScott M. Carter 	/* Synchronously rebalance */
639a120541cSScott M. Carter 	i_ddi_irm_enqueue(pool_p, B_TRUE);
640a120541cSScott M. Carter 
641a120541cSScott M. Carter 	/* Remove proxy request, and merge into original request */
642a120541cSScott M. Carter 	req_p->ireq_nreq += count;
643a120541cSScott M. Carter 	if ((delta = (count - new_req.ireq_navail)) > 0) {
644a120541cSScott M. Carter 		req_p->ireq_nreq -= delta;
645a120541cSScott M. Carter 		pool_p->ipool_reqno -= delta;
646a120541cSScott M. Carter 		pool_p->ipool_minno -= delta;
647a120541cSScott M. Carter 	}
648a120541cSScott M. Carter 	req_p->ireq_navail += new_req.ireq_navail;
649a120541cSScott M. Carter 	list_remove(&pool_p->ipool_req_list, req_p);
650a120541cSScott M. Carter 	list_remove(&pool_p->ipool_req_list, &new_req);
651a120541cSScott M. Carter 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
652a120541cSScott M. Carter 
6535febcb4aSScott Carter, SD IOSW 	return (DDI_SUCCESS);
6545febcb4aSScott Carter, SD IOSW }
6555febcb4aSScott Carter, SD IOSW 
6565febcb4aSScott Carter, SD IOSW /*
6575febcb4aSScott Carter, SD IOSW  * i_ddi_irm_remove()
6585febcb4aSScott Carter, SD IOSW  *
6595febcb4aSScott Carter, SD IOSW  *	Remove a request from an interrupt pool, and balance the pool.
6605febcb4aSScott Carter, SD IOSW  */
6615febcb4aSScott Carter, SD IOSW int
i_ddi_irm_remove(dev_info_t * dip)6625febcb4aSScott Carter, SD IOSW i_ddi_irm_remove(dev_info_t *dip)
6635febcb4aSScott Carter, SD IOSW {
6645febcb4aSScott Carter, SD IOSW 	devinfo_intr_t	*intr_p;
6655febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
6665febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
6675febcb4aSScott Carter, SD IOSW 	uint_t		nmin;
6685febcb4aSScott Carter, SD IOSW 
6695febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
6705febcb4aSScott Carter, SD IOSW 
6715febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip));
6725febcb4aSScott Carter, SD IOSW 
6735febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
6745febcb4aSScott Carter, SD IOSW 	if (dip == NULL) {
6755febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n"));
6765febcb4aSScott Carter, SD IOSW 		return (DDI_EINVAL);
6775febcb4aSScott Carter, SD IOSW 	}
6785febcb4aSScott Carter, SD IOSW 
6795febcb4aSScott Carter, SD IOSW 	/* Check if the device has a request */
6805febcb4aSScott Carter, SD IOSW 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
6815febcb4aSScott Carter, SD IOSW 	    !(req_p = intr_p->devi_irm_req_p)) {
6825febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n"));
6835febcb4aSScott Carter, SD IOSW 		return (DDI_EINVAL);
6845febcb4aSScott Carter, SD IOSW 	}
6855febcb4aSScott Carter, SD IOSW 
6865febcb4aSScott Carter, SD IOSW 	/* Lock the pool */
6875febcb4aSScott Carter, SD IOSW 	pool_p = req_p->ireq_pool_p;
6885febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
6895febcb4aSScott Carter, SD IOSW 
6905febcb4aSScott Carter, SD IOSW 	/* Remove request */
6915febcb4aSScott Carter, SD IOSW 	nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq;
6925febcb4aSScott Carter, SD IOSW 	pool_p->ipool_minno -= nmin;
6935febcb4aSScott Carter, SD IOSW 	pool_p->ipool_reqno -= req_p->ireq_nreq;
6945febcb4aSScott Carter, SD IOSW 	pool_p->ipool_resno -= req_p->ireq_navail;
6955febcb4aSScott Carter, SD IOSW 	list_remove(&pool_p->ipool_req_list, req_p);
6965febcb4aSScott Carter, SD IOSW 
6975febcb4aSScott Carter, SD IOSW 	/* Queue pool to be rebalanced */
6985febcb4aSScott Carter, SD IOSW 	i_ddi_irm_enqueue(pool_p, B_FALSE);
6995febcb4aSScott Carter, SD IOSW 
7005febcb4aSScott Carter, SD IOSW 	/* Unlock the pool */
7015febcb4aSScott Carter, SD IOSW 	mutex_exit(&pool_p->ipool_lock);
7025febcb4aSScott Carter, SD IOSW 
7035febcb4aSScott Carter, SD IOSW 	/* Destroy the request */
7045febcb4aSScott Carter, SD IOSW 	intr_p->devi_irm_req_p = NULL;
7055febcb4aSScott Carter, SD IOSW 	kmem_free(req_p, sizeof (ddi_irm_req_t));
7065febcb4aSScott Carter, SD IOSW 
7075febcb4aSScott Carter, SD IOSW 	return (DDI_SUCCESS);
7085febcb4aSScott Carter, SD IOSW }
7095febcb4aSScott Carter, SD IOSW 
7105febcb4aSScott Carter, SD IOSW /*
7115febcb4aSScott Carter, SD IOSW  * i_ddi_irm_set_cb()
7125febcb4aSScott Carter, SD IOSW  *
7135febcb4aSScott Carter, SD IOSW  *	Change the callback flag for a request, in response to
7145febcb4aSScott Carter, SD IOSW  *	a change in its callback registration.  Then rebalance
7155febcb4aSScott Carter, SD IOSW  *	the interrupt pool.
7165febcb4aSScott Carter, SD IOSW  *
7175febcb4aSScott Carter, SD IOSW  *	NOTE: the request is not locked because the navail value
7185febcb4aSScott Carter, SD IOSW  *	      is not directly affected.  The balancing thread may
7195febcb4aSScott Carter, SD IOSW  *	      modify the navail value in the background after it
7205febcb4aSScott Carter, SD IOSW  *	      locks the request itself.
7215febcb4aSScott Carter, SD IOSW  */
7225febcb4aSScott Carter, SD IOSW void
i_ddi_irm_set_cb(dev_info_t * dip,boolean_t has_cb_flag)7235febcb4aSScott Carter, SD IOSW i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag)
7245febcb4aSScott Carter, SD IOSW {
7255febcb4aSScott Carter, SD IOSW 	devinfo_intr_t	*intr_p;
7265febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
7275febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
7285febcb4aSScott Carter, SD IOSW 	uint_t		nreq;
7295febcb4aSScott Carter, SD IOSW 
7305febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
7315febcb4aSScott Carter, SD IOSW 
7325febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n",
7335febcb4aSScott Carter, SD IOSW 	    (void *)dip, (int)has_cb_flag));
7345febcb4aSScott Carter, SD IOSW 
7355febcb4aSScott Carter, SD IOSW 	/* Validate parameters */
7365febcb4aSScott Carter, SD IOSW 	if (dip == NULL)
7375febcb4aSScott Carter, SD IOSW 		return;
7385febcb4aSScott Carter, SD IOSW 
7395febcb4aSScott Carter, SD IOSW 	/* Check for association with interrupt pool */
7405febcb4aSScott Carter, SD IOSW 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
7415febcb4aSScott Carter, SD IOSW 	    !(req_p = intr_p->devi_irm_req_p)) {
7425febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n"));
7435febcb4aSScott Carter, SD IOSW 		return;
7445febcb4aSScott Carter, SD IOSW 	}
7455febcb4aSScott Carter, SD IOSW 
7465febcb4aSScott Carter, SD IOSW 	/* Lock the pool */
7475febcb4aSScott Carter, SD IOSW 	pool_p = req_p->ireq_pool_p;
7485febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
7495febcb4aSScott Carter, SD IOSW 
7505febcb4aSScott Carter, SD IOSW 	/*
7515febcb4aSScott Carter, SD IOSW 	 * Update the request and the pool
7525febcb4aSScott Carter, SD IOSW 	 */
7535febcb4aSScott Carter, SD IOSW 	if (has_cb_flag) {
7545febcb4aSScott Carter, SD IOSW 
7555febcb4aSScott Carter, SD IOSW 		/* Update pool statistics */
7565febcb4aSScott Carter, SD IOSW 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
7575febcb4aSScott Carter, SD IOSW 			pool_p->ipool_minno -= (req_p->ireq_nreq - 1);
7585febcb4aSScott Carter, SD IOSW 
7595febcb4aSScott Carter, SD IOSW 		/* Update request */
7605febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
7615febcb4aSScott Carter, SD IOSW 
7625febcb4aSScott Carter, SD IOSW 		/* Rebalance in background */
7635febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, B_FALSE);
7645febcb4aSScott Carter, SD IOSW 
7655febcb4aSScott Carter, SD IOSW 	} else {
7665febcb4aSScott Carter, SD IOSW 
7675febcb4aSScott Carter, SD IOSW 		/* Determine new request size */
7685febcb4aSScott Carter, SD IOSW 		nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz);
7695febcb4aSScott Carter, SD IOSW 
770*86ef0a63SRichard Lowe #if defined(__x86)
7717ff178cdSJimmy Vetayases 		/* Use the default static limit for non-IRM drivers */
7727ff178cdSJimmy Vetayases 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
7737ff178cdSJimmy Vetayases 			nreq = MIN(nreq, ddi_msix_alloc_limit);
7747ff178cdSJimmy Vetayases #endif
7757ff178cdSJimmy Vetayases 
7765febcb4aSScott Carter, SD IOSW 		/* Update pool statistics */
7775febcb4aSScott Carter, SD IOSW 		pool_p->ipool_reqno -= req_p->ireq_nreq;
7785febcb4aSScott Carter, SD IOSW 		pool_p->ipool_reqno += nreq;
7795febcb4aSScott Carter, SD IOSW 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) {
7805febcb4aSScott Carter, SD IOSW 			pool_p->ipool_minno -= 1;
7815febcb4aSScott Carter, SD IOSW 			pool_p->ipool_minno += nreq;
7825febcb4aSScott Carter, SD IOSW 		} else {
7835febcb4aSScott Carter, SD IOSW 			pool_p->ipool_minno -= req_p->ireq_nreq;
7845febcb4aSScott Carter, SD IOSW 			pool_p->ipool_minno += nreq;
7855febcb4aSScott Carter, SD IOSW 		}
7865febcb4aSScott Carter, SD IOSW 
7875febcb4aSScott Carter, SD IOSW 		/* Update request size, and re-sort in pool */
7885febcb4aSScott Carter, SD IOSW 		req_p->ireq_nreq = nreq;
7895febcb4aSScott Carter, SD IOSW 		list_remove(&pool_p->ipool_req_list, req_p);
7905febcb4aSScott Carter, SD IOSW 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
7915febcb4aSScott Carter, SD IOSW 
7925febcb4aSScott Carter, SD IOSW 		/* Rebalance synchronously, before losing callback */
7935febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, B_TRUE);
7945febcb4aSScott Carter, SD IOSW 
7955febcb4aSScott Carter, SD IOSW 		/* Remove callback flag */
7965febcb4aSScott Carter, SD IOSW 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK);
7975febcb4aSScott Carter, SD IOSW 	}
7985febcb4aSScott Carter, SD IOSW 
7995febcb4aSScott Carter, SD IOSW 	/* Unlock the pool */
8005febcb4aSScott Carter, SD IOSW 	mutex_exit(&pool_p->ipool_lock);
8015febcb4aSScott Carter, SD IOSW }
8025febcb4aSScott Carter, SD IOSW 
8035febcb4aSScott Carter, SD IOSW /*
804a120541cSScott M. Carter  * i_ddi_irm_supported()
805a120541cSScott M. Carter  *
806a120541cSScott M. Carter  *	Query if IRM is supported by a driver using a specific interrupt type.
807a120541cSScott M. Carter  *	Notice that IRM is limited to MSI-X users with registered callbacks.
808a120541cSScott M. Carter  */
809a120541cSScott M. Carter int
i_ddi_irm_supported(dev_info_t * dip,int type)810a120541cSScott M. Carter i_ddi_irm_supported(dev_info_t *dip, int type)
811a120541cSScott M. Carter {
812a120541cSScott M. Carter 	ddi_cb_t	*cb_p = DEVI(dip)->devi_cb_p;
813a120541cSScott M. Carter 
814a120541cSScott M. Carter 	return ((DDI_IRM_HAS_CB(cb_p) && (type == DDI_INTR_TYPE_MSIX)) ?
815a120541cSScott M. Carter 	    DDI_SUCCESS : DDI_ENOTSUP);
816a120541cSScott M. Carter }
817a120541cSScott M. Carter 
818a120541cSScott M. Carter /*
8195febcb4aSScott Carter, SD IOSW  * Interrupt Pool Balancing
8205febcb4aSScott Carter, SD IOSW  */
8215febcb4aSScott Carter, SD IOSW 
8225febcb4aSScott Carter, SD IOSW /*
8235febcb4aSScott Carter, SD IOSW  * irm_balance_thread()
8245febcb4aSScott Carter, SD IOSW  *
8255febcb4aSScott Carter, SD IOSW  *	One instance of this thread operates per each defined IRM pool.
8265febcb4aSScott Carter, SD IOSW  *	It does the initial activation of the pool, as well as balancing
8275febcb4aSScott Carter, SD IOSW  *	any requests that were queued up before the pool was active.
8285febcb4aSScott Carter, SD IOSW  *	Once active, it waits forever to service balance operations.
8295febcb4aSScott Carter, SD IOSW  */
8305febcb4aSScott Carter, SD IOSW static void
irm_balance_thread(ddi_irm_pool_t * pool_p)8315febcb4aSScott Carter, SD IOSW irm_balance_thread(ddi_irm_pool_t *pool_p)
8325febcb4aSScott Carter, SD IOSW {
833d3d50737SRafael Vanoni 	clock_t		interval;
8345febcb4aSScott Carter, SD IOSW 
8355febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n",
8365febcb4aSScott Carter, SD IOSW 	    (void *)pool_p));
8375febcb4aSScott Carter, SD IOSW 
8385febcb4aSScott Carter, SD IOSW 	/* Lock the pool */
8395febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_lock);
8405febcb4aSScott Carter, SD IOSW 
8415febcb4aSScott Carter, SD IOSW 	/* Perform initial balance if required */
8425febcb4aSScott Carter, SD IOSW 	if (pool_p->ipool_reqno > pool_p->ipool_resno)
8435febcb4aSScott Carter, SD IOSW 		i_ddi_irm_balance(pool_p);
8445febcb4aSScott Carter, SD IOSW 
8455febcb4aSScott Carter, SD IOSW 	/* Activate the pool */
8465febcb4aSScott Carter, SD IOSW 	pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE;
8475febcb4aSScott Carter, SD IOSW 
848b19b5501SEvan Yan 	/*
849b19b5501SEvan Yan 	 * Main loop.
850b19b5501SEvan Yan 	 * Iterate once first before wait on signal, in case there is signal
851b19b5501SEvan Yan 	 * sent before this thread being created
852b19b5501SEvan Yan 	 */
8535febcb4aSScott Carter, SD IOSW 	for (;;) {
8545febcb4aSScott Carter, SD IOSW 
8555febcb4aSScott Carter, SD IOSW 		/* Compute the delay interval */
8565febcb4aSScott Carter, SD IOSW 		interval = drv_usectohz(irm_balance_delay * 1000000);
8575febcb4aSScott Carter, SD IOSW 
8585febcb4aSScott Carter, SD IOSW 		/* Wait one interval, or until there are waiters */
8595febcb4aSScott Carter, SD IOSW 		if ((interval > 0) &&
8605febcb4aSScott Carter, SD IOSW 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) &&
8615febcb4aSScott Carter, SD IOSW 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) {
862d3d50737SRafael Vanoni 			(void) cv_reltimedwait(&pool_p->ipool_cv,
863d3d50737SRafael Vanoni 			    &pool_p->ipool_lock, interval, TR_CLOCK_TICK);
8645febcb4aSScott Carter, SD IOSW 		}
8655febcb4aSScott Carter, SD IOSW 
8665febcb4aSScott Carter, SD IOSW 		/* Check if awakened to exit */
8675febcb4aSScott Carter, SD IOSW 		if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) {
8685febcb4aSScott Carter, SD IOSW 			DDI_INTR_IRMDBG((CE_CONT,
8695febcb4aSScott Carter, SD IOSW 			    "irm_balance_thread: exiting...\n"));
8705febcb4aSScott Carter, SD IOSW 			mutex_exit(&pool_p->ipool_lock);
8715febcb4aSScott Carter, SD IOSW 			thread_exit();
8725febcb4aSScott Carter, SD IOSW 		}
8735febcb4aSScott Carter, SD IOSW 
8745febcb4aSScott Carter, SD IOSW 		/* Balance the pool */
8755febcb4aSScott Carter, SD IOSW 		i_ddi_irm_balance(pool_p);
8765febcb4aSScott Carter, SD IOSW 
8775febcb4aSScott Carter, SD IOSW 		/* Notify waiters */
8785febcb4aSScott Carter, SD IOSW 		if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) {
8795febcb4aSScott Carter, SD IOSW 			cv_broadcast(&pool_p->ipool_cv);
8805febcb4aSScott Carter, SD IOSW 			pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS);
8815febcb4aSScott Carter, SD IOSW 		}
8825febcb4aSScott Carter, SD IOSW 
8835febcb4aSScott Carter, SD IOSW 		/* Clear QUEUED condition */
8845febcb4aSScott Carter, SD IOSW 		pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED);
885b19b5501SEvan Yan 
886b19b5501SEvan Yan 		/* Sleep until queued */
887b19b5501SEvan Yan 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
888b19b5501SEvan Yan 
889b19b5501SEvan Yan 		DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n"));
8905febcb4aSScott Carter, SD IOSW 	}
8915febcb4aSScott Carter, SD IOSW }
8925febcb4aSScott Carter, SD IOSW 
8935febcb4aSScott Carter, SD IOSW /*
8945febcb4aSScott Carter, SD IOSW  * i_ddi_irm_balance()
8955febcb4aSScott Carter, SD IOSW  *
8965febcb4aSScott Carter, SD IOSW  *	Balance a pool.  The general algorithm is to first reset all
8975febcb4aSScott Carter, SD IOSW  *	requests to their maximum size, use reduction algorithms to
8985febcb4aSScott Carter, SD IOSW  *	solve any imbalance, and then notify affected drivers.
8995febcb4aSScott Carter, SD IOSW  */
9005febcb4aSScott Carter, SD IOSW static void
i_ddi_irm_balance(ddi_irm_pool_t * pool_p)9015febcb4aSScott Carter, SD IOSW i_ddi_irm_balance(ddi_irm_pool_t *pool_p)
9025febcb4aSScott Carter, SD IOSW {
9035febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
9045febcb4aSScott Carter, SD IOSW 
9055febcb4aSScott Carter, SD IOSW #ifdef	DEBUG
9065febcb4aSScott Carter, SD IOSW 	uint_t		debug_totsz = 0;
9075febcb4aSScott Carter, SD IOSW 	int		debug_policy = 0;
9085febcb4aSScott Carter, SD IOSW #endif	/* DEBUG */
9095febcb4aSScott Carter, SD IOSW 
9105febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
9115febcb4aSScott Carter, SD IOSW 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
9125febcb4aSScott Carter, SD IOSW 
9135febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n",
9145febcb4aSScott Carter, SD IOSW 	    (void *)pool_p));
9155febcb4aSScott Carter, SD IOSW 
916b19b5501SEvan Yan #ifndef DEBUG
917b19b5501SEvan Yan 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
918b19b5501SEvan Yan #else
919b19b5501SEvan Yan 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
920b19b5501SEvan Yan #endif  /* DEBUG */
921b19b5501SEvan Yan 		DDI_INTR_IRMDBG((CE_CONT,
922b19b5501SEvan Yan 		    "i_ddi_irm_balance: pool already balanced\n"));
923b19b5501SEvan Yan 		return;
924b19b5501SEvan Yan 	}
925b19b5501SEvan Yan 
9265febcb4aSScott Carter, SD IOSW #ifdef	DEBUG	/* Adjust size and policy settings */
9275febcb4aSScott Carter, SD IOSW 	if (irm_debug_size > pool_p->ipool_minno) {
9285febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n",
9295febcb4aSScott Carter, SD IOSW 		    irm_debug_size));
9305febcb4aSScott Carter, SD IOSW 		debug_totsz = pool_p->ipool_totsz;
9315febcb4aSScott Carter, SD IOSW 		pool_p->ipool_totsz = irm_debug_size;
9325febcb4aSScott Carter, SD IOSW 	}
9335febcb4aSScott Carter, SD IOSW 	if (DDI_IRM_POLICY_VALID(irm_debug_policy)) {
9345febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT,
9355febcb4aSScott Carter, SD IOSW 		    "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy));
9365febcb4aSScott Carter, SD IOSW 		debug_policy = pool_p->ipool_policy;
9375febcb4aSScott Carter, SD IOSW 		pool_p->ipool_policy = irm_debug_policy;
9385febcb4aSScott Carter, SD IOSW 	}
9395febcb4aSScott Carter, SD IOSW #endif	/* DEBUG */
9405febcb4aSScott Carter, SD IOSW 
9415febcb4aSScott Carter, SD IOSW 	/* Lock the availability lock */
9425febcb4aSScott Carter, SD IOSW 	mutex_enter(&pool_p->ipool_navail_lock);
9435febcb4aSScott Carter, SD IOSW 
9445febcb4aSScott Carter, SD IOSW 	/*
9455febcb4aSScott Carter, SD IOSW 	 * Put all of the reducible requests into a scratch list.
9465febcb4aSScott Carter, SD IOSW 	 * Reset each one of them to their maximum availability.
9475febcb4aSScott Carter, SD IOSW 	 */
9485febcb4aSScott Carter, SD IOSW 	for (req_p = list_head(&pool_p->ipool_req_list); req_p;
9495febcb4aSScott Carter, SD IOSW 	    req_p = list_next(&pool_p->ipool_req_list, req_p)) {
9505febcb4aSScott Carter, SD IOSW 		if (DDI_IRM_IS_REDUCIBLE(req_p)) {
9515febcb4aSScott Carter, SD IOSW 			pool_p->ipool_resno -= req_p->ireq_navail;
9525febcb4aSScott Carter, SD IOSW 			req_p->ireq_scratch = req_p->ireq_navail;
9535febcb4aSScott Carter, SD IOSW 			req_p->ireq_navail = req_p->ireq_nreq;
9545febcb4aSScott Carter, SD IOSW 			pool_p->ipool_resno += req_p->ireq_navail;
9555febcb4aSScott Carter, SD IOSW 			list_insert_tail(&pool_p->ipool_scratch_list, req_p);
9565febcb4aSScott Carter, SD IOSW 		}
9575febcb4aSScott Carter, SD IOSW 	}
9585febcb4aSScott Carter, SD IOSW 
9595febcb4aSScott Carter, SD IOSW 	/* Balance the requests */
9605febcb4aSScott Carter, SD IOSW 	i_ddi_irm_reduce(pool_p);
9615febcb4aSScott Carter, SD IOSW 
9625febcb4aSScott Carter, SD IOSW 	/* Unlock the availability lock */
9635febcb4aSScott Carter, SD IOSW 	mutex_exit(&pool_p->ipool_navail_lock);
9645febcb4aSScott Carter, SD IOSW 
9655febcb4aSScott Carter, SD IOSW 	/*
9665febcb4aSScott Carter, SD IOSW 	 * Process REMOVE notifications.
9675febcb4aSScott Carter, SD IOSW 	 *
9685febcb4aSScott Carter, SD IOSW 	 * If a driver fails to release interrupts: exclude it from
9695febcb4aSScott Carter, SD IOSW 	 * further processing, correct the resulting imbalance, and
9705febcb4aSScott Carter, SD IOSW 	 * start over again at the head of the scratch list.
9715febcb4aSScott Carter, SD IOSW 	 */
9725febcb4aSScott Carter, SD IOSW 	req_p = list_head(&pool_p->ipool_scratch_list);
9735febcb4aSScott Carter, SD IOSW 	while (req_p) {
9745febcb4aSScott Carter, SD IOSW 		if ((req_p->ireq_navail < req_p->ireq_scratch) &&
9755febcb4aSScott Carter, SD IOSW 		    (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) {
9765febcb4aSScott Carter, SD IOSW 			list_remove(&pool_p->ipool_scratch_list, req_p);
9775febcb4aSScott Carter, SD IOSW 			mutex_enter(&pool_p->ipool_navail_lock);
9785febcb4aSScott Carter, SD IOSW 			i_ddi_irm_reduce(pool_p);
9795febcb4aSScott Carter, SD IOSW 			mutex_exit(&pool_p->ipool_navail_lock);
9805febcb4aSScott Carter, SD IOSW 			req_p = list_head(&pool_p->ipool_scratch_list);
9815febcb4aSScott Carter, SD IOSW 		} else {
9825febcb4aSScott Carter, SD IOSW 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
9835febcb4aSScott Carter, SD IOSW 		}
9845febcb4aSScott Carter, SD IOSW 	}
9855febcb4aSScott Carter, SD IOSW 
9865febcb4aSScott Carter, SD IOSW 	/*
9875febcb4aSScott Carter, SD IOSW 	 * Process ADD notifications.
9885febcb4aSScott Carter, SD IOSW 	 *
9895febcb4aSScott Carter, SD IOSW 	 * This is the last use of the scratch list, so empty it.
9905febcb4aSScott Carter, SD IOSW 	 */
9915febcb4aSScott Carter, SD IOSW 	while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) {
9925febcb4aSScott Carter, SD IOSW 		if (req_p->ireq_navail > req_p->ireq_scratch) {
9935febcb4aSScott Carter, SD IOSW 			(void) i_ddi_irm_notify(pool_p, req_p);
9945febcb4aSScott Carter, SD IOSW 		}
9955febcb4aSScott Carter, SD IOSW 	}
9965febcb4aSScott Carter, SD IOSW 
9975febcb4aSScott Carter, SD IOSW #ifdef	DEBUG	/* Restore size and policy settings */
9985febcb4aSScott Carter, SD IOSW 	if (debug_totsz != 0)
9995febcb4aSScott Carter, SD IOSW 		pool_p->ipool_totsz = debug_totsz;
10005febcb4aSScott Carter, SD IOSW 	if (debug_policy != 0)
10015febcb4aSScott Carter, SD IOSW 		pool_p->ipool_policy = debug_policy;
10025febcb4aSScott Carter, SD IOSW #endif	/* DEBUG */
10035febcb4aSScott Carter, SD IOSW }
10045febcb4aSScott Carter, SD IOSW 
10055febcb4aSScott Carter, SD IOSW /*
10065febcb4aSScott Carter, SD IOSW  * i_ddi_irm_reduce()
10075febcb4aSScott Carter, SD IOSW  *
10085febcb4aSScott Carter, SD IOSW  *	Use reduction algorithms to correct an imbalance in a pool.
10095febcb4aSScott Carter, SD IOSW  */
10105febcb4aSScott Carter, SD IOSW static void
10115febcb4aSScott Carter, SD IOSW i_ddi_irm_reduce(ddi_irm_pool_t *pool_p)
10125febcb4aSScott Carter, SD IOSW {
10136a43bfaaSEvan Yan 	int	imbalance;
10145febcb4aSScott Carter, SD IOSW 
10155febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
10165febcb4aSScott Carter, SD IOSW 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10175febcb4aSScott Carter, SD IOSW 	ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy));
10185febcb4aSScott Carter, SD IOSW 
10195febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n",
10205febcb4aSScott Carter, SD IOSW 	    (void *)pool_p));
10215febcb4aSScott Carter, SD IOSW 
10225febcb4aSScott Carter, SD IOSW 	/* Compute the imbalance.  Do nothing if already balanced. */
10235febcb4aSScott Carter, SD IOSW 	if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0)
10245febcb4aSScott Carter, SD IOSW 		return;
10255febcb4aSScott Carter, SD IOSW 
10265febcb4aSScott Carter, SD IOSW 	/*
10276a43bfaaSEvan Yan 	 * Try policy based reduction first. If it failed, then
10285febcb4aSScott Carter, SD IOSW 	 * possibly reduce new requests as a last resort.
10295febcb4aSScott Carter, SD IOSW 	 */
10306a43bfaaSEvan Yan 	if (i_ddi_irm_reduce_by_policy(pool_p, imbalance, pool_p->ipool_policy)
10316a43bfaaSEvan Yan 	    != DDI_SUCCESS) {
10325febcb4aSScott Carter, SD IOSW 
10335febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT,
10345febcb4aSScott Carter, SD IOSW 		    "i_ddi_irm_reduce: policy reductions failed.\n"));
10355febcb4aSScott Carter, SD IOSW 
10365febcb4aSScott Carter, SD IOSW 		/* Compute remaining imbalance */
10375febcb4aSScott Carter, SD IOSW 		imbalance = pool_p->ipool_resno - pool_p->ipool_totsz;
10385febcb4aSScott Carter, SD IOSW 
10395febcb4aSScott Carter, SD IOSW 		ASSERT(imbalance > 0);
10405febcb4aSScott Carter, SD IOSW 
10415febcb4aSScott Carter, SD IOSW 		i_ddi_irm_reduce_new(pool_p, imbalance);
10425febcb4aSScott Carter, SD IOSW 	}
10435febcb4aSScott Carter, SD IOSW }
10445febcb4aSScott Carter, SD IOSW 
10455febcb4aSScott Carter, SD IOSW /*
10465febcb4aSScott Carter, SD IOSW  * i_ddi_irm_enqueue()
10475febcb4aSScott Carter, SD IOSW  *
10485febcb4aSScott Carter, SD IOSW  *	Queue a pool to be balanced.  Signals the balancing thread to wake
10495febcb4aSScott Carter, SD IOSW  *	up and process the pool.  If 'wait_flag' is true, then the current
10505febcb4aSScott Carter, SD IOSW  *	thread becomes a waiter and blocks until the balance is completed.
10515febcb4aSScott Carter, SD IOSW  */
10525febcb4aSScott Carter, SD IOSW static void
10535febcb4aSScott Carter, SD IOSW i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag)
10545febcb4aSScott Carter, SD IOSW {
10555febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
10565febcb4aSScott Carter, SD IOSW 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10575febcb4aSScott Carter, SD IOSW 
10585febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n",
10595febcb4aSScott Carter, SD IOSW 	    (void *)pool_p, (int)wait_flag));
10605febcb4aSScott Carter, SD IOSW 
10615febcb4aSScott Carter, SD IOSW 	/* Do nothing if pool is already balanced */
10625febcb4aSScott Carter, SD IOSW #ifndef	DEBUG
10635febcb4aSScott Carter, SD IOSW 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
10645febcb4aSScott Carter, SD IOSW #else
10655febcb4aSScott Carter, SD IOSW 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
10665febcb4aSScott Carter, SD IOSW #endif	/* DEBUG */
10675febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT,
10685febcb4aSScott Carter, SD IOSW 		    "i_ddi_irm_enqueue: pool already balanced\n"));
10695febcb4aSScott Carter, SD IOSW 		return;
10705febcb4aSScott Carter, SD IOSW 	}
10715febcb4aSScott Carter, SD IOSW 
10725febcb4aSScott Carter, SD IOSW 	/* Avoid deadlocks when IRM is not active */
10735febcb4aSScott Carter, SD IOSW 	if (!irm_active && wait_flag) {
10745febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT,
10755febcb4aSScott Carter, SD IOSW 		    "i_ddi_irm_enqueue: pool not active.\n"));
10765febcb4aSScott Carter, SD IOSW 		return;
10775febcb4aSScott Carter, SD IOSW 	}
10785febcb4aSScott Carter, SD IOSW 
10795febcb4aSScott Carter, SD IOSW 	if (wait_flag)
10805febcb4aSScott Carter, SD IOSW 		pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS;
10815febcb4aSScott Carter, SD IOSW 
10825febcb4aSScott Carter, SD IOSW 	if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) {
10835febcb4aSScott Carter, SD IOSW 		pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED;
10845febcb4aSScott Carter, SD IOSW 		cv_signal(&pool_p->ipool_cv);
10855febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n"));
10865febcb4aSScott Carter, SD IOSW 	}
10875febcb4aSScott Carter, SD IOSW 
10885febcb4aSScott Carter, SD IOSW 	if (wait_flag) {
10895febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n"));
10905febcb4aSScott Carter, SD IOSW 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
10915febcb4aSScott Carter, SD IOSW 	}
10925febcb4aSScott Carter, SD IOSW }
10935febcb4aSScott Carter, SD IOSW 
10945febcb4aSScott Carter, SD IOSW /*
10956a43bfaaSEvan Yan  * i_ddi_irm_reduce_by_policy()
10965febcb4aSScott Carter, SD IOSW  *
10976a43bfaaSEvan Yan  *	Reduces requests based on reduction policies.
10985febcb4aSScott Carter, SD IOSW  *
10996a43bfaaSEvan Yan  *	For the DDI_IRM_POLICY_LARGE reduction policy, the algorithm
11006a43bfaaSEvan Yan  *	generally reduces larger requests first, before advancing
11016a43bfaaSEvan Yan  *	to smaller requests.
11026a43bfaaSEvan Yan  *	For the DDI_IRM_POLICY_EVEN reduction policy, the algorithm
11036a43bfaaSEvan Yan  *	reduces requests evenly, without giving a specific preference
11046a43bfaaSEvan Yan  *	to smaller or larger requests. Each iteration reduces all
11056a43bfaaSEvan Yan  *	reducible requests by the same amount until the imbalance is
11066a43bfaaSEvan Yan  *	corrected.
11076a43bfaaSEvan Yan  *
11086a43bfaaSEvan Yan  *	The scratch list is initially sorted in descending order by current
11096a43bfaaSEvan Yan  *	navail values, which are maximized prior to reduction. This sorted
11106a43bfaaSEvan Yan  *	order is preserved.  It avoids reducing requests below the threshold
11116a43bfaaSEvan Yan  *	of the interrupt pool's default allocation size.
11125febcb4aSScott Carter, SD IOSW  *
11135febcb4aSScott Carter, SD IOSW  *	Optimizations in this algorithm include trying to reduce multiple
11146a43bfaaSEvan Yan  *	requests together.  And the algorithm attempts to reduce in larger
11156a43bfaaSEvan Yan  *	increments when possible to minimize the total number of iterations.
11165febcb4aSScott Carter, SD IOSW  */
11175febcb4aSScott Carter, SD IOSW static int
11186a43bfaaSEvan Yan i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *pool_p, int imbalance, int policy)
11195febcb4aSScott Carter, SD IOSW {
11205febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
11215febcb4aSScott Carter, SD IOSW 	ASSERT(imbalance > 0);
11225febcb4aSScott Carter, SD IOSW 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
11235febcb4aSScott Carter, SD IOSW 
11245febcb4aSScott Carter, SD IOSW 	while (imbalance > 0) {
11256a43bfaaSEvan Yan 		list_t		*slist_p = &pool_p->ipool_scratch_list;
11266a43bfaaSEvan Yan 		ddi_irm_req_t	*req_p = list_head(slist_p), *last_p;
11276a43bfaaSEvan Yan 		uint_t		nreduce = 0, nremain = 0, stop_navail;
11286a43bfaaSEvan Yan 		uint_t		pool_defsz = pool_p->ipool_defsz;
11296a43bfaaSEvan Yan 		uint_t		reduction, max_redu;
11305febcb4aSScott Carter, SD IOSW 
11316a43bfaaSEvan Yan 		/* Fail if none are reducible */
11326a43bfaaSEvan Yan 		if (!req_p || req_p->ireq_navail <= pool_defsz) {
11335febcb4aSScott Carter, SD IOSW 			DDI_INTR_IRMDBG((CE_CONT,
11346a43bfaaSEvan Yan 			    "i_ddi_irm_reduce_by_policy: Failure. "
113563ea9ad2SEvan Yan 			    "All requests have downsized to low limit.\n"));
11365febcb4aSScott Carter, SD IOSW 			return (DDI_FAILURE);
11375febcb4aSScott Carter, SD IOSW 		}
11385febcb4aSScott Carter, SD IOSW 
11395febcb4aSScott Carter, SD IOSW 		/* Count reducible requests */
11406a43bfaaSEvan Yan 		stop_navail = (policy == DDI_IRM_POLICY_LARGE) ?
11416a43bfaaSEvan Yan 		    req_p->ireq_navail - 1 : pool_defsz;
11426a43bfaaSEvan Yan 		for (; req_p; req_p = list_next(slist_p, req_p)) {
11436a43bfaaSEvan Yan 			if (req_p->ireq_navail <= stop_navail)
11445febcb4aSScott Carter, SD IOSW 				break;
11455febcb4aSScott Carter, SD IOSW 			nreduce++;
11465febcb4aSScott Carter, SD IOSW 		}
11475febcb4aSScott Carter, SD IOSW 
11485febcb4aSScott Carter, SD IOSW 		/* Compute reduction */
11496a43bfaaSEvan Yan 		last_p = req_p ? list_prev(slist_p, req_p) : list_tail(slist_p);
11506a43bfaaSEvan Yan 		if ((policy == DDI_IRM_POLICY_LARGE) && req_p &&
11516a43bfaaSEvan Yan 		    req_p->ireq_navail > pool_defsz)
11526a43bfaaSEvan Yan 			reduction = last_p->ireq_navail - req_p->ireq_navail;
11536a43bfaaSEvan Yan 		else
11546a43bfaaSEvan Yan 			reduction = last_p->ireq_navail - pool_defsz;
11556a43bfaaSEvan Yan 
11566a43bfaaSEvan Yan 		if ((max_redu = reduction * nreduce) > imbalance) {
11575febcb4aSScott Carter, SD IOSW 			reduction = imbalance / nreduce;
11586a43bfaaSEvan Yan 			nremain = imbalance % nreduce;
11596a43bfaaSEvan Yan 			pool_p->ipool_resno -= imbalance;
11606a43bfaaSEvan Yan 			imbalance = 0;
11615febcb4aSScott Carter, SD IOSW 		} else {
11626a43bfaaSEvan Yan 			pool_p->ipool_resno -= max_redu;
11636a43bfaaSEvan Yan 			imbalance -= max_redu;
11645febcb4aSScott Carter, SD IOSW 		}
11655febcb4aSScott Carter, SD IOSW 
11666a43bfaaSEvan Yan 		/* Reduce */
11676a43bfaaSEvan Yan 		for (req_p = list_head(slist_p); (reduction != 0) && nreduce--;
11686a43bfaaSEvan Yan 		    req_p = list_next(slist_p, req_p)) {
11695febcb4aSScott Carter, SD IOSW 			req_p->ireq_navail -= reduction;
11706a43bfaaSEvan Yan 		}
11716a43bfaaSEvan Yan 
11726a43bfaaSEvan Yan 		for (req_p = last_p; nremain--;
11736a43bfaaSEvan Yan 		    req_p = list_prev(slist_p, req_p)) {
11746a43bfaaSEvan Yan 			req_p->ireq_navail--;
11755febcb4aSScott Carter, SD IOSW 		}
11765febcb4aSScott Carter, SD IOSW 	}
11775febcb4aSScott Carter, SD IOSW 
11785febcb4aSScott Carter, SD IOSW 	return (DDI_SUCCESS);
11795febcb4aSScott Carter, SD IOSW }
11805febcb4aSScott Carter, SD IOSW 
11815febcb4aSScott Carter, SD IOSW /*
11825febcb4aSScott Carter, SD IOSW  * i_ddi_irm_reduce_new()
11835febcb4aSScott Carter, SD IOSW  *
118463ea9ad2SEvan Yan  *	Reduces new requests.  This is only used as a last resort
118563ea9ad2SEvan Yan  *	after another reduction algorithm failed.
11866a43bfaaSEvan Yan  *
11876a43bfaaSEvan Yan  *	NOTE: The pool locking in i_ddi_irm_insert() ensures
11886a43bfaaSEvan Yan  *	there can be only one new request at a time in a pool.
11895febcb4aSScott Carter, SD IOSW  */
11905febcb4aSScott Carter, SD IOSW static void
11915febcb4aSScott Carter, SD IOSW i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance)
11925febcb4aSScott Carter, SD IOSW {
11935febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*req_p;
11945febcb4aSScott Carter, SD IOSW 
11955febcb4aSScott Carter, SD IOSW 	ASSERT(pool_p != NULL);
11965febcb4aSScott Carter, SD IOSW 	ASSERT(imbalance > 0);
11975febcb4aSScott Carter, SD IOSW 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
11985febcb4aSScott Carter, SD IOSW 
11996a43bfaaSEvan Yan 	DDI_INTR_IRMDBG((CE_CONT,
12006a43bfaaSEvan Yan 	    "i_ddi_irm_reduce_new: pool_p %p imbalance %d\n",
12016a43bfaaSEvan Yan 	    (void *)pool_p, imbalance));
120263ea9ad2SEvan Yan 
12036a43bfaaSEvan Yan 	for (req_p = list_head(&pool_p->ipool_scratch_list); req_p;
120463ea9ad2SEvan Yan 	    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
12055febcb4aSScott Carter, SD IOSW 		if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) {
12066a43bfaaSEvan Yan 			ASSERT(req_p->ireq_navail >= imbalance);
12076a43bfaaSEvan Yan 			req_p->ireq_navail -= imbalance;
12086a43bfaaSEvan Yan 			pool_p->ipool_resno -= imbalance;
12096a43bfaaSEvan Yan 			return;
12105febcb4aSScott Carter, SD IOSW 		}
12115febcb4aSScott Carter, SD IOSW 	}
12126a43bfaaSEvan Yan 
12136a43bfaaSEvan Yan 	/* should never go here */
12146a43bfaaSEvan Yan 	ASSERT(B_FALSE);
12155febcb4aSScott Carter, SD IOSW }
12165febcb4aSScott Carter, SD IOSW 
12175febcb4aSScott Carter, SD IOSW /*
12185febcb4aSScott Carter, SD IOSW  * Miscellaneous Helper Functions
12195febcb4aSScott Carter, SD IOSW  */
12205febcb4aSScott Carter, SD IOSW 
12215febcb4aSScott Carter, SD IOSW /*
12225febcb4aSScott Carter, SD IOSW  * i_ddi_intr_get_pool()
12235febcb4aSScott Carter, SD IOSW  *
12245febcb4aSScott Carter, SD IOSW  *	Get an IRM pool that supplies interrupts of a specified type.
12255febcb4aSScott Carter, SD IOSW  *	Invokes a DDI_INTROP_GETPOOL to the bus nexus driver.  Fails
12265febcb4aSScott Carter, SD IOSW  *	if no pool exists.
12275febcb4aSScott Carter, SD IOSW  */
12285febcb4aSScott Carter, SD IOSW ddi_irm_pool_t *
12295febcb4aSScott Carter, SD IOSW i_ddi_intr_get_pool(dev_info_t *dip, int type)
12305febcb4aSScott Carter, SD IOSW {
12315febcb4aSScott Carter, SD IOSW 	devinfo_intr_t		*intr_p;
12325febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t		*pool_p;
12335febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t		*req_p;
12345febcb4aSScott Carter, SD IOSW 	ddi_intr_handle_impl_t	hdl;
12355febcb4aSScott Carter, SD IOSW 
12365febcb4aSScott Carter, SD IOSW 	ASSERT(dip != NULL);
12375febcb4aSScott Carter, SD IOSW 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
12385febcb4aSScott Carter, SD IOSW 
12395febcb4aSScott Carter, SD IOSW 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
12405febcb4aSScott Carter, SD IOSW 	    ((req_p = intr_p->devi_irm_req_p) != NULL) &&
12415febcb4aSScott Carter, SD IOSW 	    ((pool_p = req_p->ireq_pool_p) != NULL) &&
12425febcb4aSScott Carter, SD IOSW 	    (pool_p->ipool_types & type)) {
12435febcb4aSScott Carter, SD IOSW 		return (pool_p);
12445febcb4aSScott Carter, SD IOSW 	}
12455febcb4aSScott Carter, SD IOSW 
12465febcb4aSScott Carter, SD IOSW 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
12475febcb4aSScott Carter, SD IOSW 	hdl.ih_dip = dip;
12485febcb4aSScott Carter, SD IOSW 	hdl.ih_type = type;
12495febcb4aSScott Carter, SD IOSW 
12505febcb4aSScott Carter, SD IOSW 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL,
12515febcb4aSScott Carter, SD IOSW 	    &hdl, (void *)&pool_p) == DDI_SUCCESS)
12525febcb4aSScott Carter, SD IOSW 		return (pool_p);
12535febcb4aSScott Carter, SD IOSW 
12545febcb4aSScott Carter, SD IOSW 	return (NULL);
12555febcb4aSScott Carter, SD IOSW }
12565febcb4aSScott Carter, SD IOSW 
12575febcb4aSScott Carter, SD IOSW /*
12585febcb4aSScott Carter, SD IOSW  * i_ddi_irm_insertion_sort()
12595febcb4aSScott Carter, SD IOSW  *
12605febcb4aSScott Carter, SD IOSW  *	Use the insertion sort method to insert a request into a list.
12615febcb4aSScott Carter, SD IOSW  *	The list is sorted in descending order by request size.
12625febcb4aSScott Carter, SD IOSW  */
12635febcb4aSScott Carter, SD IOSW static void
12645febcb4aSScott Carter, SD IOSW i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p)
12655febcb4aSScott Carter, SD IOSW {
12665febcb4aSScott Carter, SD IOSW 	ddi_irm_req_t	*next_p;
12675febcb4aSScott Carter, SD IOSW 
12685febcb4aSScott Carter, SD IOSW 	next_p = list_head(req_list);
12695febcb4aSScott Carter, SD IOSW 
12705febcb4aSScott Carter, SD IOSW 	while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq))
12715febcb4aSScott Carter, SD IOSW 		next_p = list_next(req_list, next_p);
12725febcb4aSScott Carter, SD IOSW 
12735febcb4aSScott Carter, SD IOSW 	list_insert_before(req_list, next_p, req_p);
12745febcb4aSScott Carter, SD IOSW }
12755febcb4aSScott Carter, SD IOSW 
12765febcb4aSScott Carter, SD IOSW /*
12775febcb4aSScott Carter, SD IOSW  * i_ddi_irm_notify()
12785febcb4aSScott Carter, SD IOSW  *
12795febcb4aSScott Carter, SD IOSW  *	Notify a driver of changes to its interrupt request using the
12805febcb4aSScott Carter, SD IOSW  *	generic callback mechanism.  Checks for errors in processing.
12815febcb4aSScott Carter, SD IOSW  */
12825febcb4aSScott Carter, SD IOSW static int
12835febcb4aSScott Carter, SD IOSW i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
12845febcb4aSScott Carter, SD IOSW {
12855febcb4aSScott Carter, SD IOSW 	ddi_cb_action_t	action;
12865febcb4aSScott Carter, SD IOSW 	ddi_cb_t	*cb_p;
12875febcb4aSScott Carter, SD IOSW 	uint_t		nintrs;
12885febcb4aSScott Carter, SD IOSW 	int		ret, count;
12895febcb4aSScott Carter, SD IOSW 
12905febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n",
12915febcb4aSScott Carter, SD IOSW 	    (void *)pool_p, (void *)req_p));
12925febcb4aSScott Carter, SD IOSW 
12935febcb4aSScott Carter, SD IOSW 	/* Do not notify new or unchanged requests */
12945febcb4aSScott Carter, SD IOSW 	if ((req_p->ireq_navail == req_p->ireq_scratch) ||
12955febcb4aSScott Carter, SD IOSW 	    (req_p->ireq_flags & DDI_IRM_FLAG_NEW))
12965febcb4aSScott Carter, SD IOSW 		return (DDI_SUCCESS);
12975febcb4aSScott Carter, SD IOSW 
12985febcb4aSScott Carter, SD IOSW 	/* Determine action and count */
12995febcb4aSScott Carter, SD IOSW 	if (req_p->ireq_navail > req_p->ireq_scratch) {
13005febcb4aSScott Carter, SD IOSW 		action = DDI_CB_INTR_ADD;
13015febcb4aSScott Carter, SD IOSW 		count = req_p->ireq_navail - req_p->ireq_scratch;
13025febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n",
13035febcb4aSScott Carter, SD IOSW 		    count));
13045febcb4aSScott Carter, SD IOSW 	} else {
13055febcb4aSScott Carter, SD IOSW 		action = DDI_CB_INTR_REMOVE;
13065febcb4aSScott Carter, SD IOSW 		count = req_p->ireq_scratch - req_p->ireq_navail;
13075febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n",
13085febcb4aSScott Carter, SD IOSW 		    count));
13095febcb4aSScott Carter, SD IOSW 	}
13105febcb4aSScott Carter, SD IOSW 
13115febcb4aSScott Carter, SD IOSW 	/* Lookup driver callback */
13125febcb4aSScott Carter, SD IOSW 	if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) {
13135febcb4aSScott Carter, SD IOSW 		DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n"));
13145febcb4aSScott Carter, SD IOSW 		return (DDI_FAILURE);
13155febcb4aSScott Carter, SD IOSW 	}
13165febcb4aSScott Carter, SD IOSW 
13175febcb4aSScott Carter, SD IOSW 	/* Do callback */
13185febcb4aSScott Carter, SD IOSW 	ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count,
13195febcb4aSScott Carter, SD IOSW 	    cb_p->cb_arg1, cb_p->cb_arg2);
13205febcb4aSScott Carter, SD IOSW 
13215febcb4aSScott Carter, SD IOSW 	/* Log callback errors */
13225febcb4aSScott Carter, SD IOSW 	if (ret != DDI_SUCCESS) {
13235febcb4aSScott Carter, SD IOSW 		cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
13245febcb4aSScott Carter, SD IOSW 		    ddi_driver_name(req_p->ireq_dip),
13255febcb4aSScott Carter, SD IOSW 		    ddi_get_instance(req_p->ireq_dip), (int)action, ret);
13265febcb4aSScott Carter, SD IOSW 	}
13275febcb4aSScott Carter, SD IOSW 
13285febcb4aSScott Carter, SD IOSW 	/* Check if the driver exceeds its availability */
13295febcb4aSScott Carter, SD IOSW 	nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip);
13305febcb4aSScott Carter, SD IOSW 	if (nintrs > req_p->ireq_navail) {
13315febcb4aSScott Carter, SD IOSW 		cmn_err(CE_WARN, "%s%d: failed to release interrupts "
13325febcb4aSScott Carter, SD IOSW 		    "(nintrs=%d, navail=%d).\n",
13335febcb4aSScott Carter, SD IOSW 		    ddi_driver_name(req_p->ireq_dip),
13345febcb4aSScott Carter, SD IOSW 		    ddi_get_instance(req_p->ireq_dip), nintrs,
13355febcb4aSScott Carter, SD IOSW 		    req_p->ireq_navail);
13365febcb4aSScott Carter, SD IOSW 		pool_p->ipool_resno += (nintrs - req_p->ireq_navail);
13375febcb4aSScott Carter, SD IOSW 		req_p->ireq_navail = nintrs;
13385febcb4aSScott Carter, SD IOSW 		return (DDI_FAILURE);
13395febcb4aSScott Carter, SD IOSW 	}
13405febcb4aSScott Carter, SD IOSW 
13415febcb4aSScott Carter, SD IOSW 	/* Update request */
13425febcb4aSScott Carter, SD IOSW 	req_p->ireq_scratch = req_p->ireq_navail;
13435febcb4aSScott Carter, SD IOSW 
13445febcb4aSScott Carter, SD IOSW 	return (DDI_SUCCESS);
13455febcb4aSScott Carter, SD IOSW }
13465febcb4aSScott Carter, SD IOSW 
13475febcb4aSScott Carter, SD IOSW /*
13485febcb4aSScott Carter, SD IOSW  * i_ddi_irm_debug_balance()
13495febcb4aSScott Carter, SD IOSW  *
13505febcb4aSScott Carter, SD IOSW  *	A debug/test only routine to force the immediate,
13515febcb4aSScott Carter, SD IOSW  *	synchronous rebalancing of an interrupt pool.
13525febcb4aSScott Carter, SD IOSW  */
13535febcb4aSScott Carter, SD IOSW #ifdef	DEBUG
13545febcb4aSScott Carter, SD IOSW void
13555febcb4aSScott Carter, SD IOSW i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag)
13565febcb4aSScott Carter, SD IOSW {
13575febcb4aSScott Carter, SD IOSW 	ddi_irm_pool_t	*pool_p;
13585febcb4aSScott Carter, SD IOSW 	int		type;
13595febcb4aSScott Carter, SD IOSW 
13605febcb4aSScott Carter, SD IOSW 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n",
13615febcb4aSScott Carter, SD IOSW 	    (void *)dip, (int)wait_flag));
13625febcb4aSScott Carter, SD IOSW 
13635febcb4aSScott Carter, SD IOSW 	if (((type = i_ddi_intr_get_current_type(dip)) != 0) &&
13645febcb4aSScott Carter, SD IOSW 	    ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) {
13655febcb4aSScott Carter, SD IOSW 		mutex_enter(&pool_p->ipool_lock);
13665febcb4aSScott Carter, SD IOSW 		i_ddi_irm_enqueue(pool_p, wait_flag);
13675febcb4aSScott Carter, SD IOSW 		mutex_exit(&pool_p->ipool_lock);
13685febcb4aSScott Carter, SD IOSW 	}
13695febcb4aSScott Carter, SD IOSW }
13705febcb4aSScott Carter, SD IOSW #endif
1371