xref: /onnv-gate/usr/src/uts/common/os/ddi_intr_irm.c (revision 12745:74161c36ea70)
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 /*
2212473SScott.Carter@Oracle.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
238561SScott.Carter@Sun.COM  */
248561SScott.Carter@Sun.COM 
258561SScott.Carter@Sun.COM #include <sys/note.h>
268561SScott.Carter@Sun.COM #include <sys/sysmacros.h>
278561SScott.Carter@Sun.COM #include <sys/types.h>
288561SScott.Carter@Sun.COM #include <sys/param.h>
298561SScott.Carter@Sun.COM #include <sys/systm.h>
308561SScott.Carter@Sun.COM #include <sys/kmem.h>
318561SScott.Carter@Sun.COM #include <sys/cmn_err.h>
328561SScott.Carter@Sun.COM #include <sys/debug.h>
338561SScott.Carter@Sun.COM #include <sys/ddi.h>
348561SScott.Carter@Sun.COM #include <sys/sunndi.h>
358561SScott.Carter@Sun.COM #include <sys/ndi_impldefs.h>	/* include prototypes */
368561SScott.Carter@Sun.COM 
3712683SJimmy.Vetayases@oracle.com #if defined(__i386) || defined(__amd64)
3812683SJimmy.Vetayases@oracle.com /*
3912683SJimmy.Vetayases@oracle.com  * MSI-X allocation limit.
4012683SJimmy.Vetayases@oracle.com  */
4112683SJimmy.Vetayases@oracle.com extern uint_t		ddi_msix_alloc_limit;
4212683SJimmy.Vetayases@oracle.com #endif
4312683SJimmy.Vetayases@oracle.com 
448561SScott.Carter@Sun.COM /*
458561SScott.Carter@Sun.COM  * Interrupt Resource Management (IRM).
468561SScott.Carter@Sun.COM  */
478561SScott.Carter@Sun.COM 
488561SScott.Carter@Sun.COM #define	DDI_IRM_BALANCE_DELAY	(60)	/* In seconds */
498561SScott.Carter@Sun.COM 
508561SScott.Carter@Sun.COM #define	DDI_IRM_HAS_CB(c)	((c) && (c->cb_flags & DDI_CB_FLAG_INTR))
518561SScott.Carter@Sun.COM 
528561SScott.Carter@Sun.COM #define	DDI_IRM_IS_REDUCIBLE(r)	(((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \
538561SScott.Carter@Sun.COM 				(r->ireq_type == DDI_INTR_TYPE_MSIX)) || \
548561SScott.Carter@Sun.COM 				(r->ireq_flags & DDI_IRM_FLAG_NEW))
558561SScott.Carter@Sun.COM 
568561SScott.Carter@Sun.COM extern pri_t	minclsyspri;
578561SScott.Carter@Sun.COM 
588561SScott.Carter@Sun.COM /* Global policies */
598561SScott.Carter@Sun.COM int		irm_enable = 1;
608561SScott.Carter@Sun.COM boolean_t	irm_active = B_FALSE;
618561SScott.Carter@Sun.COM int		irm_default_policy = DDI_IRM_POLICY_LARGE;
628561SScott.Carter@Sun.COM uint_t		irm_balance_delay = DDI_IRM_BALANCE_DELAY;
638561SScott.Carter@Sun.COM 
648561SScott.Carter@Sun.COM /* Global list of interrupt pools */
658561SScott.Carter@Sun.COM kmutex_t	irm_pools_lock;
668561SScott.Carter@Sun.COM list_t		irm_pools_list;
678561SScott.Carter@Sun.COM 
688561SScott.Carter@Sun.COM /* Global debug tunables */
698561SScott.Carter@Sun.COM #ifdef	DEBUG
708561SScott.Carter@Sun.COM int		irm_debug_policy = 0;
718561SScott.Carter@Sun.COM uint_t		irm_debug_size = 0;
728561SScott.Carter@Sun.COM #endif	/* DEBUG */
738561SScott.Carter@Sun.COM 
748561SScott.Carter@Sun.COM static void	irm_balance_thread(ddi_irm_pool_t *);
758561SScott.Carter@Sun.COM static void	i_ddi_irm_balance(ddi_irm_pool_t *);
768561SScott.Carter@Sun.COM static void	i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t);
778561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce(ddi_irm_pool_t *pool);
7810173SEvan.Yan@Sun.COM static int	i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *, int, int);
798561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce_new(ddi_irm_pool_t *, int);
808561SScott.Carter@Sun.COM static void	i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *);
818561SScott.Carter@Sun.COM static int	i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *);
8212473SScott.Carter@Oracle.COM static int	i_ddi_irm_modify_increase(ddi_irm_req_t *, int);
838561SScott.Carter@Sun.COM 
848561SScott.Carter@Sun.COM /*
858561SScott.Carter@Sun.COM  * OS Initialization Routines
868561SScott.Carter@Sun.COM  */
878561SScott.Carter@Sun.COM 
888561SScott.Carter@Sun.COM /*
898561SScott.Carter@Sun.COM  * irm_init()
908561SScott.Carter@Sun.COM  *
918561SScott.Carter@Sun.COM  *	Initialize IRM subsystem before any drivers are attached.
928561SScott.Carter@Sun.COM  */
938561SScott.Carter@Sun.COM void
irm_init(void)948561SScott.Carter@Sun.COM irm_init(void)
958561SScott.Carter@Sun.COM {
968561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
978561SScott.Carter@Sun.COM 	if (!irm_enable)
988561SScott.Carter@Sun.COM 		return;
998561SScott.Carter@Sun.COM 
1008561SScott.Carter@Sun.COM 	/* Verify that the default balancing policy is valid */
1018561SScott.Carter@Sun.COM 	if (!DDI_IRM_POLICY_VALID(irm_default_policy))
1028561SScott.Carter@Sun.COM 		irm_default_policy = DDI_IRM_POLICY_LARGE;
1038561SScott.Carter@Sun.COM 
1048561SScott.Carter@Sun.COM 	/* Initialize the global list of interrupt pools */
1058561SScott.Carter@Sun.COM 	mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL);
1068561SScott.Carter@Sun.COM 	list_create(&irm_pools_list, sizeof (ddi_irm_pool_t),
1078561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_pool_t, ipool_link));
1088561SScott.Carter@Sun.COM }
1098561SScott.Carter@Sun.COM 
1108561SScott.Carter@Sun.COM /*
1118561SScott.Carter@Sun.COM  * i_ddi_irm_poststartup()
1128561SScott.Carter@Sun.COM  *
1138561SScott.Carter@Sun.COM  *	IRM is not activated until after the IO subsystem is initialized.
1148561SScott.Carter@Sun.COM  *	When activated, per-pool balancing threads are spawned and a flag
1158561SScott.Carter@Sun.COM  *	is set so that all future pools will be activated when created.
1168561SScott.Carter@Sun.COM  *
1178561SScott.Carter@Sun.COM  *	NOTE: the global variable 'irm_enable' disables IRM if zero.
1188561SScott.Carter@Sun.COM  */
1198561SScott.Carter@Sun.COM void
i_ddi_irm_poststartup(void)1208561SScott.Carter@Sun.COM i_ddi_irm_poststartup(void)
1218561SScott.Carter@Sun.COM {
1228561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1238561SScott.Carter@Sun.COM 
1248561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
1258561SScott.Carter@Sun.COM 	if (!irm_enable)
1268561SScott.Carter@Sun.COM 		return;
1278561SScott.Carter@Sun.COM 
1288561SScott.Carter@Sun.COM 	/* Lock the global list */
1298561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1308561SScott.Carter@Sun.COM 
1318561SScott.Carter@Sun.COM 	/* Activate all defined pools */
1328561SScott.Carter@Sun.COM 	for (pool_p = list_head(&irm_pools_list); pool_p;
1338561SScott.Carter@Sun.COM 	    pool_p = list_next(&irm_pools_list, pool_p))
1348561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
1358561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1368561SScott.Carter@Sun.COM 
1378561SScott.Carter@Sun.COM 	/* Set future pools to be active */
1388561SScott.Carter@Sun.COM 	irm_active = B_TRUE;
1398561SScott.Carter@Sun.COM 
1408561SScott.Carter@Sun.COM 	/* Unlock the global list */
1418561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1428561SScott.Carter@Sun.COM }
1438561SScott.Carter@Sun.COM 
1448561SScott.Carter@Sun.COM /*
1458561SScott.Carter@Sun.COM  * NDI interfaces for creating/destroying IRM pools.
1468561SScott.Carter@Sun.COM  */
1478561SScott.Carter@Sun.COM 
1488561SScott.Carter@Sun.COM /*
1498561SScott.Carter@Sun.COM  * ndi_irm_create()
1508561SScott.Carter@Sun.COM  *
1518561SScott.Carter@Sun.COM  *	Nexus interface to create an IRM pool.  Create the new
1528561SScott.Carter@Sun.COM  *	pool and add it to the global list of interrupt pools.
1538561SScott.Carter@Sun.COM  */
1548561SScott.Carter@Sun.COM int
ndi_irm_create(dev_info_t * dip,ddi_irm_params_t * paramsp,ddi_irm_pool_t ** pool_retp)1558561SScott.Carter@Sun.COM ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp,
1568561SScott.Carter@Sun.COM     ddi_irm_pool_t **pool_retp)
1578561SScott.Carter@Sun.COM {
1588561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1598561SScott.Carter@Sun.COM 
1608561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
1618561SScott.Carter@Sun.COM 	ASSERT(paramsp != NULL);
1628561SScott.Carter@Sun.COM 	ASSERT(pool_retp != NULL);
1638561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_total >= 1);
1648561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_types != 0);
1658561SScott.Carter@Sun.COM 
1668561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip));
1678561SScott.Carter@Sun.COM 
1688561SScott.Carter@Sun.COM 	/* Check if IRM is enabled */
1698561SScott.Carter@Sun.COM 	if (!irm_enable)
1708561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1718561SScott.Carter@Sun.COM 
1728561SScott.Carter@Sun.COM 	/* Validate parameters */
1738561SScott.Carter@Sun.COM 	if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) ||
1748925SEvan.Yan@Sun.COM 	    (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0))
1758561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1768561SScott.Carter@Sun.COM 
1778561SScott.Carter@Sun.COM 	/* Allocate and initialize the pool */
1788561SScott.Carter@Sun.COM 	pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP);
1798561SScott.Carter@Sun.COM 	pool_p->ipool_owner = dip;
1808561SScott.Carter@Sun.COM 	pool_p->ipool_policy = irm_default_policy;
1818561SScott.Carter@Sun.COM 	pool_p->ipool_types = paramsp->iparams_types;
1828561SScott.Carter@Sun.COM 	pool_p->ipool_totsz = paramsp->iparams_total;
1838925SEvan.Yan@Sun.COM 	pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC,
1848925SEvan.Yan@Sun.COM 	    paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER));
1858561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t),
1868561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_link));
1878561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t),
1888561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_scratch_link));
1898561SScott.Carter@Sun.COM 	cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL);
1908561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL);
1918561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL);
1928561SScott.Carter@Sun.COM 
1938561SScott.Carter@Sun.COM 	/* Add to global list of pools */
1948561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1958561SScott.Carter@Sun.COM 	list_insert_tail(&irm_pools_list, pool_p);
1968561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1978561SScott.Carter@Sun.COM 
1988561SScott.Carter@Sun.COM 	/* If IRM is active, then activate the pool */
1998561SScott.Carter@Sun.COM 	if (irm_active)
2008561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
2018561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
2028561SScott.Carter@Sun.COM 
2038561SScott.Carter@Sun.COM 	*pool_retp = pool_p;
2048561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
2058561SScott.Carter@Sun.COM }
2068561SScott.Carter@Sun.COM 
2078561SScott.Carter@Sun.COM /*
20812683SJimmy.Vetayases@oracle.com  * ndi_irm_resize_pool()
20912683SJimmy.Vetayases@oracle.com  *
21012683SJimmy.Vetayases@oracle.com  *	Nexus interface to resize IRM pool. If the pool size drops
21112683SJimmy.Vetayases@oracle.com  *	below  the allocated number of vectors then initiate rebalance
21212683SJimmy.Vetayases@oracle.com  *	operation before resizing the pool. If rebalance operation fails
21312683SJimmy.Vetayases@oracle.com  *	then return NDI_FAILURE.
21412683SJimmy.Vetayases@oracle.com  */
21512683SJimmy.Vetayases@oracle.com int
ndi_irm_resize_pool(ddi_irm_pool_t * pool_p,uint_t new_size)21612683SJimmy.Vetayases@oracle.com ndi_irm_resize_pool(ddi_irm_pool_t *pool_p, uint_t new_size)
21712683SJimmy.Vetayases@oracle.com {
21812683SJimmy.Vetayases@oracle.com 	uint_t prev_size;
21912683SJimmy.Vetayases@oracle.com 
22012683SJimmy.Vetayases@oracle.com 	ASSERT(pool_p != NULL);
22112683SJimmy.Vetayases@oracle.com 
22212683SJimmy.Vetayases@oracle.com 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
22312683SJimmy.Vetayases@oracle.com 	    " current-size 0x%x new-size 0x%x\n",
22412683SJimmy.Vetayases@oracle.com 	    (void *)pool_p, pool_p->ipool_totsz, new_size));
22512683SJimmy.Vetayases@oracle.com 
22612683SJimmy.Vetayases@oracle.com 	if (pool_p == NULL)
22712683SJimmy.Vetayases@oracle.com 		return (NDI_EINVAL);
22812683SJimmy.Vetayases@oracle.com 
22912683SJimmy.Vetayases@oracle.com 	/* Check if IRM is enabled */
23012683SJimmy.Vetayases@oracle.com 	if (!irm_enable)
23112683SJimmy.Vetayases@oracle.com 		return (NDI_FAILURE);
23212683SJimmy.Vetayases@oracle.com 
23312683SJimmy.Vetayases@oracle.com 	mutex_enter(&pool_p->ipool_lock);
23412683SJimmy.Vetayases@oracle.com 
23512683SJimmy.Vetayases@oracle.com 	/*
23612683SJimmy.Vetayases@oracle.com 	 * If we are increasing the pool size or if the reserved
23712683SJimmy.Vetayases@oracle.com 	 * number of vectors is <= the new pool size then simply
23812683SJimmy.Vetayases@oracle.com 	 * update the pool size and enqueue a reblance operation
23912683SJimmy.Vetayases@oracle.com 	 * if necessary to use the new vectors.
24012683SJimmy.Vetayases@oracle.com 	 */
24112683SJimmy.Vetayases@oracle.com 	if ((pool_p->ipool_totsz < new_size) ||
24212683SJimmy.Vetayases@oracle.com 	    (pool_p->ipool_resno <= new_size)) {
24312683SJimmy.Vetayases@oracle.com 		/* set new pool size */
24412683SJimmy.Vetayases@oracle.com 		pool_p->ipool_totsz = new_size;
24512683SJimmy.Vetayases@oracle.com 		/* adjust the default allocation limit */
24612683SJimmy.Vetayases@oracle.com 		pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC,
24712683SJimmy.Vetayases@oracle.com 		    MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER));
24812683SJimmy.Vetayases@oracle.com 		/* queue a rebalance operation to use the new vectors */
24912683SJimmy.Vetayases@oracle.com 		if (pool_p->ipool_reqno > pool_p->ipool_resno)
25012683SJimmy.Vetayases@oracle.com 			i_ddi_irm_enqueue(pool_p, B_FALSE);
25112683SJimmy.Vetayases@oracle.com 		mutex_exit(&pool_p->ipool_lock);
25212683SJimmy.Vetayases@oracle.com 		return (NDI_SUCCESS);
25312683SJimmy.Vetayases@oracle.com 	}
25412683SJimmy.Vetayases@oracle.com 
25512683SJimmy.Vetayases@oracle.com 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
25612683SJimmy.Vetayases@oracle.com 	    " needs a rebalance operation\n", (void *)pool_p));
25712683SJimmy.Vetayases@oracle.com 
25812683SJimmy.Vetayases@oracle.com 	/*
25912683SJimmy.Vetayases@oracle.com 	 * requires a rebalance operation
26012683SJimmy.Vetayases@oracle.com 	 */
26112683SJimmy.Vetayases@oracle.com 	/* save the current pool size */
26212683SJimmy.Vetayases@oracle.com 	prev_size = pool_p->ipool_totsz;
26312683SJimmy.Vetayases@oracle.com 	/* set the pool size to the desired new value */
26412683SJimmy.Vetayases@oracle.com 	pool_p->ipool_totsz = new_size;
26512683SJimmy.Vetayases@oracle.com 	/* perform the rebalance operation */
26612683SJimmy.Vetayases@oracle.com 	i_ddi_irm_enqueue(pool_p, B_TRUE);
26712683SJimmy.Vetayases@oracle.com 
26812683SJimmy.Vetayases@oracle.com 	/*
26912683SJimmy.Vetayases@oracle.com 	 * If rebalance operation couldn't free up enough
27012683SJimmy.Vetayases@oracle.com 	 * vectors then fail the resize operation.
27112683SJimmy.Vetayases@oracle.com 	 */
27212683SJimmy.Vetayases@oracle.com 	if (pool_p->ipool_resno > new_size) { /* rebalance failed */
27312683SJimmy.Vetayases@oracle.com 		/* restore the pool size to the previous value */
27412683SJimmy.Vetayases@oracle.com 		pool_p->ipool_totsz = prev_size;
27512683SJimmy.Vetayases@oracle.com 		/* enqueue a rebalance operation for the original pool size */
27612683SJimmy.Vetayases@oracle.com 		i_ddi_irm_enqueue(pool_p, B_FALSE);
27712683SJimmy.Vetayases@oracle.com 		mutex_exit(&pool_p->ipool_lock);
27812683SJimmy.Vetayases@oracle.com 		return (NDI_FAILURE);
27912683SJimmy.Vetayases@oracle.com 	} else { /* rebalance worked */
28012683SJimmy.Vetayases@oracle.com 		/* adjust the default allocation limit */
28112683SJimmy.Vetayases@oracle.com 		pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC,
28212683SJimmy.Vetayases@oracle.com 		    MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER));
28312683SJimmy.Vetayases@oracle.com 		mutex_exit(&pool_p->ipool_lock);
28412683SJimmy.Vetayases@oracle.com 		DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p"
28512683SJimmy.Vetayases@oracle.com 		    " resized from %x to %x\n",
28612683SJimmy.Vetayases@oracle.com 		    (void *)pool_p, prev_size, pool_p->ipool_totsz));
28712683SJimmy.Vetayases@oracle.com 		return (NDI_SUCCESS);
28812683SJimmy.Vetayases@oracle.com 	}
28912683SJimmy.Vetayases@oracle.com }
29012683SJimmy.Vetayases@oracle.com 
29112683SJimmy.Vetayases@oracle.com /*
2928561SScott.Carter@Sun.COM  * ndi_irm_destroy()
2938561SScott.Carter@Sun.COM  *
2948561SScott.Carter@Sun.COM  *	Nexus interface to destroy an IRM pool.  Destroy the pool
2958561SScott.Carter@Sun.COM  *	and remove it from the global list of interrupt pools.
2968561SScott.Carter@Sun.COM  */
2978561SScott.Carter@Sun.COM int
ndi_irm_destroy(ddi_irm_pool_t * pool_p)2988561SScott.Carter@Sun.COM ndi_irm_destroy(ddi_irm_pool_t *pool_p)
2998561SScott.Carter@Sun.COM {
3008561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
3018561SScott.Carter@Sun.COM 	ASSERT(pool_p->ipool_resno == 0);
3028561SScott.Carter@Sun.COM 
3038561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n",
3048561SScott.Carter@Sun.COM 	    (void *)pool_p));
3058561SScott.Carter@Sun.COM 
3068561SScott.Carter@Sun.COM 	/* Validate parameters */
3078561SScott.Carter@Sun.COM 	if (pool_p == NULL)
3088561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
3098561SScott.Carter@Sun.COM 
3108561SScott.Carter@Sun.COM 	/* Validate that pool is empty */
3118561SScott.Carter@Sun.COM 	if (pool_p->ipool_resno != 0)
3128561SScott.Carter@Sun.COM 		return (NDI_BUSY);
3138561SScott.Carter@Sun.COM 
3148561SScott.Carter@Sun.COM 	/* Remove the pool from the global list */
3158561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
3168561SScott.Carter@Sun.COM 	list_remove(&irm_pools_list, pool_p);
3178561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
3188561SScott.Carter@Sun.COM 
3198561SScott.Carter@Sun.COM 	/* Terminate the balancing thread */
3208561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
3218561SScott.Carter@Sun.COM 	if (pool_p->ipool_thread &&
3228561SScott.Carter@Sun.COM 	    (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) {
3238561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT;
3248561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
3258885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3268561SScott.Carter@Sun.COM 		thread_join(pool_p->ipool_thread->t_did);
3278885SJustin.Frank@Sun.COM 	} else
3288885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3298561SScott.Carter@Sun.COM 
3308561SScott.Carter@Sun.COM 	/* Destroy the pool */
3318561SScott.Carter@Sun.COM 	cv_destroy(&pool_p->ipool_cv);
3328561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_lock);
3338561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_navail_lock);
3348561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_req_list);
3358561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_scratch_list);
3368561SScott.Carter@Sun.COM 	kmem_free(pool_p, sizeof (ddi_irm_pool_t));
3378561SScott.Carter@Sun.COM 
3388561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
3398561SScott.Carter@Sun.COM }
3408561SScott.Carter@Sun.COM 
3418561SScott.Carter@Sun.COM /*
3428561SScott.Carter@Sun.COM  * Insert/Modify/Remove Interrupt Requests
3438561SScott.Carter@Sun.COM  */
3448561SScott.Carter@Sun.COM 
3458561SScott.Carter@Sun.COM /*
3468561SScott.Carter@Sun.COM  * i_ddi_irm_insert()
3478561SScott.Carter@Sun.COM  *
3488561SScott.Carter@Sun.COM  *	Insert a new request into an interrupt pool, and balance the pool.
3498561SScott.Carter@Sun.COM  */
3508561SScott.Carter@Sun.COM int
i_ddi_irm_insert(dev_info_t * dip,int type,int count)3518561SScott.Carter@Sun.COM i_ddi_irm_insert(dev_info_t *dip, int type, int count)
3528561SScott.Carter@Sun.COM {
3538561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
3548561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
3558561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
3568561SScott.Carter@Sun.COM 	uint_t		nreq, nmin, npartial;
3578561SScott.Carter@Sun.COM 	boolean_t	irm_flag = B_FALSE;
3588561SScott.Carter@Sun.COM 
3598561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
3608561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
3618561SScott.Carter@Sun.COM 	ASSERT(count > 0);
3628561SScott.Carter@Sun.COM 
3638561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n",
3648561SScott.Carter@Sun.COM 	    (void *)dip, type, count));
3658561SScott.Carter@Sun.COM 
3668561SScott.Carter@Sun.COM 	/* Validate parameters */
3678561SScott.Carter@Sun.COM 	if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) {
3688561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n"));
3698561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
3708561SScott.Carter@Sun.COM 	}
3718561SScott.Carter@Sun.COM 
3728561SScott.Carter@Sun.COM 	/* Check for an existing request */
3738561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
3748561SScott.Carter@Sun.COM 	    (intr_p->devi_irm_req_p != NULL))
3758561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
3768561SScott.Carter@Sun.COM 
3778561SScott.Carter@Sun.COM 	/* Check for IRM support from the system */
3788561SScott.Carter@Sun.COM 	if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) {
3798561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n"));
3808561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
3818561SScott.Carter@Sun.COM 	}
3828561SScott.Carter@Sun.COM 
3838561SScott.Carter@Sun.COM 	/* Check for IRM support from the driver */
38412473SScott.Carter@Oracle.COM 	if (i_ddi_irm_supported(dip, type) == DDI_SUCCESS)
3858561SScott.Carter@Sun.COM 		irm_flag = B_TRUE;
3868561SScott.Carter@Sun.COM 
3878561SScott.Carter@Sun.COM 	/* Determine request size */
3889205SEvan.Yan@Sun.COM 	nreq = (irm_flag) ? count :
38912473SScott.Carter@Oracle.COM 	    MIN(count, i_ddi_intr_get_limit(dip, type, pool_p));
3908561SScott.Carter@Sun.COM 	nmin = (irm_flag) ? 1 : nreq;
3918561SScott.Carter@Sun.COM 	npartial = MIN(nreq, pool_p->ipool_defsz);
3928561SScott.Carter@Sun.COM 
3938561SScott.Carter@Sun.COM 	/* Allocate and initialize the request */
3948561SScott.Carter@Sun.COM 	req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP);
3958561SScott.Carter@Sun.COM 	req_p->ireq_type = type;
3968561SScott.Carter@Sun.COM 	req_p->ireq_dip = dip;
3978561SScott.Carter@Sun.COM 	req_p->ireq_pool_p = pool_p;
3988561SScott.Carter@Sun.COM 	req_p->ireq_nreq = nreq;
3998561SScott.Carter@Sun.COM 	req_p->ireq_flags = DDI_IRM_FLAG_NEW;
40012473SScott.Carter@Oracle.COM 	if (irm_flag)
4018561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
4028561SScott.Carter@Sun.COM 
4038561SScott.Carter@Sun.COM 	/* Lock the pool */
4048561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
4058561SScott.Carter@Sun.COM 
4068561SScott.Carter@Sun.COM 	/* Check for minimal fit before inserting */
4078561SScott.Carter@Sun.COM 	if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) {
4088561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
4098561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
4108561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
4118561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
4128561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
4138561SScott.Carter@Sun.COM 	}
4148561SScott.Carter@Sun.COM 
4158561SScott.Carter@Sun.COM 	/* Insert the request into the pool */
4168561SScott.Carter@Sun.COM 	pool_p->ipool_reqno += nreq;
4178561SScott.Carter@Sun.COM 	pool_p->ipool_minno += nmin;
4188561SScott.Carter@Sun.COM 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
4198561SScott.Carter@Sun.COM 
4208561SScott.Carter@Sun.COM 	/*
4218561SScott.Carter@Sun.COM 	 * Try to fulfill the request.
4228561SScott.Carter@Sun.COM 	 *
4238561SScott.Carter@Sun.COM 	 * If all the interrupts are available, and either the request
4248561SScott.Carter@Sun.COM 	 * is static or the pool is active, then just take them directly.
4258561SScott.Carter@Sun.COM 	 *
4268561SScott.Carter@Sun.COM 	 * If only some of the interrupts are available, and the request
4278561SScott.Carter@Sun.COM 	 * can receive future callbacks, then take some now but queue the
4288561SScott.Carter@Sun.COM 	 * pool to be rebalanced later.
4298561SScott.Carter@Sun.COM 	 *
4308561SScott.Carter@Sun.COM 	 * Otherwise, immediately rebalance the pool and wait.
4318561SScott.Carter@Sun.COM 	 */
4328561SScott.Carter@Sun.COM 	if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) &&
4338561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) {
4348561SScott.Carter@Sun.COM 
4358561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4368561SScott.Carter@Sun.COM 		    "request completely fulfilled.\n"));
4378561SScott.Carter@Sun.COM 		pool_p->ipool_resno += nreq;
4388561SScott.Carter@Sun.COM 		req_p->ireq_navail = nreq;
4398561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4408561SScott.Carter@Sun.COM 
4418561SScott.Carter@Sun.COM 	} else if (irm_flag &&
4428561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) {
4438561SScott.Carter@Sun.COM 
4448561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4458561SScott.Carter@Sun.COM 		    "request partially fulfilled.\n"));
4468561SScott.Carter@Sun.COM 		pool_p->ipool_resno += npartial;
4478561SScott.Carter@Sun.COM 		req_p->ireq_navail = npartial;
4488561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4498561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
4508561SScott.Carter@Sun.COM 
4518561SScott.Carter@Sun.COM 	} else {
4528561SScott.Carter@Sun.COM 
4538561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
4548561SScott.Carter@Sun.COM 		    "request needs immediate rebalance.\n"));
4558561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
4568561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
4578561SScott.Carter@Sun.COM 	}
4588561SScott.Carter@Sun.COM 
4598561SScott.Carter@Sun.COM 	/* Fail if the request cannot be fulfilled at all */
4608561SScott.Carter@Sun.COM 	if (req_p->ireq_navail == 0) {
4618561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
4628561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
4638561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= nreq;
4648561SScott.Carter@Sun.COM 		pool_p->ipool_minno -= nmin;
4658561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
46610173SEvan.Yan@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
4678561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
4688561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
4698561SScott.Carter@Sun.COM 	}
4708561SScott.Carter@Sun.COM 
4718561SScott.Carter@Sun.COM 	/* Unlock the pool */
4728561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
4738561SScott.Carter@Sun.COM 
4748561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = req_p;
4758561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
4768561SScott.Carter@Sun.COM }
4778561SScott.Carter@Sun.COM 
4788561SScott.Carter@Sun.COM /*
4798561SScott.Carter@Sun.COM  * i_ddi_irm_modify()
4808561SScott.Carter@Sun.COM  *
4818561SScott.Carter@Sun.COM  *	Modify an existing request in an interrupt pool, and balance the pool.
4828561SScott.Carter@Sun.COM  */
4838561SScott.Carter@Sun.COM int
i_ddi_irm_modify(dev_info_t * dip,int nreq)4848561SScott.Carter@Sun.COM i_ddi_irm_modify(dev_info_t *dip, int nreq)
4858561SScott.Carter@Sun.COM {
4868561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
4878561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
4888561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
48912473SScott.Carter@Oracle.COM 	int		type;
49012473SScott.Carter@Oracle.COM 	int		retval = DDI_SUCCESS;
4918561SScott.Carter@Sun.COM 
4928561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
49312473SScott.Carter@Oracle.COM 	ASSERT(nreq > 0);
4948561SScott.Carter@Sun.COM 
4958561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n",
4968561SScott.Carter@Sun.COM 	    (void *)dip, nreq));
4978561SScott.Carter@Sun.COM 
4988561SScott.Carter@Sun.COM 	/* Validate parameters */
4998561SScott.Carter@Sun.COM 	if ((dip == NULL) || (nreq < 1)) {
5008561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
5018561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
5028561SScott.Carter@Sun.COM 	}
5038561SScott.Carter@Sun.COM 
50412473SScott.Carter@Oracle.COM 	/* Do nothing if not mapped to an IRM pool */
50512473SScott.Carter@Oracle.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) == NULL) ||
50612473SScott.Carter@Oracle.COM 	    ((req_p = intr_p->devi_irm_req_p) == NULL))
50712473SScott.Carter@Oracle.COM 		return (DDI_SUCCESS);
50812473SScott.Carter@Oracle.COM 
50912473SScott.Carter@Oracle.COM 	/* Do nothing if new size is the same */
51012473SScott.Carter@Oracle.COM 	if (nreq == req_p->ireq_nreq)
51112473SScott.Carter@Oracle.COM 		return (DDI_SUCCESS);
51212473SScott.Carter@Oracle.COM 
51312473SScott.Carter@Oracle.COM 	/* Do not allow MSI requests to be resized */
51412473SScott.Carter@Oracle.COM 	if ((type = req_p->ireq_type) == DDI_INTR_TYPE_MSI) {
51512473SScott.Carter@Oracle.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid type\n"));
5168561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
5178561SScott.Carter@Sun.COM 	}
5188561SScott.Carter@Sun.COM 
51912473SScott.Carter@Oracle.COM 	/* Select the pool */
52012473SScott.Carter@Oracle.COM 	if ((pool_p = req_p->ireq_pool_p) == NULL) {
52112473SScott.Carter@Oracle.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: missing pool\n"));
52212473SScott.Carter@Oracle.COM 		return (DDI_FAILURE);
52312473SScott.Carter@Oracle.COM 	}
52412473SScott.Carter@Oracle.COM 
5258561SScott.Carter@Sun.COM 	/* Validate request size is not too large */
52612473SScott.Carter@Oracle.COM 	if (nreq > i_ddi_intr_get_limit(dip, type, pool_p)) {
5278561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
5288561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
5298561SScott.Carter@Sun.COM 	}
5308561SScott.Carter@Sun.COM 
53112473SScott.Carter@Oracle.COM 	/* Lock the pool */
53212473SScott.Carter@Oracle.COM 	mutex_enter(&pool_p->ipool_lock);
53312473SScott.Carter@Oracle.COM 
5348561SScott.Carter@Sun.COM 	/*
53512473SScott.Carter@Oracle.COM 	 * Process the modification.
53612473SScott.Carter@Oracle.COM 	 *
53712473SScott.Carter@Oracle.COM 	 *	- To increase a non-IRM request, call the implementation in
53812473SScott.Carter@Oracle.COM 	 *	  i_ddi_irm_modify_increase().
53912473SScott.Carter@Oracle.COM 	 *
54012473SScott.Carter@Oracle.COM 	 *	- To decrease a non-IRM request, directly update the pool and
54112473SScott.Carter@Oracle.COM 	 *	  request, then queue the pool for later rebalancing.
54212473SScott.Carter@Oracle.COM 	 *
54312473SScott.Carter@Oracle.COM 	 *	- To modify an IRM request, always queue the pool for later
54412473SScott.Carter@Oracle.COM 	 *	  rebalancing.  IRM consumers rely upon callbacks for changes.
5458561SScott.Carter@Sun.COM 	 */
54612473SScott.Carter@Oracle.COM 	if ((nreq > req_p->ireq_nreq) &&
54712473SScott.Carter@Oracle.COM 	    (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)) {
5488561SScott.Carter@Sun.COM 
54912473SScott.Carter@Oracle.COM 		retval = i_ddi_irm_modify_increase(req_p, nreq);
55012473SScott.Carter@Oracle.COM 
55112473SScott.Carter@Oracle.COM 	} else {
5528561SScott.Carter@Sun.COM 
5538561SScott.Carter@Sun.COM 		/* Update pool and request */
5548561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
5558561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
55612473SScott.Carter@Oracle.COM 		if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS) {
55712473SScott.Carter@Oracle.COM 			pool_p->ipool_minno -= req_p->ireq_navail;
55812473SScott.Carter@Oracle.COM 			pool_p->ipool_resno -= req_p->ireq_navail;
55912473SScott.Carter@Oracle.COM 			pool_p->ipool_minno += nreq;
56012473SScott.Carter@Oracle.COM 			pool_p->ipool_resno += nreq;
56112473SScott.Carter@Oracle.COM 			req_p->ireq_navail = nreq;
56212473SScott.Carter@Oracle.COM 		}
5638561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
5648561SScott.Carter@Sun.COM 
56512473SScott.Carter@Oracle.COM 		/* Re-sort request into the pool */
5668561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
5678561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
5688561SScott.Carter@Sun.COM 
56912473SScott.Carter@Oracle.COM 		/* Queue pool for asynchronous rebalance */
5708561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
57112473SScott.Carter@Oracle.COM 	}
57212473SScott.Carter@Oracle.COM 
57312473SScott.Carter@Oracle.COM 	/* Unlock the pool */
57412473SScott.Carter@Oracle.COM 	mutex_exit(&pool_p->ipool_lock);
57512473SScott.Carter@Oracle.COM 
57612473SScott.Carter@Oracle.COM 	return (retval);
57712473SScott.Carter@Oracle.COM }
57812473SScott.Carter@Oracle.COM 
57912473SScott.Carter@Oracle.COM /*
58012473SScott.Carter@Oracle.COM  * i_ddi_irm_modify_increase()
58112473SScott.Carter@Oracle.COM  *
58212473SScott.Carter@Oracle.COM  *	Increase a non-IRM request.  The additional interrupts are
58312473SScott.Carter@Oracle.COM  *	directly taken from the pool when possible.  Otherwise, an
58412473SScott.Carter@Oracle.COM  *	immediate, synchronous rebalance is performed.  A temporary
58512473SScott.Carter@Oracle.COM  *	proxy request is used for any rebalance operation to ensure
58612473SScott.Carter@Oracle.COM  *	the request is not reduced below its current allocation.
58712473SScott.Carter@Oracle.COM  *
58812473SScott.Carter@Oracle.COM  *	NOTE: pool must already be locked.
58912473SScott.Carter@Oracle.COM  */
59012473SScott.Carter@Oracle.COM static int
i_ddi_irm_modify_increase(ddi_irm_req_t * req_p,int nreq)59112473SScott.Carter@Oracle.COM i_ddi_irm_modify_increase(ddi_irm_req_t *req_p, int nreq)
59212473SScott.Carter@Oracle.COM {
59312473SScott.Carter@Oracle.COM 	dev_info_t	*dip = req_p->ireq_dip;
59412473SScott.Carter@Oracle.COM 	ddi_irm_pool_t	*pool_p = req_p->ireq_pool_p;
59512473SScott.Carter@Oracle.COM 	ddi_irm_req_t	new_req;
59612473SScott.Carter@Oracle.COM 	int		count, delta;
59712473SScott.Carter@Oracle.COM 
59812473SScott.Carter@Oracle.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
59912473SScott.Carter@Oracle.COM 
60012473SScott.Carter@Oracle.COM 	/* Compute number of additional vectors */
60112473SScott.Carter@Oracle.COM 	count = nreq - req_p->ireq_nreq;
60212473SScott.Carter@Oracle.COM 
60312473SScott.Carter@Oracle.COM 	/* Check for minimal fit */
60412473SScott.Carter@Oracle.COM 	if ((pool_p->ipool_minno + count) > pool_p->ipool_totsz) {
60512473SScott.Carter@Oracle.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
60612473SScott.Carter@Oracle.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
60712473SScott.Carter@Oracle.COM 		return (DDI_EAGAIN);
60812473SScott.Carter@Oracle.COM 	}
6098561SScott.Carter@Sun.COM 
61012473SScott.Carter@Oracle.COM 	/* Update the pool */
61112473SScott.Carter@Oracle.COM 	pool_p->ipool_reqno += count;
61212473SScott.Carter@Oracle.COM 	pool_p->ipool_minno += count;
61312473SScott.Carter@Oracle.COM 
61412473SScott.Carter@Oracle.COM 	/* Attempt direct implementation */
61512473SScott.Carter@Oracle.COM 	if ((pool_p->ipool_resno + count) <= pool_p->ipool_totsz) {
61612473SScott.Carter@Oracle.COM 		req_p->ireq_nreq += count;
61712473SScott.Carter@Oracle.COM 		req_p->ireq_navail += count;
61812473SScott.Carter@Oracle.COM 		pool_p->ipool_resno += count;
61912473SScott.Carter@Oracle.COM 		return (DDI_SUCCESS);
62012473SScott.Carter@Oracle.COM 	}
62112473SScott.Carter@Oracle.COM 
62212473SScott.Carter@Oracle.COM 	/* Rebalance required: fail if pool is not active */
62312473SScott.Carter@Oracle.COM 	if ((pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE) == 0) {
62412473SScott.Carter@Oracle.COM 		pool_p->ipool_reqno -= count;
62512473SScott.Carter@Oracle.COM 		pool_p->ipool_minno -= count;
62612473SScott.Carter@Oracle.COM 		return (DDI_EAGAIN);
6278561SScott.Carter@Sun.COM 	}
6288561SScott.Carter@Sun.COM 
62912473SScott.Carter@Oracle.COM 	/* Insert temporary proxy request */
63012473SScott.Carter@Oracle.COM 	bzero(&new_req, sizeof (ddi_irm_req_t));
63112473SScott.Carter@Oracle.COM 	new_req.ireq_dip = dip;
63212473SScott.Carter@Oracle.COM 	new_req.ireq_nreq = count;
63312473SScott.Carter@Oracle.COM 	new_req.ireq_pool_p = pool_p;
63412473SScott.Carter@Oracle.COM 	new_req.ireq_type = req_p->ireq_type;
63512473SScott.Carter@Oracle.COM 	new_req.ireq_flags = DDI_IRM_FLAG_NEW;
63612473SScott.Carter@Oracle.COM 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, &new_req);
63712473SScott.Carter@Oracle.COM 
63812473SScott.Carter@Oracle.COM 	/* Synchronously rebalance */
63912473SScott.Carter@Oracle.COM 	i_ddi_irm_enqueue(pool_p, B_TRUE);
64012473SScott.Carter@Oracle.COM 
64112473SScott.Carter@Oracle.COM 	/* Remove proxy request, and merge into original request */
64212473SScott.Carter@Oracle.COM 	req_p->ireq_nreq += count;
64312473SScott.Carter@Oracle.COM 	if ((delta = (count - new_req.ireq_navail)) > 0) {
64412473SScott.Carter@Oracle.COM 		req_p->ireq_nreq -= delta;
64512473SScott.Carter@Oracle.COM 		pool_p->ipool_reqno -= delta;
64612473SScott.Carter@Oracle.COM 		pool_p->ipool_minno -= delta;
64712473SScott.Carter@Oracle.COM 	}
64812473SScott.Carter@Oracle.COM 	req_p->ireq_navail += new_req.ireq_navail;
64912473SScott.Carter@Oracle.COM 	list_remove(&pool_p->ipool_req_list, req_p);
65012473SScott.Carter@Oracle.COM 	list_remove(&pool_p->ipool_req_list, &new_req);
65112473SScott.Carter@Oracle.COM 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
65212473SScott.Carter@Oracle.COM 
6538561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
6548561SScott.Carter@Sun.COM }
6558561SScott.Carter@Sun.COM 
6568561SScott.Carter@Sun.COM /*
6578561SScott.Carter@Sun.COM  * i_ddi_irm_remove()
6588561SScott.Carter@Sun.COM  *
6598561SScott.Carter@Sun.COM  *	Remove a request from an interrupt pool, and balance the pool.
6608561SScott.Carter@Sun.COM  */
6618561SScott.Carter@Sun.COM int
i_ddi_irm_remove(dev_info_t * dip)6628561SScott.Carter@Sun.COM i_ddi_irm_remove(dev_info_t *dip)
6638561SScott.Carter@Sun.COM {
6648561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
6658561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
6668561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
6678561SScott.Carter@Sun.COM 	uint_t		nmin;
6688561SScott.Carter@Sun.COM 
6698561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
6708561SScott.Carter@Sun.COM 
6718561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip));
6728561SScott.Carter@Sun.COM 
6738561SScott.Carter@Sun.COM 	/* Validate parameters */
6748561SScott.Carter@Sun.COM 	if (dip == NULL) {
6758561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n"));
6768561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
6778561SScott.Carter@Sun.COM 	}
6788561SScott.Carter@Sun.COM 
6798561SScott.Carter@Sun.COM 	/* Check if the device has a request */
6808561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
6818561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
6828561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n"));
6838561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
6848561SScott.Carter@Sun.COM 	}
6858561SScott.Carter@Sun.COM 
6868561SScott.Carter@Sun.COM 	/* Lock the pool */
6878561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
6888561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
6898561SScott.Carter@Sun.COM 
6908561SScott.Carter@Sun.COM 	/* Remove request */
6918561SScott.Carter@Sun.COM 	nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq;
6928561SScott.Carter@Sun.COM 	pool_p->ipool_minno -= nmin;
6938561SScott.Carter@Sun.COM 	pool_p->ipool_reqno -= req_p->ireq_nreq;
6948561SScott.Carter@Sun.COM 	pool_p->ipool_resno -= req_p->ireq_navail;
6958561SScott.Carter@Sun.COM 	list_remove(&pool_p->ipool_req_list, req_p);
6968561SScott.Carter@Sun.COM 
6978561SScott.Carter@Sun.COM 	/* Queue pool to be rebalanced */
6988561SScott.Carter@Sun.COM 	i_ddi_irm_enqueue(pool_p, B_FALSE);
6998561SScott.Carter@Sun.COM 
7008561SScott.Carter@Sun.COM 	/* Unlock the pool */
7018561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
7028561SScott.Carter@Sun.COM 
7038561SScott.Carter@Sun.COM 	/* Destroy the request */
7048561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = NULL;
7058561SScott.Carter@Sun.COM 	kmem_free(req_p, sizeof (ddi_irm_req_t));
7068561SScott.Carter@Sun.COM 
7078561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
7088561SScott.Carter@Sun.COM }
7098561SScott.Carter@Sun.COM 
7108561SScott.Carter@Sun.COM /*
7118561SScott.Carter@Sun.COM  * i_ddi_irm_set_cb()
7128561SScott.Carter@Sun.COM  *
7138561SScott.Carter@Sun.COM  *	Change the callback flag for a request, in response to
7148561SScott.Carter@Sun.COM  *	a change in its callback registration.  Then rebalance
7158561SScott.Carter@Sun.COM  *	the interrupt pool.
7168561SScott.Carter@Sun.COM  *
7178561SScott.Carter@Sun.COM  *	NOTE: the request is not locked because the navail value
7188561SScott.Carter@Sun.COM  *	      is not directly affected.  The balancing thread may
7198561SScott.Carter@Sun.COM  *	      modify the navail value in the background after it
7208561SScott.Carter@Sun.COM  *	      locks the request itself.
7218561SScott.Carter@Sun.COM  */
7228561SScott.Carter@Sun.COM void
i_ddi_irm_set_cb(dev_info_t * dip,boolean_t has_cb_flag)7238561SScott.Carter@Sun.COM i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag)
7248561SScott.Carter@Sun.COM {
7258561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
7268561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
7278561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
7288561SScott.Carter@Sun.COM 	uint_t		nreq;
7298561SScott.Carter@Sun.COM 
7308561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
7318561SScott.Carter@Sun.COM 
7328561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n",
7338561SScott.Carter@Sun.COM 	    (void *)dip, (int)has_cb_flag));
7348561SScott.Carter@Sun.COM 
7358561SScott.Carter@Sun.COM 	/* Validate parameters */
7368561SScott.Carter@Sun.COM 	if (dip == NULL)
7378561SScott.Carter@Sun.COM 		return;
7388561SScott.Carter@Sun.COM 
7398561SScott.Carter@Sun.COM 	/* Check for association with interrupt pool */
7408561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
7418561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
7428561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n"));
7438561SScott.Carter@Sun.COM 		return;
7448561SScott.Carter@Sun.COM 	}
7458561SScott.Carter@Sun.COM 
7468561SScott.Carter@Sun.COM 	/* Lock the pool */
7478561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
7488561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
7498561SScott.Carter@Sun.COM 
7508561SScott.Carter@Sun.COM 	/*
7518561SScott.Carter@Sun.COM 	 * Update the request and the pool
7528561SScott.Carter@Sun.COM 	 */
7538561SScott.Carter@Sun.COM 	if (has_cb_flag) {
7548561SScott.Carter@Sun.COM 
7558561SScott.Carter@Sun.COM 		/* Update pool statistics */
7568561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
7578561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= (req_p->ireq_nreq - 1);
7588561SScott.Carter@Sun.COM 
7598561SScott.Carter@Sun.COM 		/* Update request */
7608561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
7618561SScott.Carter@Sun.COM 
7628561SScott.Carter@Sun.COM 		/* Rebalance in background */
7638561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
7648561SScott.Carter@Sun.COM 
7658561SScott.Carter@Sun.COM 	} else {
7668561SScott.Carter@Sun.COM 
7678561SScott.Carter@Sun.COM 		/* Determine new request size */
7688561SScott.Carter@Sun.COM 		nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz);
7698561SScott.Carter@Sun.COM 
77012683SJimmy.Vetayases@oracle.com #if defined(__i386) || defined(__amd64)
77112683SJimmy.Vetayases@oracle.com 		/* Use the default static limit for non-IRM drivers */
77212683SJimmy.Vetayases@oracle.com 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
77312683SJimmy.Vetayases@oracle.com 			nreq = MIN(nreq, ddi_msix_alloc_limit);
77412683SJimmy.Vetayases@oracle.com #endif
77512683SJimmy.Vetayases@oracle.com 
7768561SScott.Carter@Sun.COM 		/* Update pool statistics */
7778561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
7788561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
7798561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) {
7808561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= 1;
7818561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
7828561SScott.Carter@Sun.COM 		} else {
7838561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= req_p->ireq_nreq;
7848561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
7858561SScott.Carter@Sun.COM 		}
7868561SScott.Carter@Sun.COM 
7878561SScott.Carter@Sun.COM 		/* Update request size, and re-sort in pool */
7888561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
7898561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
7908561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
7918561SScott.Carter@Sun.COM 
7928561SScott.Carter@Sun.COM 		/* Rebalance synchronously, before losing callback */
7938561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
7948561SScott.Carter@Sun.COM 
7958561SScott.Carter@Sun.COM 		/* Remove callback flag */
7968561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK);
7978561SScott.Carter@Sun.COM 	}
7988561SScott.Carter@Sun.COM 
7998561SScott.Carter@Sun.COM 	/* Unlock the pool */
8008561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
8018561SScott.Carter@Sun.COM }
8028561SScott.Carter@Sun.COM 
8038561SScott.Carter@Sun.COM /*
80412473SScott.Carter@Oracle.COM  * i_ddi_irm_supported()
80512473SScott.Carter@Oracle.COM  *
80612473SScott.Carter@Oracle.COM  *	Query if IRM is supported by a driver using a specific interrupt type.
80712473SScott.Carter@Oracle.COM  *	Notice that IRM is limited to MSI-X users with registered callbacks.
80812473SScott.Carter@Oracle.COM  */
80912473SScott.Carter@Oracle.COM int
i_ddi_irm_supported(dev_info_t * dip,int type)81012473SScott.Carter@Oracle.COM i_ddi_irm_supported(dev_info_t *dip, int type)
81112473SScott.Carter@Oracle.COM {
81212473SScott.Carter@Oracle.COM 	ddi_cb_t	*cb_p = DEVI(dip)->devi_cb_p;
81312473SScott.Carter@Oracle.COM 
81412473SScott.Carter@Oracle.COM 	return ((DDI_IRM_HAS_CB(cb_p) && (type == DDI_INTR_TYPE_MSIX)) ?
81512473SScott.Carter@Oracle.COM 	    DDI_SUCCESS : DDI_ENOTSUP);
81612473SScott.Carter@Oracle.COM }
81712473SScott.Carter@Oracle.COM 
81812473SScott.Carter@Oracle.COM /*
8198561SScott.Carter@Sun.COM  * Interrupt Pool Balancing
8208561SScott.Carter@Sun.COM  */
8218561SScott.Carter@Sun.COM 
8228561SScott.Carter@Sun.COM /*
8238561SScott.Carter@Sun.COM  * irm_balance_thread()
8248561SScott.Carter@Sun.COM  *
8258561SScott.Carter@Sun.COM  *	One instance of this thread operates per each defined IRM pool.
8268561SScott.Carter@Sun.COM  *	It does the initial activation of the pool, as well as balancing
8278561SScott.Carter@Sun.COM  *	any requests that were queued up before the pool was active.
8288561SScott.Carter@Sun.COM  *	Once active, it waits forever to service balance operations.
8298561SScott.Carter@Sun.COM  */
8308561SScott.Carter@Sun.COM static void
irm_balance_thread(ddi_irm_pool_t * pool_p)8318561SScott.Carter@Sun.COM irm_balance_thread(ddi_irm_pool_t *pool_p)
8328561SScott.Carter@Sun.COM {
83311066Srafael.vanoni@sun.com 	clock_t		interval;
8348561SScott.Carter@Sun.COM 
8358561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n",
8368561SScott.Carter@Sun.COM 	    (void *)pool_p));
8378561SScott.Carter@Sun.COM 
8388561SScott.Carter@Sun.COM 	/* Lock the pool */
8398561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
8408561SScott.Carter@Sun.COM 
8418561SScott.Carter@Sun.COM 	/* Perform initial balance if required */
8428561SScott.Carter@Sun.COM 	if (pool_p->ipool_reqno > pool_p->ipool_resno)
8438561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
8448561SScott.Carter@Sun.COM 
8458561SScott.Carter@Sun.COM 	/* Activate the pool */
8468561SScott.Carter@Sun.COM 	pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE;
8478561SScott.Carter@Sun.COM 
848*12745SEvan.Yan@Sun.COM 	/*
849*12745SEvan.Yan@Sun.COM 	 * Main loop.
850*12745SEvan.Yan@Sun.COM 	 * Iterate once first before wait on signal, in case there is signal
851*12745SEvan.Yan@Sun.COM 	 * sent before this thread being created
852*12745SEvan.Yan@Sun.COM 	 */
8538561SScott.Carter@Sun.COM 	for (;;) {
8548561SScott.Carter@Sun.COM 
8558561SScott.Carter@Sun.COM 		/* Compute the delay interval */
8568561SScott.Carter@Sun.COM 		interval = drv_usectohz(irm_balance_delay * 1000000);
8578561SScott.Carter@Sun.COM 
8588561SScott.Carter@Sun.COM 		/* Wait one interval, or until there are waiters */
8598561SScott.Carter@Sun.COM 		if ((interval > 0) &&
8608561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) &&
8618561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) {
86211066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&pool_p->ipool_cv,
86311066Srafael.vanoni@sun.com 			    &pool_p->ipool_lock, interval, TR_CLOCK_TICK);
8648561SScott.Carter@Sun.COM 		}
8658561SScott.Carter@Sun.COM 
8668561SScott.Carter@Sun.COM 		/* Check if awakened to exit */
8678561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) {
8688561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
8698561SScott.Carter@Sun.COM 			    "irm_balance_thread: exiting...\n"));
8708561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_lock);
8718561SScott.Carter@Sun.COM 			thread_exit();
8728561SScott.Carter@Sun.COM 		}
8738561SScott.Carter@Sun.COM 
8748561SScott.Carter@Sun.COM 		/* Balance the pool */
8758561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
8768561SScott.Carter@Sun.COM 
8778561SScott.Carter@Sun.COM 		/* Notify waiters */
8788561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) {
8798561SScott.Carter@Sun.COM 			cv_broadcast(&pool_p->ipool_cv);
8808561SScott.Carter@Sun.COM 			pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS);
8818561SScott.Carter@Sun.COM 		}
8828561SScott.Carter@Sun.COM 
8838561SScott.Carter@Sun.COM 		/* Clear QUEUED condition */
8848561SScott.Carter@Sun.COM 		pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED);
885*12745SEvan.Yan@Sun.COM 
886*12745SEvan.Yan@Sun.COM 		/* Sleep until queued */
887*12745SEvan.Yan@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
888*12745SEvan.Yan@Sun.COM 
889*12745SEvan.Yan@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n"));
8908561SScott.Carter@Sun.COM 	}
8918561SScott.Carter@Sun.COM }
8928561SScott.Carter@Sun.COM 
8938561SScott.Carter@Sun.COM /*
8948561SScott.Carter@Sun.COM  * i_ddi_irm_balance()
8958561SScott.Carter@Sun.COM  *
8968561SScott.Carter@Sun.COM  *	Balance a pool.  The general algorithm is to first reset all
8978561SScott.Carter@Sun.COM  *	requests to their maximum size, use reduction algorithms to
8988561SScott.Carter@Sun.COM  *	solve any imbalance, and then notify affected drivers.
8998561SScott.Carter@Sun.COM  */
9008561SScott.Carter@Sun.COM static void
i_ddi_irm_balance(ddi_irm_pool_t * pool_p)9018561SScott.Carter@Sun.COM i_ddi_irm_balance(ddi_irm_pool_t *pool_p)
9028561SScott.Carter@Sun.COM {
9038561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
9048561SScott.Carter@Sun.COM 
9058561SScott.Carter@Sun.COM #ifdef	DEBUG
9068561SScott.Carter@Sun.COM 	uint_t		debug_totsz = 0;
9078561SScott.Carter@Sun.COM 	int		debug_policy = 0;
9088561SScott.Carter@Sun.COM #endif	/* DEBUG */
9098561SScott.Carter@Sun.COM 
9108561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
9118561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
9128561SScott.Carter@Sun.COM 
9138561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n",
9148561SScott.Carter@Sun.COM 	    (void *)pool_p));
9158561SScott.Carter@Sun.COM 
916*12745SEvan.Yan@Sun.COM #ifndef DEBUG
917*12745SEvan.Yan@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
918*12745SEvan.Yan@Sun.COM #else
919*12745SEvan.Yan@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
920*12745SEvan.Yan@Sun.COM #endif  /* DEBUG */
921*12745SEvan.Yan@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
922*12745SEvan.Yan@Sun.COM 		    "i_ddi_irm_balance: pool already balanced\n"));
923*12745SEvan.Yan@Sun.COM 		return;
924*12745SEvan.Yan@Sun.COM 	}
925*12745SEvan.Yan@Sun.COM 
9268561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Adjust size and policy settings */
9278561SScott.Carter@Sun.COM 	if (irm_debug_size > pool_p->ipool_minno) {
9288561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n",
9298561SScott.Carter@Sun.COM 		    irm_debug_size));
9308561SScott.Carter@Sun.COM 		debug_totsz = pool_p->ipool_totsz;
9318561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = irm_debug_size;
9328561SScott.Carter@Sun.COM 	}
9338561SScott.Carter@Sun.COM 	if (DDI_IRM_POLICY_VALID(irm_debug_policy)) {
9348561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
9358561SScott.Carter@Sun.COM 		    "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy));
9368561SScott.Carter@Sun.COM 		debug_policy = pool_p->ipool_policy;
9378561SScott.Carter@Sun.COM 		pool_p->ipool_policy = irm_debug_policy;
9388561SScott.Carter@Sun.COM 	}
9398561SScott.Carter@Sun.COM #endif	/* DEBUG */
9408561SScott.Carter@Sun.COM 
9418561SScott.Carter@Sun.COM 	/* Lock the availability lock */
9428561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_navail_lock);
9438561SScott.Carter@Sun.COM 
9448561SScott.Carter@Sun.COM 	/*
9458561SScott.Carter@Sun.COM 	 * Put all of the reducible requests into a scratch list.
9468561SScott.Carter@Sun.COM 	 * Reset each one of them to their maximum availability.
9478561SScott.Carter@Sun.COM 	 */
9488561SScott.Carter@Sun.COM 	for (req_p = list_head(&pool_p->ipool_req_list); req_p;
9498561SScott.Carter@Sun.COM 	    req_p = list_next(&pool_p->ipool_req_list, req_p)) {
9508561SScott.Carter@Sun.COM 		if (DDI_IRM_IS_REDUCIBLE(req_p)) {
9518561SScott.Carter@Sun.COM 			pool_p->ipool_resno -= req_p->ireq_navail;
9528561SScott.Carter@Sun.COM 			req_p->ireq_scratch = req_p->ireq_navail;
9538561SScott.Carter@Sun.COM 			req_p->ireq_navail = req_p->ireq_nreq;
9548561SScott.Carter@Sun.COM 			pool_p->ipool_resno += req_p->ireq_navail;
9558561SScott.Carter@Sun.COM 			list_insert_tail(&pool_p->ipool_scratch_list, req_p);
9568561SScott.Carter@Sun.COM 		}
9578561SScott.Carter@Sun.COM 	}
9588561SScott.Carter@Sun.COM 
9598561SScott.Carter@Sun.COM 	/* Balance the requests */
9608561SScott.Carter@Sun.COM 	i_ddi_irm_reduce(pool_p);
9618561SScott.Carter@Sun.COM 
9628561SScott.Carter@Sun.COM 	/* Unlock the availability lock */
9638561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_navail_lock);
9648561SScott.Carter@Sun.COM 
9658561SScott.Carter@Sun.COM 	/*
9668561SScott.Carter@Sun.COM 	 * Process REMOVE notifications.
9678561SScott.Carter@Sun.COM 	 *
9688561SScott.Carter@Sun.COM 	 * If a driver fails to release interrupts: exclude it from
9698561SScott.Carter@Sun.COM 	 * further processing, correct the resulting imbalance, and
9708561SScott.Carter@Sun.COM 	 * start over again at the head of the scratch list.
9718561SScott.Carter@Sun.COM 	 */
9728561SScott.Carter@Sun.COM 	req_p = list_head(&pool_p->ipool_scratch_list);
9738561SScott.Carter@Sun.COM 	while (req_p) {
9748561SScott.Carter@Sun.COM 		if ((req_p->ireq_navail < req_p->ireq_scratch) &&
9758561SScott.Carter@Sun.COM 		    (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) {
9768561SScott.Carter@Sun.COM 			list_remove(&pool_p->ipool_scratch_list, req_p);
9778561SScott.Carter@Sun.COM 			mutex_enter(&pool_p->ipool_navail_lock);
9788561SScott.Carter@Sun.COM 			i_ddi_irm_reduce(pool_p);
9798561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_navail_lock);
9808561SScott.Carter@Sun.COM 			req_p = list_head(&pool_p->ipool_scratch_list);
9818561SScott.Carter@Sun.COM 		} else {
9828561SScott.Carter@Sun.COM 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
9838561SScott.Carter@Sun.COM 		}
9848561SScott.Carter@Sun.COM 	}
9858561SScott.Carter@Sun.COM 
9868561SScott.Carter@Sun.COM 	/*
9878561SScott.Carter@Sun.COM 	 * Process ADD notifications.
9888561SScott.Carter@Sun.COM 	 *
9898561SScott.Carter@Sun.COM 	 * This is the last use of the scratch list, so empty it.
9908561SScott.Carter@Sun.COM 	 */
9918561SScott.Carter@Sun.COM 	while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) {
9928561SScott.Carter@Sun.COM 		if (req_p->ireq_navail > req_p->ireq_scratch) {
9938561SScott.Carter@Sun.COM 			(void) i_ddi_irm_notify(pool_p, req_p);
9948561SScott.Carter@Sun.COM 		}
9958561SScott.Carter@Sun.COM 	}
9968561SScott.Carter@Sun.COM 
9978561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Restore size and policy settings */
9988561SScott.Carter@Sun.COM 	if (debug_totsz != 0)
9998561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = debug_totsz;
10008561SScott.Carter@Sun.COM 	if (debug_policy != 0)
10018561SScott.Carter@Sun.COM 		pool_p->ipool_policy = debug_policy;
10028561SScott.Carter@Sun.COM #endif	/* DEBUG */
10038561SScott.Carter@Sun.COM }
10048561SScott.Carter@Sun.COM 
10058561SScott.Carter@Sun.COM /*
10068561SScott.Carter@Sun.COM  * i_ddi_irm_reduce()
10078561SScott.Carter@Sun.COM  *
10088561SScott.Carter@Sun.COM  *	Use reduction algorithms to correct an imbalance in a pool.
10098561SScott.Carter@Sun.COM  */
10108561SScott.Carter@Sun.COM static void
10118561SScott.Carter@Sun.COM i_ddi_irm_reduce(ddi_irm_pool_t *pool_p)
10128561SScott.Carter@Sun.COM {
101310173SEvan.Yan@Sun.COM 	int	imbalance;
10148561SScott.Carter@Sun.COM 
10158561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
10168561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10178561SScott.Carter@Sun.COM 	ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy));
10188561SScott.Carter@Sun.COM 
10198561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n",
10208561SScott.Carter@Sun.COM 	    (void *)pool_p));
10218561SScott.Carter@Sun.COM 
10228561SScott.Carter@Sun.COM 	/* Compute the imbalance.  Do nothing if already balanced. */
10238561SScott.Carter@Sun.COM 	if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0)
10248561SScott.Carter@Sun.COM 		return;
10258561SScott.Carter@Sun.COM 
10268561SScott.Carter@Sun.COM 	/*
102710173SEvan.Yan@Sun.COM 	 * Try policy based reduction first. If it failed, then
10288561SScott.Carter@Sun.COM 	 * possibly reduce new requests as a last resort.
10298561SScott.Carter@Sun.COM 	 */
103010173SEvan.Yan@Sun.COM 	if (i_ddi_irm_reduce_by_policy(pool_p, imbalance, pool_p->ipool_policy)
103110173SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
10328561SScott.Carter@Sun.COM 
10338561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
10348561SScott.Carter@Sun.COM 		    "i_ddi_irm_reduce: policy reductions failed.\n"));
10358561SScott.Carter@Sun.COM 
10368561SScott.Carter@Sun.COM 		/* Compute remaining imbalance */
10378561SScott.Carter@Sun.COM 		imbalance = pool_p->ipool_resno - pool_p->ipool_totsz;
10388561SScott.Carter@Sun.COM 
10398561SScott.Carter@Sun.COM 		ASSERT(imbalance > 0);
10408561SScott.Carter@Sun.COM 
10418561SScott.Carter@Sun.COM 		i_ddi_irm_reduce_new(pool_p, imbalance);
10428561SScott.Carter@Sun.COM 	}
10438561SScott.Carter@Sun.COM }
10448561SScott.Carter@Sun.COM 
10458561SScott.Carter@Sun.COM /*
10468561SScott.Carter@Sun.COM  * i_ddi_irm_enqueue()
10478561SScott.Carter@Sun.COM  *
10488561SScott.Carter@Sun.COM  *	Queue a pool to be balanced.  Signals the balancing thread to wake
10498561SScott.Carter@Sun.COM  *	up and process the pool.  If 'wait_flag' is true, then the current
10508561SScott.Carter@Sun.COM  *	thread becomes a waiter and blocks until the balance is completed.
10518561SScott.Carter@Sun.COM  */
10528561SScott.Carter@Sun.COM static void
10538561SScott.Carter@Sun.COM i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag)
10548561SScott.Carter@Sun.COM {
10558561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
10568561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
10578561SScott.Carter@Sun.COM 
10588561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n",
10598561SScott.Carter@Sun.COM 	    (void *)pool_p, (int)wait_flag));
10608561SScott.Carter@Sun.COM 
10618561SScott.Carter@Sun.COM 	/* Do nothing if pool is already balanced */
10628561SScott.Carter@Sun.COM #ifndef	DEBUG
10638561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
10648561SScott.Carter@Sun.COM #else
10658561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
10668561SScott.Carter@Sun.COM #endif	/* DEBUG */
10678561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
10688561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool already balanced\n"));
10698561SScott.Carter@Sun.COM 		return;
10708561SScott.Carter@Sun.COM 	}
10718561SScott.Carter@Sun.COM 
10728561SScott.Carter@Sun.COM 	/* Avoid deadlocks when IRM is not active */
10738561SScott.Carter@Sun.COM 	if (!irm_active && wait_flag) {
10748561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
10758561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool not active.\n"));
10768561SScott.Carter@Sun.COM 		return;
10778561SScott.Carter@Sun.COM 	}
10788561SScott.Carter@Sun.COM 
10798561SScott.Carter@Sun.COM 	if (wait_flag)
10808561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS;
10818561SScott.Carter@Sun.COM 
10828561SScott.Carter@Sun.COM 	if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) {
10838561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED;
10848561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
10858561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n"));
10868561SScott.Carter@Sun.COM 	}
10878561SScott.Carter@Sun.COM 
10888561SScott.Carter@Sun.COM 	if (wait_flag) {
10898561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n"));
10908561SScott.Carter@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
10918561SScott.Carter@Sun.COM 	}
10928561SScott.Carter@Sun.COM }
10938561SScott.Carter@Sun.COM 
10948561SScott.Carter@Sun.COM /*
109510173SEvan.Yan@Sun.COM  * i_ddi_irm_reduce_by_policy()
10968561SScott.Carter@Sun.COM  *
109710173SEvan.Yan@Sun.COM  *	Reduces requests based on reduction policies.
10988561SScott.Carter@Sun.COM  *
109910173SEvan.Yan@Sun.COM  *	For the DDI_IRM_POLICY_LARGE reduction policy, the algorithm
110010173SEvan.Yan@Sun.COM  *	generally reduces larger requests first, before advancing
110110173SEvan.Yan@Sun.COM  *	to smaller requests.
110210173SEvan.Yan@Sun.COM  *	For the DDI_IRM_POLICY_EVEN reduction policy, the algorithm
110310173SEvan.Yan@Sun.COM  *	reduces requests evenly, without giving a specific preference
110410173SEvan.Yan@Sun.COM  *	to smaller or larger requests. Each iteration reduces all
110510173SEvan.Yan@Sun.COM  *	reducible requests by the same amount until the imbalance is
110610173SEvan.Yan@Sun.COM  *	corrected.
110710173SEvan.Yan@Sun.COM  *
110810173SEvan.Yan@Sun.COM  *	The scratch list is initially sorted in descending order by current
110910173SEvan.Yan@Sun.COM  *	navail values, which are maximized prior to reduction. This sorted
111010173SEvan.Yan@Sun.COM  *	order is preserved.  It avoids reducing requests below the threshold
111110173SEvan.Yan@Sun.COM  *	of the interrupt pool's default allocation size.
11128561SScott.Carter@Sun.COM  *
11138561SScott.Carter@Sun.COM  *	Optimizations in this algorithm include trying to reduce multiple
111410173SEvan.Yan@Sun.COM  *	requests together.  And the algorithm attempts to reduce in larger
111510173SEvan.Yan@Sun.COM  *	increments when possible to minimize the total number of iterations.
11168561SScott.Carter@Sun.COM  */
11178561SScott.Carter@Sun.COM static int
111810173SEvan.Yan@Sun.COM i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *pool_p, int imbalance, int policy)
11198561SScott.Carter@Sun.COM {
11208561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
11218561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
11228561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
11238561SScott.Carter@Sun.COM 
11248561SScott.Carter@Sun.COM 	while (imbalance > 0) {
112510173SEvan.Yan@Sun.COM 		list_t		*slist_p = &pool_p->ipool_scratch_list;
112610173SEvan.Yan@Sun.COM 		ddi_irm_req_t	*req_p = list_head(slist_p), *last_p;
112710173SEvan.Yan@Sun.COM 		uint_t		nreduce = 0, nremain = 0, stop_navail;
112810173SEvan.Yan@Sun.COM 		uint_t		pool_defsz = pool_p->ipool_defsz;
112910173SEvan.Yan@Sun.COM 		uint_t		reduction, max_redu;
11308561SScott.Carter@Sun.COM 
113110173SEvan.Yan@Sun.COM 		/* Fail if none are reducible */
113210173SEvan.Yan@Sun.COM 		if (!req_p || req_p->ireq_navail <= pool_defsz) {
11338561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
113410173SEvan.Yan@Sun.COM 			    "i_ddi_irm_reduce_by_policy: Failure. "
11358925SEvan.Yan@Sun.COM 			    "All requests have downsized to low limit.\n"));
11368561SScott.Carter@Sun.COM 			return (DDI_FAILURE);
11378561SScott.Carter@Sun.COM 		}
11388561SScott.Carter@Sun.COM 
11398561SScott.Carter@Sun.COM 		/* Count reducible requests */
114010173SEvan.Yan@Sun.COM 		stop_navail = (policy == DDI_IRM_POLICY_LARGE) ?
114110173SEvan.Yan@Sun.COM 		    req_p->ireq_navail - 1 : pool_defsz;
114210173SEvan.Yan@Sun.COM 		for (; req_p; req_p = list_next(slist_p, req_p)) {
114310173SEvan.Yan@Sun.COM 			if (req_p->ireq_navail <= stop_navail)
11448561SScott.Carter@Sun.COM 				break;
11458561SScott.Carter@Sun.COM 			nreduce++;
11468561SScott.Carter@Sun.COM 		}
11478561SScott.Carter@Sun.COM 
114810173SEvan.Yan@Sun.COM 		/* Compute reduction */
114910173SEvan.Yan@Sun.COM 		last_p = req_p ? list_prev(slist_p, req_p) : list_tail(slist_p);
115010173SEvan.Yan@Sun.COM 		if ((policy == DDI_IRM_POLICY_LARGE) && req_p &&
115110173SEvan.Yan@Sun.COM 		    req_p->ireq_navail > pool_defsz)
115210173SEvan.Yan@Sun.COM 			reduction = last_p->ireq_navail - req_p->ireq_navail;
115310173SEvan.Yan@Sun.COM 		else
115410173SEvan.Yan@Sun.COM 			reduction = last_p->ireq_navail - pool_defsz;
115510173SEvan.Yan@Sun.COM 
115610173SEvan.Yan@Sun.COM 		if ((max_redu = reduction * nreduce) > imbalance) {
115710173SEvan.Yan@Sun.COM 			reduction = imbalance / nreduce;
115810173SEvan.Yan@Sun.COM 			nremain = imbalance % nreduce;
115910173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= imbalance;
116010173SEvan.Yan@Sun.COM 			imbalance = 0;
116110173SEvan.Yan@Sun.COM 		} else {
116210173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= max_redu;
116310173SEvan.Yan@Sun.COM 			imbalance -= max_redu;
11648561SScott.Carter@Sun.COM 		}
11658561SScott.Carter@Sun.COM 
116610173SEvan.Yan@Sun.COM 		/* Reduce */
116710173SEvan.Yan@Sun.COM 		for (req_p = list_head(slist_p); (reduction != 0) && nreduce--;
116810173SEvan.Yan@Sun.COM 		    req_p = list_next(slist_p, req_p)) {
116910173SEvan.Yan@Sun.COM 			req_p->ireq_navail -= reduction;
11708561SScott.Carter@Sun.COM 		}
11718561SScott.Carter@Sun.COM 
117210173SEvan.Yan@Sun.COM 		for (req_p = last_p; nremain--;
117310173SEvan.Yan@Sun.COM 		    req_p = list_prev(slist_p, req_p)) {
117410173SEvan.Yan@Sun.COM 			req_p->ireq_navail--;
11758561SScott.Carter@Sun.COM 		}
11768561SScott.Carter@Sun.COM 	}
11778561SScott.Carter@Sun.COM 
11788561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
11798561SScott.Carter@Sun.COM }
11808561SScott.Carter@Sun.COM 
11818561SScott.Carter@Sun.COM /*
11828561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_new()
11838561SScott.Carter@Sun.COM  *
11848925SEvan.Yan@Sun.COM  *	Reduces new requests.  This is only used as a last resort
11858925SEvan.Yan@Sun.COM  *	after another reduction algorithm failed.
118610173SEvan.Yan@Sun.COM  *
118710173SEvan.Yan@Sun.COM  *	NOTE: The pool locking in i_ddi_irm_insert() ensures
118810173SEvan.Yan@Sun.COM  *	there can be only one new request at a time in a pool.
11898561SScott.Carter@Sun.COM  */
11908561SScott.Carter@Sun.COM static void
11918561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance)
11928561SScott.Carter@Sun.COM {
11938561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
11948561SScott.Carter@Sun.COM 
11958561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
11968561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
11978561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
11988561SScott.Carter@Sun.COM 
119910173SEvan.Yan@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT,
120010173SEvan.Yan@Sun.COM 	    "i_ddi_irm_reduce_new: pool_p %p imbalance %d\n",
120110173SEvan.Yan@Sun.COM 	    (void *)pool_p, imbalance));
120210173SEvan.Yan@Sun.COM 
120310173SEvan.Yan@Sun.COM 	for (req_p = list_head(&pool_p->ipool_scratch_list); req_p;
120410173SEvan.Yan@Sun.COM 	    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
120510173SEvan.Yan@Sun.COM 		if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) {
120610173SEvan.Yan@Sun.COM 			ASSERT(req_p->ireq_navail >= imbalance);
120710173SEvan.Yan@Sun.COM 			req_p->ireq_navail -= imbalance;
120810173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= imbalance;
120910173SEvan.Yan@Sun.COM 			return;
12108925SEvan.Yan@Sun.COM 		}
12118925SEvan.Yan@Sun.COM 	}
12128925SEvan.Yan@Sun.COM 
121310173SEvan.Yan@Sun.COM 	/* should never go here */
121410173SEvan.Yan@Sun.COM 	ASSERT(B_FALSE);
12158561SScott.Carter@Sun.COM }
12168561SScott.Carter@Sun.COM 
12178561SScott.Carter@Sun.COM /*
12188561SScott.Carter@Sun.COM  * Miscellaneous Helper Functions
12198561SScott.Carter@Sun.COM  */
12208561SScott.Carter@Sun.COM 
12218561SScott.Carter@Sun.COM /*
12228561SScott.Carter@Sun.COM  * i_ddi_intr_get_pool()
12238561SScott.Carter@Sun.COM  *
12248561SScott.Carter@Sun.COM  *	Get an IRM pool that supplies interrupts of a specified type.
12258561SScott.Carter@Sun.COM  *	Invokes a DDI_INTROP_GETPOOL to the bus nexus driver.  Fails
12268561SScott.Carter@Sun.COM  *	if no pool exists.
12278561SScott.Carter@Sun.COM  */
12288561SScott.Carter@Sun.COM ddi_irm_pool_t *
12298561SScott.Carter@Sun.COM i_ddi_intr_get_pool(dev_info_t *dip, int type)
12308561SScott.Carter@Sun.COM {
12318561SScott.Carter@Sun.COM 	devinfo_intr_t		*intr_p;
12328561SScott.Carter@Sun.COM 	ddi_irm_pool_t		*pool_p;
12338561SScott.Carter@Sun.COM 	ddi_irm_req_t		*req_p;
12348561SScott.Carter@Sun.COM 	ddi_intr_handle_impl_t	hdl;
12358561SScott.Carter@Sun.COM 
12368561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
12378561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
12388561SScott.Carter@Sun.COM 
12398561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
12408561SScott.Carter@Sun.COM 	    ((req_p = intr_p->devi_irm_req_p) != NULL) &&
12418561SScott.Carter@Sun.COM 	    ((pool_p = req_p->ireq_pool_p) != NULL) &&
12428561SScott.Carter@Sun.COM 	    (pool_p->ipool_types & type)) {
12438561SScott.Carter@Sun.COM 		return (pool_p);
12448561SScott.Carter@Sun.COM 	}
12458561SScott.Carter@Sun.COM 
12468561SScott.Carter@Sun.COM 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
12478561SScott.Carter@Sun.COM 	hdl.ih_dip = dip;
12488561SScott.Carter@Sun.COM 	hdl.ih_type = type;
12498561SScott.Carter@Sun.COM 
12508561SScott.Carter@Sun.COM 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL,
12518561SScott.Carter@Sun.COM 	    &hdl, (void *)&pool_p) == DDI_SUCCESS)
12528561SScott.Carter@Sun.COM 		return (pool_p);
12538561SScott.Carter@Sun.COM 
12548561SScott.Carter@Sun.COM 	return (NULL);
12558561SScott.Carter@Sun.COM }
12568561SScott.Carter@Sun.COM 
12578561SScott.Carter@Sun.COM /*
12588561SScott.Carter@Sun.COM  * i_ddi_irm_insertion_sort()
12598561SScott.Carter@Sun.COM  *
12608561SScott.Carter@Sun.COM  *	Use the insertion sort method to insert a request into a list.
12618561SScott.Carter@Sun.COM  *	The list is sorted in descending order by request size.
12628561SScott.Carter@Sun.COM  */
12638561SScott.Carter@Sun.COM static void
12648561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p)
12658561SScott.Carter@Sun.COM {
12668561SScott.Carter@Sun.COM 	ddi_irm_req_t	*next_p;
12678561SScott.Carter@Sun.COM 
12688561SScott.Carter@Sun.COM 	next_p = list_head(req_list);
12698561SScott.Carter@Sun.COM 
12708561SScott.Carter@Sun.COM 	while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq))
12718561SScott.Carter@Sun.COM 		next_p = list_next(req_list, next_p);
12728561SScott.Carter@Sun.COM 
12738561SScott.Carter@Sun.COM 	list_insert_before(req_list, next_p, req_p);
12748561SScott.Carter@Sun.COM }
12758561SScott.Carter@Sun.COM 
12768561SScott.Carter@Sun.COM /*
12778561SScott.Carter@Sun.COM  * i_ddi_irm_notify()
12788561SScott.Carter@Sun.COM  *
12798561SScott.Carter@Sun.COM  *	Notify a driver of changes to its interrupt request using the
12808561SScott.Carter@Sun.COM  *	generic callback mechanism.  Checks for errors in processing.
12818561SScott.Carter@Sun.COM  */
12828561SScott.Carter@Sun.COM static int
12838561SScott.Carter@Sun.COM i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
12848561SScott.Carter@Sun.COM {
12858561SScott.Carter@Sun.COM 	ddi_cb_action_t	action;
12868561SScott.Carter@Sun.COM 	ddi_cb_t	*cb_p;
12878561SScott.Carter@Sun.COM 	uint_t		nintrs;
12888561SScott.Carter@Sun.COM 	int		ret, count;
12898561SScott.Carter@Sun.COM 
12908561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n",
12918561SScott.Carter@Sun.COM 	    (void *)pool_p, (void *)req_p));
12928561SScott.Carter@Sun.COM 
12938561SScott.Carter@Sun.COM 	/* Do not notify new or unchanged requests */
12948561SScott.Carter@Sun.COM 	if ((req_p->ireq_navail == req_p->ireq_scratch) ||
12958561SScott.Carter@Sun.COM 	    (req_p->ireq_flags & DDI_IRM_FLAG_NEW))
12968561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
12978561SScott.Carter@Sun.COM 
12988561SScott.Carter@Sun.COM 	/* Determine action and count */
12998561SScott.Carter@Sun.COM 	if (req_p->ireq_navail > req_p->ireq_scratch) {
13008561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_ADD;
13018561SScott.Carter@Sun.COM 		count = req_p->ireq_navail - req_p->ireq_scratch;
13028561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n",
13038561SScott.Carter@Sun.COM 		    count));
13048561SScott.Carter@Sun.COM 	} else {
13058561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_REMOVE;
13068561SScott.Carter@Sun.COM 		count = req_p->ireq_scratch - req_p->ireq_navail;
13078561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n",
13088561SScott.Carter@Sun.COM 		    count));
13098561SScott.Carter@Sun.COM 	}
13108561SScott.Carter@Sun.COM 
13118561SScott.Carter@Sun.COM 	/* Lookup driver callback */
13128561SScott.Carter@Sun.COM 	if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) {
13138561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n"));
13148561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
13158561SScott.Carter@Sun.COM 	}
13168561SScott.Carter@Sun.COM 
13178561SScott.Carter@Sun.COM 	/* Do callback */
13188561SScott.Carter@Sun.COM 	ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count,
13198561SScott.Carter@Sun.COM 	    cb_p->cb_arg1, cb_p->cb_arg2);
13208561SScott.Carter@Sun.COM 
13218561SScott.Carter@Sun.COM 	/* Log callback errors */
13228561SScott.Carter@Sun.COM 	if (ret != DDI_SUCCESS) {
13238561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
13248561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
13258561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), (int)action, ret);
13268561SScott.Carter@Sun.COM 	}
13278561SScott.Carter@Sun.COM 
13288561SScott.Carter@Sun.COM 	/* Check if the driver exceeds its availability */
13298561SScott.Carter@Sun.COM 	nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip);
13308561SScott.Carter@Sun.COM 	if (nintrs > req_p->ireq_navail) {
13318561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed to release interrupts "
13328561SScott.Carter@Sun.COM 		    "(nintrs=%d, navail=%d).\n",
13338561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
13348561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), nintrs,
13358561SScott.Carter@Sun.COM 		    req_p->ireq_navail);
13368561SScott.Carter@Sun.COM 		pool_p->ipool_resno += (nintrs - req_p->ireq_navail);
13378561SScott.Carter@Sun.COM 		req_p->ireq_navail = nintrs;
13388561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
13398561SScott.Carter@Sun.COM 	}
13408561SScott.Carter@Sun.COM 
13418561SScott.Carter@Sun.COM 	/* Update request */
13428561SScott.Carter@Sun.COM 	req_p->ireq_scratch = req_p->ireq_navail;
13438561SScott.Carter@Sun.COM 
13448561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
13458561SScott.Carter@Sun.COM }
13468561SScott.Carter@Sun.COM 
13478561SScott.Carter@Sun.COM /*
13488561SScott.Carter@Sun.COM  * i_ddi_irm_debug_balance()
13498561SScott.Carter@Sun.COM  *
13508561SScott.Carter@Sun.COM  *	A debug/test only routine to force the immediate,
13518561SScott.Carter@Sun.COM  *	synchronous rebalancing of an interrupt pool.
13528561SScott.Carter@Sun.COM  */
13538561SScott.Carter@Sun.COM #ifdef	DEBUG
13548561SScott.Carter@Sun.COM void
13558561SScott.Carter@Sun.COM i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag)
13568561SScott.Carter@Sun.COM {
13578561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
13588561SScott.Carter@Sun.COM 	int		type;
13598561SScott.Carter@Sun.COM 
13608561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n",
13618561SScott.Carter@Sun.COM 	    (void *)dip, (int)wait_flag));
13628561SScott.Carter@Sun.COM 
13638561SScott.Carter@Sun.COM 	if (((type = i_ddi_intr_get_current_type(dip)) != 0) &&
13648561SScott.Carter@Sun.COM 	    ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) {
13658561SScott.Carter@Sun.COM 		mutex_enter(&pool_p->ipool_lock);
13668561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, wait_flag);
13678561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
13688561SScott.Carter@Sun.COM 	}
13698561SScott.Carter@Sun.COM }
13708561SScott.Carter@Sun.COM #endif
1371