xref: /onnv-gate/usr/src/uts/common/os/ddi_intr_irm.c (revision 11066:cebb50cbe4f9)
18561SScott.Carter@Sun.COM /*
28561SScott.Carter@Sun.COM  * CDDL HEADER START
38561SScott.Carter@Sun.COM  *
48561SScott.Carter@Sun.COM  * The contents of this file are subject to the terms of the
58561SScott.Carter@Sun.COM  * Common Development and Distribution License (the "License").
68561SScott.Carter@Sun.COM  * You may not use this file except in compliance with the License.
78561SScott.Carter@Sun.COM  *
88561SScott.Carter@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98561SScott.Carter@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108561SScott.Carter@Sun.COM  * See the License for the specific language governing permissions
118561SScott.Carter@Sun.COM  * and limitations under the License.
128561SScott.Carter@Sun.COM  *
138561SScott.Carter@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148561SScott.Carter@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158561SScott.Carter@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168561SScott.Carter@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178561SScott.Carter@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188561SScott.Carter@Sun.COM  *
198561SScott.Carter@Sun.COM  * CDDL HEADER END
208561SScott.Carter@Sun.COM  */
218561SScott.Carter@Sun.COM /*
228561SScott.Carter@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238561SScott.Carter@Sun.COM  * Use is subject to license terms.
248561SScott.Carter@Sun.COM  */
258561SScott.Carter@Sun.COM 
268561SScott.Carter@Sun.COM #include <sys/note.h>
278561SScott.Carter@Sun.COM #include <sys/sysmacros.h>
288561SScott.Carter@Sun.COM #include <sys/types.h>
298561SScott.Carter@Sun.COM #include <sys/param.h>
308561SScott.Carter@Sun.COM #include <sys/systm.h>
318561SScott.Carter@Sun.COM #include <sys/kmem.h>
328561SScott.Carter@Sun.COM #include <sys/cmn_err.h>
338561SScott.Carter@Sun.COM #include <sys/debug.h>
348561SScott.Carter@Sun.COM #include <sys/ddi.h>
358561SScott.Carter@Sun.COM #include <sys/sunndi.h>
368561SScott.Carter@Sun.COM #include <sys/ndi_impldefs.h>	/* include prototypes */
378561SScott.Carter@Sun.COM 
388561SScott.Carter@Sun.COM /*
398561SScott.Carter@Sun.COM  * Interrupt Resource Management (IRM).
408561SScott.Carter@Sun.COM  */
418561SScott.Carter@Sun.COM 
428561SScott.Carter@Sun.COM #define	DDI_IRM_BALANCE_DELAY	(60)	/* In seconds */
438561SScott.Carter@Sun.COM 
448561SScott.Carter@Sun.COM #define	DDI_IRM_HAS_CB(c)	((c) && (c->cb_flags & DDI_CB_FLAG_INTR))
458561SScott.Carter@Sun.COM 
468561SScott.Carter@Sun.COM #define	DDI_IRM_IS_REDUCIBLE(r)	(((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \
478561SScott.Carter@Sun.COM 				(r->ireq_type == DDI_INTR_TYPE_MSIX)) || \
488561SScott.Carter@Sun.COM 				(r->ireq_flags & DDI_IRM_FLAG_NEW))
498561SScott.Carter@Sun.COM 
508561SScott.Carter@Sun.COM extern pri_t	minclsyspri;
518561SScott.Carter@Sun.COM 
528561SScott.Carter@Sun.COM /* Global policies */
538561SScott.Carter@Sun.COM int		irm_enable = 1;
548561SScott.Carter@Sun.COM boolean_t	irm_active = B_FALSE;
558561SScott.Carter@Sun.COM int		irm_default_policy = DDI_IRM_POLICY_LARGE;
568561SScott.Carter@Sun.COM uint_t		irm_balance_delay = DDI_IRM_BALANCE_DELAY;
578561SScott.Carter@Sun.COM 
588561SScott.Carter@Sun.COM /* Global list of interrupt pools */
598561SScott.Carter@Sun.COM kmutex_t	irm_pools_lock;
608561SScott.Carter@Sun.COM list_t		irm_pools_list;
618561SScott.Carter@Sun.COM 
628561SScott.Carter@Sun.COM /* Global debug tunables */
638561SScott.Carter@Sun.COM #ifdef	DEBUG
648561SScott.Carter@Sun.COM int		irm_debug_policy = 0;
658561SScott.Carter@Sun.COM uint_t		irm_debug_size = 0;
668561SScott.Carter@Sun.COM #endif	/* DEBUG */
678561SScott.Carter@Sun.COM 
688561SScott.Carter@Sun.COM static void	irm_balance_thread(ddi_irm_pool_t *);
698561SScott.Carter@Sun.COM static void	i_ddi_irm_balance(ddi_irm_pool_t *);
708561SScott.Carter@Sun.COM static void	i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t);
718561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce(ddi_irm_pool_t *pool);
7210173SEvan.Yan@Sun.COM static int	i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *, int, int);
738561SScott.Carter@Sun.COM static void	i_ddi_irm_reduce_new(ddi_irm_pool_t *, int);
748561SScott.Carter@Sun.COM static void	i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *);
758561SScott.Carter@Sun.COM static int	i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *);
768561SScott.Carter@Sun.COM 
778561SScott.Carter@Sun.COM /*
788561SScott.Carter@Sun.COM  * OS Initialization Routines
798561SScott.Carter@Sun.COM  */
808561SScott.Carter@Sun.COM 
818561SScott.Carter@Sun.COM /*
828561SScott.Carter@Sun.COM  * irm_init()
838561SScott.Carter@Sun.COM  *
848561SScott.Carter@Sun.COM  *	Initialize IRM subsystem before any drivers are attached.
858561SScott.Carter@Sun.COM  */
868561SScott.Carter@Sun.COM void
878561SScott.Carter@Sun.COM irm_init(void)
888561SScott.Carter@Sun.COM {
898561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
908561SScott.Carter@Sun.COM 	if (!irm_enable)
918561SScott.Carter@Sun.COM 		return;
928561SScott.Carter@Sun.COM 
938561SScott.Carter@Sun.COM 	/* Verify that the default balancing policy is valid */
948561SScott.Carter@Sun.COM 	if (!DDI_IRM_POLICY_VALID(irm_default_policy))
958561SScott.Carter@Sun.COM 		irm_default_policy = DDI_IRM_POLICY_LARGE;
968561SScott.Carter@Sun.COM 
978561SScott.Carter@Sun.COM 	/* Initialize the global list of interrupt pools */
988561SScott.Carter@Sun.COM 	mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL);
998561SScott.Carter@Sun.COM 	list_create(&irm_pools_list, sizeof (ddi_irm_pool_t),
1008561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_pool_t, ipool_link));
1018561SScott.Carter@Sun.COM }
1028561SScott.Carter@Sun.COM 
1038561SScott.Carter@Sun.COM /*
1048561SScott.Carter@Sun.COM  * i_ddi_irm_poststartup()
1058561SScott.Carter@Sun.COM  *
1068561SScott.Carter@Sun.COM  *	IRM is not activated until after the IO subsystem is initialized.
1078561SScott.Carter@Sun.COM  *	When activated, per-pool balancing threads are spawned and a flag
1088561SScott.Carter@Sun.COM  *	is set so that all future pools will be activated when created.
1098561SScott.Carter@Sun.COM  *
1108561SScott.Carter@Sun.COM  *	NOTE: the global variable 'irm_enable' disables IRM if zero.
1118561SScott.Carter@Sun.COM  */
1128561SScott.Carter@Sun.COM void
1138561SScott.Carter@Sun.COM i_ddi_irm_poststartup(void)
1148561SScott.Carter@Sun.COM {
1158561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1168561SScott.Carter@Sun.COM 
1178561SScott.Carter@Sun.COM 	/* Do nothing if IRM is disabled */
1188561SScott.Carter@Sun.COM 	if (!irm_enable)
1198561SScott.Carter@Sun.COM 		return;
1208561SScott.Carter@Sun.COM 
1218561SScott.Carter@Sun.COM 	/* Lock the global list */
1228561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1238561SScott.Carter@Sun.COM 
1248561SScott.Carter@Sun.COM 	/* Activate all defined pools */
1258561SScott.Carter@Sun.COM 	for (pool_p = list_head(&irm_pools_list); pool_p;
1268561SScott.Carter@Sun.COM 	    pool_p = list_next(&irm_pools_list, pool_p))
1278561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
1288561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1298561SScott.Carter@Sun.COM 
1308561SScott.Carter@Sun.COM 	/* Set future pools to be active */
1318561SScott.Carter@Sun.COM 	irm_active = B_TRUE;
1328561SScott.Carter@Sun.COM 
1338561SScott.Carter@Sun.COM 	/* Unlock the global list */
1348561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1358561SScott.Carter@Sun.COM }
1368561SScott.Carter@Sun.COM 
1378561SScott.Carter@Sun.COM /*
1388561SScott.Carter@Sun.COM  * NDI interfaces for creating/destroying IRM pools.
1398561SScott.Carter@Sun.COM  */
1408561SScott.Carter@Sun.COM 
1418561SScott.Carter@Sun.COM /*
1428561SScott.Carter@Sun.COM  * ndi_irm_create()
1438561SScott.Carter@Sun.COM  *
1448561SScott.Carter@Sun.COM  *	Nexus interface to create an IRM pool.  Create the new
1458561SScott.Carter@Sun.COM  *	pool and add it to the global list of interrupt pools.
1468561SScott.Carter@Sun.COM  */
1478561SScott.Carter@Sun.COM int
1488561SScott.Carter@Sun.COM ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp,
1498561SScott.Carter@Sun.COM     ddi_irm_pool_t **pool_retp)
1508561SScott.Carter@Sun.COM {
1518561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
1528561SScott.Carter@Sun.COM 
1538561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
1548561SScott.Carter@Sun.COM 	ASSERT(paramsp != NULL);
1558561SScott.Carter@Sun.COM 	ASSERT(pool_retp != NULL);
1568561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_total >= 1);
1578561SScott.Carter@Sun.COM 	ASSERT(paramsp->iparams_types != 0);
1588561SScott.Carter@Sun.COM 
1598561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip));
1608561SScott.Carter@Sun.COM 
1618561SScott.Carter@Sun.COM 	/* Check if IRM is enabled */
1628561SScott.Carter@Sun.COM 	if (!irm_enable)
1638561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1648561SScott.Carter@Sun.COM 
1658561SScott.Carter@Sun.COM 	/* Validate parameters */
1668561SScott.Carter@Sun.COM 	if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) ||
1678925SEvan.Yan@Sun.COM 	    (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0))
1688561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
1698561SScott.Carter@Sun.COM 
1708561SScott.Carter@Sun.COM 	/* Allocate and initialize the pool */
1718561SScott.Carter@Sun.COM 	pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP);
1728561SScott.Carter@Sun.COM 	pool_p->ipool_owner = dip;
1738561SScott.Carter@Sun.COM 	pool_p->ipool_policy = irm_default_policy;
1748561SScott.Carter@Sun.COM 	pool_p->ipool_types = paramsp->iparams_types;
1758561SScott.Carter@Sun.COM 	pool_p->ipool_totsz = paramsp->iparams_total;
1768925SEvan.Yan@Sun.COM 	pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC,
1778925SEvan.Yan@Sun.COM 	    paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER));
1788561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t),
1798561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_link));
1808561SScott.Carter@Sun.COM 	list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t),
1818561SScott.Carter@Sun.COM 	    offsetof(ddi_irm_req_t, ireq_scratch_link));
1828561SScott.Carter@Sun.COM 	cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL);
1838561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL);
1848561SScott.Carter@Sun.COM 	mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL);
1858561SScott.Carter@Sun.COM 
1868561SScott.Carter@Sun.COM 	/* Add to global list of pools */
1878561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
1888561SScott.Carter@Sun.COM 	list_insert_tail(&irm_pools_list, pool_p);
1898561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
1908561SScott.Carter@Sun.COM 
1918561SScott.Carter@Sun.COM 	/* If IRM is active, then activate the pool */
1928561SScott.Carter@Sun.COM 	if (irm_active)
1938561SScott.Carter@Sun.COM 		pool_p->ipool_thread = thread_create(NULL, 0,
1948561SScott.Carter@Sun.COM 		    irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri);
1958561SScott.Carter@Sun.COM 
1968561SScott.Carter@Sun.COM 	*pool_retp = pool_p;
1978561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
1988561SScott.Carter@Sun.COM }
1998561SScott.Carter@Sun.COM 
2008561SScott.Carter@Sun.COM /*
2018561SScott.Carter@Sun.COM  * ndi_irm_destroy()
2028561SScott.Carter@Sun.COM  *
2038561SScott.Carter@Sun.COM  *	Nexus interface to destroy an IRM pool.  Destroy the pool
2048561SScott.Carter@Sun.COM  *	and remove it from the global list of interrupt pools.
2058561SScott.Carter@Sun.COM  */
2068561SScott.Carter@Sun.COM int
2078561SScott.Carter@Sun.COM ndi_irm_destroy(ddi_irm_pool_t *pool_p)
2088561SScott.Carter@Sun.COM {
2098561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
2108561SScott.Carter@Sun.COM 	ASSERT(pool_p->ipool_resno == 0);
2118561SScott.Carter@Sun.COM 
2128561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n",
2138561SScott.Carter@Sun.COM 	    (void *)pool_p));
2148561SScott.Carter@Sun.COM 
2158561SScott.Carter@Sun.COM 	/* Validate parameters */
2168561SScott.Carter@Sun.COM 	if (pool_p == NULL)
2178561SScott.Carter@Sun.COM 		return (NDI_FAILURE);
2188561SScott.Carter@Sun.COM 
2198561SScott.Carter@Sun.COM 	/* Validate that pool is empty */
2208561SScott.Carter@Sun.COM 	if (pool_p->ipool_resno != 0)
2218561SScott.Carter@Sun.COM 		return (NDI_BUSY);
2228561SScott.Carter@Sun.COM 
2238561SScott.Carter@Sun.COM 	/* Remove the pool from the global list */
2248561SScott.Carter@Sun.COM 	mutex_enter(&irm_pools_lock);
2258561SScott.Carter@Sun.COM 	list_remove(&irm_pools_list, pool_p);
2268561SScott.Carter@Sun.COM 	mutex_exit(&irm_pools_lock);
2278561SScott.Carter@Sun.COM 
2288561SScott.Carter@Sun.COM 	/* Terminate the balancing thread */
2298561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
2308561SScott.Carter@Sun.COM 	if (pool_p->ipool_thread &&
2318561SScott.Carter@Sun.COM 	    (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) {
2328561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT;
2338561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
2348885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
2358561SScott.Carter@Sun.COM 		thread_join(pool_p->ipool_thread->t_did);
2368885SJustin.Frank@Sun.COM 	} else
2378885SJustin.Frank@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
2388561SScott.Carter@Sun.COM 
2398561SScott.Carter@Sun.COM 	/* Destroy the pool */
2408561SScott.Carter@Sun.COM 	cv_destroy(&pool_p->ipool_cv);
2418561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_lock);
2428561SScott.Carter@Sun.COM 	mutex_destroy(&pool_p->ipool_navail_lock);
2438561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_req_list);
2448561SScott.Carter@Sun.COM 	list_destroy(&pool_p->ipool_scratch_list);
2458561SScott.Carter@Sun.COM 	kmem_free(pool_p, sizeof (ddi_irm_pool_t));
2468561SScott.Carter@Sun.COM 
2478561SScott.Carter@Sun.COM 	return (NDI_SUCCESS);
2488561SScott.Carter@Sun.COM }
2498561SScott.Carter@Sun.COM 
2508561SScott.Carter@Sun.COM /*
2518561SScott.Carter@Sun.COM  * Insert/Modify/Remove Interrupt Requests
2528561SScott.Carter@Sun.COM  */
2538561SScott.Carter@Sun.COM 
2548561SScott.Carter@Sun.COM /*
2558561SScott.Carter@Sun.COM  * i_ddi_irm_insert()
2568561SScott.Carter@Sun.COM  *
2578561SScott.Carter@Sun.COM  *	Insert a new request into an interrupt pool, and balance the pool.
2588561SScott.Carter@Sun.COM  */
2598561SScott.Carter@Sun.COM int
2608561SScott.Carter@Sun.COM i_ddi_irm_insert(dev_info_t *dip, int type, int count)
2618561SScott.Carter@Sun.COM {
2628561SScott.Carter@Sun.COM 	ddi_cb_t	*cb_p;
2638561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
2648561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
2658561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
2668561SScott.Carter@Sun.COM 	uint_t		nreq, nmin, npartial;
2678561SScott.Carter@Sun.COM 	boolean_t	irm_flag = B_FALSE;
2688561SScott.Carter@Sun.COM 
2698561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
2708561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
2718561SScott.Carter@Sun.COM 	ASSERT(count > 0);
2728561SScott.Carter@Sun.COM 
2738561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n",
2748561SScott.Carter@Sun.COM 	    (void *)dip, type, count));
2758561SScott.Carter@Sun.COM 
2768561SScott.Carter@Sun.COM 	/* Validate parameters */
2778561SScott.Carter@Sun.COM 	if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) {
2788561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n"));
2798561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
2808561SScott.Carter@Sun.COM 	}
2818561SScott.Carter@Sun.COM 
2828561SScott.Carter@Sun.COM 	/* Check for an existing request */
2838561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
2848561SScott.Carter@Sun.COM 	    (intr_p->devi_irm_req_p != NULL))
2858561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
2868561SScott.Carter@Sun.COM 
2878561SScott.Carter@Sun.COM 	/* Check for IRM support from the system */
2888561SScott.Carter@Sun.COM 	if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) {
2898561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n"));
2908561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
2918561SScott.Carter@Sun.COM 	}
2928561SScott.Carter@Sun.COM 
2938561SScott.Carter@Sun.COM 	/* Check for IRM support from the driver */
2948561SScott.Carter@Sun.COM 	if (((cb_p = DEVI(dip)->devi_cb_p) != NULL) && DDI_IRM_HAS_CB(cb_p) &&
2958561SScott.Carter@Sun.COM 	    (type == DDI_INTR_TYPE_MSIX))
2968561SScott.Carter@Sun.COM 		irm_flag = B_TRUE;
2978561SScott.Carter@Sun.COM 
2988561SScott.Carter@Sun.COM 	/* Determine request size */
2999205SEvan.Yan@Sun.COM 	nreq = (irm_flag) ? count :
3009205SEvan.Yan@Sun.COM 	    MIN(count, i_ddi_intr_get_current_navail(dip, type));
3018561SScott.Carter@Sun.COM 	nmin = (irm_flag) ? 1 : nreq;
3028561SScott.Carter@Sun.COM 	npartial = MIN(nreq, pool_p->ipool_defsz);
3038561SScott.Carter@Sun.COM 
3048561SScott.Carter@Sun.COM 	/* Allocate and initialize the request */
3058561SScott.Carter@Sun.COM 	req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP);
3068561SScott.Carter@Sun.COM 	req_p->ireq_type = type;
3078561SScott.Carter@Sun.COM 	req_p->ireq_dip = dip;
3088561SScott.Carter@Sun.COM 	req_p->ireq_pool_p = pool_p;
3098561SScott.Carter@Sun.COM 	req_p->ireq_nreq = nreq;
3108561SScott.Carter@Sun.COM 	req_p->ireq_flags = DDI_IRM_FLAG_NEW;
3118561SScott.Carter@Sun.COM 	if (DDI_IRM_HAS_CB(cb_p))
3128561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
3138561SScott.Carter@Sun.COM 
3148561SScott.Carter@Sun.COM 	/* Lock the pool */
3158561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
3168561SScott.Carter@Sun.COM 
3178561SScott.Carter@Sun.COM 	/* Check for minimal fit before inserting */
3188561SScott.Carter@Sun.COM 	if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) {
3198561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
3208561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
3218561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3228561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
3238561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
3248561SScott.Carter@Sun.COM 	}
3258561SScott.Carter@Sun.COM 
3268561SScott.Carter@Sun.COM 	/* Insert the request into the pool */
3278561SScott.Carter@Sun.COM 	pool_p->ipool_reqno += nreq;
3288561SScott.Carter@Sun.COM 	pool_p->ipool_minno += nmin;
3298561SScott.Carter@Sun.COM 	i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
3308561SScott.Carter@Sun.COM 
3318561SScott.Carter@Sun.COM 	/*
3328561SScott.Carter@Sun.COM 	 * Try to fulfill the request.
3338561SScott.Carter@Sun.COM 	 *
3348561SScott.Carter@Sun.COM 	 * If all the interrupts are available, and either the request
3358561SScott.Carter@Sun.COM 	 * is static or the pool is active, then just take them directly.
3368561SScott.Carter@Sun.COM 	 *
3378561SScott.Carter@Sun.COM 	 * If only some of the interrupts are available, and the request
3388561SScott.Carter@Sun.COM 	 * can receive future callbacks, then take some now but queue the
3398561SScott.Carter@Sun.COM 	 * pool to be rebalanced later.
3408561SScott.Carter@Sun.COM 	 *
3418561SScott.Carter@Sun.COM 	 * Otherwise, immediately rebalance the pool and wait.
3428561SScott.Carter@Sun.COM 	 */
3438561SScott.Carter@Sun.COM 	if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) &&
3448561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) {
3458561SScott.Carter@Sun.COM 
3468561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3478561SScott.Carter@Sun.COM 		    "request completely fulfilled.\n"));
3488561SScott.Carter@Sun.COM 		pool_p->ipool_resno += nreq;
3498561SScott.Carter@Sun.COM 		req_p->ireq_navail = nreq;
3508561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3518561SScott.Carter@Sun.COM 
3528561SScott.Carter@Sun.COM 	} else if (irm_flag &&
3538561SScott.Carter@Sun.COM 	    ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) {
3548561SScott.Carter@Sun.COM 
3558561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3568561SScott.Carter@Sun.COM 		    "request partially fulfilled.\n"));
3578561SScott.Carter@Sun.COM 		pool_p->ipool_resno += npartial;
3588561SScott.Carter@Sun.COM 		req_p->ireq_navail = npartial;
3598561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3608561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
3618561SScott.Carter@Sun.COM 
3628561SScott.Carter@Sun.COM 	} else {
3638561SScott.Carter@Sun.COM 
3648561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: "
3658561SScott.Carter@Sun.COM 		    "request needs immediate rebalance.\n"));
3668561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
3678561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW);
3688561SScott.Carter@Sun.COM 	}
3698561SScott.Carter@Sun.COM 
3708561SScott.Carter@Sun.COM 	/* Fail if the request cannot be fulfilled at all */
3718561SScott.Carter@Sun.COM 	if (req_p->ireq_navail == 0) {
3728561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n",
3738561SScott.Carter@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
3748561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= nreq;
3758561SScott.Carter@Sun.COM 		pool_p->ipool_minno -= nmin;
3768561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
37710173SEvan.Yan@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
3788561SScott.Carter@Sun.COM 		kmem_free(req_p, sizeof (ddi_irm_req_t));
3798561SScott.Carter@Sun.COM 		return (DDI_EAGAIN);
3808561SScott.Carter@Sun.COM 	}
3818561SScott.Carter@Sun.COM 
3828561SScott.Carter@Sun.COM 	/* Unlock the pool */
3838561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
3848561SScott.Carter@Sun.COM 
3858561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = req_p;
3868561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
3878561SScott.Carter@Sun.COM }
3888561SScott.Carter@Sun.COM 
3898561SScott.Carter@Sun.COM /*
3908561SScott.Carter@Sun.COM  * i_ddi_irm_modify()
3918561SScott.Carter@Sun.COM  *
3928561SScott.Carter@Sun.COM  *	Modify an existing request in an interrupt pool, and balance the pool.
3938561SScott.Carter@Sun.COM  */
3948561SScott.Carter@Sun.COM int
3958561SScott.Carter@Sun.COM i_ddi_irm_modify(dev_info_t *dip, int nreq)
3968561SScott.Carter@Sun.COM {
3978561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
3988561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
3998561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
4008561SScott.Carter@Sun.COM 
4018561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
4028561SScott.Carter@Sun.COM 
4038561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n",
4048561SScott.Carter@Sun.COM 	    (void *)dip, nreq));
4058561SScott.Carter@Sun.COM 
4068561SScott.Carter@Sun.COM 	/* Validate parameters */
4078561SScott.Carter@Sun.COM 	if ((dip == NULL) || (nreq < 1)) {
4088561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
4098561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4108561SScott.Carter@Sun.COM 	}
4118561SScott.Carter@Sun.COM 
4128561SScott.Carter@Sun.COM 	/* Check that the operation is supported */
4138561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
4148561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p) ||
4158561SScott.Carter@Sun.COM 	    !DDI_IRM_IS_REDUCIBLE(req_p)) {
4168561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not supported\n"));
4178561SScott.Carter@Sun.COM 		return (DDI_ENOTSUP);
4188561SScott.Carter@Sun.COM 	}
4198561SScott.Carter@Sun.COM 
4208561SScott.Carter@Sun.COM 	/* Validate request size is not too large */
4218561SScott.Carter@Sun.COM 	if (nreq > intr_p->devi_intr_sup_nintrs) {
4228561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n"));
4238561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4248561SScott.Carter@Sun.COM 	}
4258561SScott.Carter@Sun.COM 
4268561SScott.Carter@Sun.COM 	/*
4278561SScott.Carter@Sun.COM 	 * Modify request, but only if new size is different.
4288561SScott.Carter@Sun.COM 	 */
4298561SScott.Carter@Sun.COM 	if (nreq != req_p->ireq_nreq) {
4308561SScott.Carter@Sun.COM 
4318561SScott.Carter@Sun.COM 		/* Lock the pool */
4328561SScott.Carter@Sun.COM 		pool_p = req_p->ireq_pool_p;
4338561SScott.Carter@Sun.COM 		mutex_enter(&pool_p->ipool_lock);
4348561SScott.Carter@Sun.COM 
4358561SScott.Carter@Sun.COM 		/* Update pool and request */
4368561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
4378561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
4388561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
4398561SScott.Carter@Sun.COM 
4408561SScott.Carter@Sun.COM 		/* Re-sort request in the pool */
4418561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
4428561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
4438561SScott.Carter@Sun.COM 
4448561SScott.Carter@Sun.COM 		/* Queue pool to be rebalanced */
4458561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
4468561SScott.Carter@Sun.COM 
4478561SScott.Carter@Sun.COM 		/* Unlock the pool */
4488561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
4498561SScott.Carter@Sun.COM 	}
4508561SScott.Carter@Sun.COM 
4518561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
4528561SScott.Carter@Sun.COM }
4538561SScott.Carter@Sun.COM 
4548561SScott.Carter@Sun.COM /*
4558561SScott.Carter@Sun.COM  * i_ddi_irm_remove()
4568561SScott.Carter@Sun.COM  *
4578561SScott.Carter@Sun.COM  *	Remove a request from an interrupt pool, and balance the pool.
4588561SScott.Carter@Sun.COM  */
4598561SScott.Carter@Sun.COM int
4608561SScott.Carter@Sun.COM i_ddi_irm_remove(dev_info_t *dip)
4618561SScott.Carter@Sun.COM {
4628561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
4638561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
4648561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
4658561SScott.Carter@Sun.COM 	uint_t		nmin;
4668561SScott.Carter@Sun.COM 
4678561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
4688561SScott.Carter@Sun.COM 
4698561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip));
4708561SScott.Carter@Sun.COM 
4718561SScott.Carter@Sun.COM 	/* Validate parameters */
4728561SScott.Carter@Sun.COM 	if (dip == NULL) {
4738561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n"));
4748561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4758561SScott.Carter@Sun.COM 	}
4768561SScott.Carter@Sun.COM 
4778561SScott.Carter@Sun.COM 	/* Check if the device has a request */
4788561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
4798561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
4808561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n"));
4818561SScott.Carter@Sun.COM 		return (DDI_EINVAL);
4828561SScott.Carter@Sun.COM 	}
4838561SScott.Carter@Sun.COM 
4848561SScott.Carter@Sun.COM 	/* Lock the pool */
4858561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
4868561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
4878561SScott.Carter@Sun.COM 
4888561SScott.Carter@Sun.COM 	/* Remove request */
4898561SScott.Carter@Sun.COM 	nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq;
4908561SScott.Carter@Sun.COM 	pool_p->ipool_minno -= nmin;
4918561SScott.Carter@Sun.COM 	pool_p->ipool_reqno -= req_p->ireq_nreq;
4928561SScott.Carter@Sun.COM 	pool_p->ipool_resno -= req_p->ireq_navail;
4938561SScott.Carter@Sun.COM 	list_remove(&pool_p->ipool_req_list, req_p);
4948561SScott.Carter@Sun.COM 
4958561SScott.Carter@Sun.COM 	/* Queue pool to be rebalanced */
4968561SScott.Carter@Sun.COM 	i_ddi_irm_enqueue(pool_p, B_FALSE);
4978561SScott.Carter@Sun.COM 
4988561SScott.Carter@Sun.COM 	/* Unlock the pool */
4998561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
5008561SScott.Carter@Sun.COM 
5018561SScott.Carter@Sun.COM 	/* Destroy the request */
5028561SScott.Carter@Sun.COM 	intr_p->devi_irm_req_p = NULL;
5038561SScott.Carter@Sun.COM 	kmem_free(req_p, sizeof (ddi_irm_req_t));
5048561SScott.Carter@Sun.COM 
5058561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
5068561SScott.Carter@Sun.COM }
5078561SScott.Carter@Sun.COM 
5088561SScott.Carter@Sun.COM /*
5098561SScott.Carter@Sun.COM  * i_ddi_irm_set_cb()
5108561SScott.Carter@Sun.COM  *
5118561SScott.Carter@Sun.COM  *	Change the callback flag for a request, in response to
5128561SScott.Carter@Sun.COM  *	a change in its callback registration.  Then rebalance
5138561SScott.Carter@Sun.COM  *	the interrupt pool.
5148561SScott.Carter@Sun.COM  *
5158561SScott.Carter@Sun.COM  *	NOTE: the request is not locked because the navail value
5168561SScott.Carter@Sun.COM  *	      is not directly affected.  The balancing thread may
5178561SScott.Carter@Sun.COM  *	      modify the navail value in the background after it
5188561SScott.Carter@Sun.COM  *	      locks the request itself.
5198561SScott.Carter@Sun.COM  */
5208561SScott.Carter@Sun.COM void
5218561SScott.Carter@Sun.COM i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag)
5228561SScott.Carter@Sun.COM {
5238561SScott.Carter@Sun.COM 	devinfo_intr_t	*intr_p;
5248561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
5258561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
5268561SScott.Carter@Sun.COM 	uint_t		nreq;
5278561SScott.Carter@Sun.COM 
5288561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
5298561SScott.Carter@Sun.COM 
5308561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n",
5318561SScott.Carter@Sun.COM 	    (void *)dip, (int)has_cb_flag));
5328561SScott.Carter@Sun.COM 
5338561SScott.Carter@Sun.COM 	/* Validate parameters */
5348561SScott.Carter@Sun.COM 	if (dip == NULL)
5358561SScott.Carter@Sun.COM 		return;
5368561SScott.Carter@Sun.COM 
5378561SScott.Carter@Sun.COM 	/* Check for association with interrupt pool */
5388561SScott.Carter@Sun.COM 	if (!(intr_p = DEVI(dip)->devi_intr_p) ||
5398561SScott.Carter@Sun.COM 	    !(req_p = intr_p->devi_irm_req_p)) {
5408561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n"));
5418561SScott.Carter@Sun.COM 		return;
5428561SScott.Carter@Sun.COM 	}
5438561SScott.Carter@Sun.COM 
5448561SScott.Carter@Sun.COM 	/* Lock the pool */
5458561SScott.Carter@Sun.COM 	pool_p = req_p->ireq_pool_p;
5468561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
5478561SScott.Carter@Sun.COM 
5488561SScott.Carter@Sun.COM 	/*
5498561SScott.Carter@Sun.COM 	 * Update the request and the pool
5508561SScott.Carter@Sun.COM 	 */
5518561SScott.Carter@Sun.COM 	if (has_cb_flag) {
5528561SScott.Carter@Sun.COM 
5538561SScott.Carter@Sun.COM 		/* Update pool statistics */
5548561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX)
5558561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= (req_p->ireq_nreq - 1);
5568561SScott.Carter@Sun.COM 
5578561SScott.Carter@Sun.COM 		/* Update request */
5588561SScott.Carter@Sun.COM 		req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK;
5598561SScott.Carter@Sun.COM 
5608561SScott.Carter@Sun.COM 		/* Rebalance in background */
5618561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_FALSE);
5628561SScott.Carter@Sun.COM 
5638561SScott.Carter@Sun.COM 	} else {
5648561SScott.Carter@Sun.COM 
5658561SScott.Carter@Sun.COM 		/* Determine new request size */
5668561SScott.Carter@Sun.COM 		nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz);
5678561SScott.Carter@Sun.COM 
5688561SScott.Carter@Sun.COM 		/* Update pool statistics */
5698561SScott.Carter@Sun.COM 		pool_p->ipool_reqno -= req_p->ireq_nreq;
5708561SScott.Carter@Sun.COM 		pool_p->ipool_reqno += nreq;
5718561SScott.Carter@Sun.COM 		if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) {
5728561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= 1;
5738561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
5748561SScott.Carter@Sun.COM 		} else {
5758561SScott.Carter@Sun.COM 			pool_p->ipool_minno -= req_p->ireq_nreq;
5768561SScott.Carter@Sun.COM 			pool_p->ipool_minno += nreq;
5778561SScott.Carter@Sun.COM 		}
5788561SScott.Carter@Sun.COM 
5798561SScott.Carter@Sun.COM 		/* Update request size, and re-sort in pool */
5808561SScott.Carter@Sun.COM 		req_p->ireq_nreq = nreq;
5818561SScott.Carter@Sun.COM 		list_remove(&pool_p->ipool_req_list, req_p);
5828561SScott.Carter@Sun.COM 		i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p);
5838561SScott.Carter@Sun.COM 
5848561SScott.Carter@Sun.COM 		/* Rebalance synchronously, before losing callback */
5858561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, B_TRUE);
5868561SScott.Carter@Sun.COM 
5878561SScott.Carter@Sun.COM 		/* Remove callback flag */
5888561SScott.Carter@Sun.COM 		req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK);
5898561SScott.Carter@Sun.COM 	}
5908561SScott.Carter@Sun.COM 
5918561SScott.Carter@Sun.COM 	/* Unlock the pool */
5928561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_lock);
5938561SScott.Carter@Sun.COM }
5948561SScott.Carter@Sun.COM 
5958561SScott.Carter@Sun.COM /*
5968561SScott.Carter@Sun.COM  * Interrupt Pool Balancing
5978561SScott.Carter@Sun.COM  */
5988561SScott.Carter@Sun.COM 
5998561SScott.Carter@Sun.COM /*
6008561SScott.Carter@Sun.COM  * irm_balance_thread()
6018561SScott.Carter@Sun.COM  *
6028561SScott.Carter@Sun.COM  *	One instance of this thread operates per each defined IRM pool.
6038561SScott.Carter@Sun.COM  *	It does the initial activation of the pool, as well as balancing
6048561SScott.Carter@Sun.COM  *	any requests that were queued up before the pool was active.
6058561SScott.Carter@Sun.COM  *	Once active, it waits forever to service balance operations.
6068561SScott.Carter@Sun.COM  */
6078561SScott.Carter@Sun.COM static void
6088561SScott.Carter@Sun.COM irm_balance_thread(ddi_irm_pool_t *pool_p)
6098561SScott.Carter@Sun.COM {
610*11066Srafael.vanoni@sun.com 	clock_t		interval;
6118561SScott.Carter@Sun.COM 
6128561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n",
6138561SScott.Carter@Sun.COM 	    (void *)pool_p));
6148561SScott.Carter@Sun.COM 
6158561SScott.Carter@Sun.COM 	/* Lock the pool */
6168561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_lock);
6178561SScott.Carter@Sun.COM 
6188561SScott.Carter@Sun.COM 	/* Perform initial balance if required */
6198561SScott.Carter@Sun.COM 	if (pool_p->ipool_reqno > pool_p->ipool_resno)
6208561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
6218561SScott.Carter@Sun.COM 
6228561SScott.Carter@Sun.COM 	/* Activate the pool */
6238561SScott.Carter@Sun.COM 	pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE;
6248561SScott.Carter@Sun.COM 
6258561SScott.Carter@Sun.COM 	/* Main loop */
6268561SScott.Carter@Sun.COM 	for (;;) {
6278561SScott.Carter@Sun.COM 
6288561SScott.Carter@Sun.COM 		/* Compute the delay interval */
6298561SScott.Carter@Sun.COM 		interval = drv_usectohz(irm_balance_delay * 1000000);
6308561SScott.Carter@Sun.COM 
6318561SScott.Carter@Sun.COM 		/* Sleep until queued */
6328561SScott.Carter@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
6338561SScott.Carter@Sun.COM 
6348561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n"));
6358561SScott.Carter@Sun.COM 
6368561SScott.Carter@Sun.COM 		/* Wait one interval, or until there are waiters */
6378561SScott.Carter@Sun.COM 		if ((interval > 0) &&
6388561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) &&
6398561SScott.Carter@Sun.COM 		    !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) {
640*11066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&pool_p->ipool_cv,
641*11066Srafael.vanoni@sun.com 			    &pool_p->ipool_lock, interval, TR_CLOCK_TICK);
6428561SScott.Carter@Sun.COM 		}
6438561SScott.Carter@Sun.COM 
6448561SScott.Carter@Sun.COM 		/* Check if awakened to exit */
6458561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) {
6468561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
6478561SScott.Carter@Sun.COM 			    "irm_balance_thread: exiting...\n"));
6488561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_lock);
6498561SScott.Carter@Sun.COM 			thread_exit();
6508561SScott.Carter@Sun.COM 		}
6518561SScott.Carter@Sun.COM 
6528561SScott.Carter@Sun.COM 		/* Balance the pool */
6538561SScott.Carter@Sun.COM 		i_ddi_irm_balance(pool_p);
6548561SScott.Carter@Sun.COM 
6558561SScott.Carter@Sun.COM 		/* Notify waiters */
6568561SScott.Carter@Sun.COM 		if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) {
6578561SScott.Carter@Sun.COM 			cv_broadcast(&pool_p->ipool_cv);
6588561SScott.Carter@Sun.COM 			pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS);
6598561SScott.Carter@Sun.COM 		}
6608561SScott.Carter@Sun.COM 
6618561SScott.Carter@Sun.COM 		/* Clear QUEUED condition */
6628561SScott.Carter@Sun.COM 		pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED);
6638561SScott.Carter@Sun.COM 	}
6648561SScott.Carter@Sun.COM }
6658561SScott.Carter@Sun.COM 
6668561SScott.Carter@Sun.COM /*
6678561SScott.Carter@Sun.COM  * i_ddi_irm_balance()
6688561SScott.Carter@Sun.COM  *
6698561SScott.Carter@Sun.COM  *	Balance a pool.  The general algorithm is to first reset all
6708561SScott.Carter@Sun.COM  *	requests to their maximum size, use reduction algorithms to
6718561SScott.Carter@Sun.COM  *	solve any imbalance, and then notify affected drivers.
6728561SScott.Carter@Sun.COM  */
6738561SScott.Carter@Sun.COM static void
6748561SScott.Carter@Sun.COM i_ddi_irm_balance(ddi_irm_pool_t *pool_p)
6758561SScott.Carter@Sun.COM {
6768561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
6778561SScott.Carter@Sun.COM 
6788561SScott.Carter@Sun.COM #ifdef	DEBUG
6798561SScott.Carter@Sun.COM 	uint_t		debug_totsz = 0;
6808561SScott.Carter@Sun.COM 	int		debug_policy = 0;
6818561SScott.Carter@Sun.COM #endif	/* DEBUG */
6828561SScott.Carter@Sun.COM 
6838561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
6848561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
6858561SScott.Carter@Sun.COM 
6868561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n",
6878561SScott.Carter@Sun.COM 	    (void *)pool_p));
6888561SScott.Carter@Sun.COM 
6898561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Adjust size and policy settings */
6908561SScott.Carter@Sun.COM 	if (irm_debug_size > pool_p->ipool_minno) {
6918561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n",
6928561SScott.Carter@Sun.COM 		    irm_debug_size));
6938561SScott.Carter@Sun.COM 		debug_totsz = pool_p->ipool_totsz;
6948561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = irm_debug_size;
6958561SScott.Carter@Sun.COM 	}
6968561SScott.Carter@Sun.COM 	if (DDI_IRM_POLICY_VALID(irm_debug_policy)) {
6978561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
6988561SScott.Carter@Sun.COM 		    "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy));
6998561SScott.Carter@Sun.COM 		debug_policy = pool_p->ipool_policy;
7008561SScott.Carter@Sun.COM 		pool_p->ipool_policy = irm_debug_policy;
7018561SScott.Carter@Sun.COM 	}
7028561SScott.Carter@Sun.COM #endif	/* DEBUG */
7038561SScott.Carter@Sun.COM 
7048561SScott.Carter@Sun.COM 	/* Lock the availability lock */
7058561SScott.Carter@Sun.COM 	mutex_enter(&pool_p->ipool_navail_lock);
7068561SScott.Carter@Sun.COM 
7078561SScott.Carter@Sun.COM 	/*
7088561SScott.Carter@Sun.COM 	 * Put all of the reducible requests into a scratch list.
7098561SScott.Carter@Sun.COM 	 * Reset each one of them to their maximum availability.
7108561SScott.Carter@Sun.COM 	 */
7118561SScott.Carter@Sun.COM 	for (req_p = list_head(&pool_p->ipool_req_list); req_p;
7128561SScott.Carter@Sun.COM 	    req_p = list_next(&pool_p->ipool_req_list, req_p)) {
7138561SScott.Carter@Sun.COM 		if (DDI_IRM_IS_REDUCIBLE(req_p)) {
7148561SScott.Carter@Sun.COM 			pool_p->ipool_resno -= req_p->ireq_navail;
7158561SScott.Carter@Sun.COM 			req_p->ireq_scratch = req_p->ireq_navail;
7168561SScott.Carter@Sun.COM 			req_p->ireq_navail = req_p->ireq_nreq;
7178561SScott.Carter@Sun.COM 			pool_p->ipool_resno += req_p->ireq_navail;
7188561SScott.Carter@Sun.COM 			list_insert_tail(&pool_p->ipool_scratch_list, req_p);
7198561SScott.Carter@Sun.COM 		}
7208561SScott.Carter@Sun.COM 	}
7218561SScott.Carter@Sun.COM 
7228561SScott.Carter@Sun.COM 	/* Balance the requests */
7238561SScott.Carter@Sun.COM 	i_ddi_irm_reduce(pool_p);
7248561SScott.Carter@Sun.COM 
7258561SScott.Carter@Sun.COM 	/* Unlock the availability lock */
7268561SScott.Carter@Sun.COM 	mutex_exit(&pool_p->ipool_navail_lock);
7278561SScott.Carter@Sun.COM 
7288561SScott.Carter@Sun.COM 	/*
7298561SScott.Carter@Sun.COM 	 * Process REMOVE notifications.
7308561SScott.Carter@Sun.COM 	 *
7318561SScott.Carter@Sun.COM 	 * If a driver fails to release interrupts: exclude it from
7328561SScott.Carter@Sun.COM 	 * further processing, correct the resulting imbalance, and
7338561SScott.Carter@Sun.COM 	 * start over again at the head of the scratch list.
7348561SScott.Carter@Sun.COM 	 */
7358561SScott.Carter@Sun.COM 	req_p = list_head(&pool_p->ipool_scratch_list);
7368561SScott.Carter@Sun.COM 	while (req_p) {
7378561SScott.Carter@Sun.COM 		if ((req_p->ireq_navail < req_p->ireq_scratch) &&
7388561SScott.Carter@Sun.COM 		    (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) {
7398561SScott.Carter@Sun.COM 			list_remove(&pool_p->ipool_scratch_list, req_p);
7408561SScott.Carter@Sun.COM 			mutex_enter(&pool_p->ipool_navail_lock);
7418561SScott.Carter@Sun.COM 			i_ddi_irm_reduce(pool_p);
7428561SScott.Carter@Sun.COM 			mutex_exit(&pool_p->ipool_navail_lock);
7438561SScott.Carter@Sun.COM 			req_p = list_head(&pool_p->ipool_scratch_list);
7448561SScott.Carter@Sun.COM 		} else {
7458561SScott.Carter@Sun.COM 			req_p = list_next(&pool_p->ipool_scratch_list, req_p);
7468561SScott.Carter@Sun.COM 		}
7478561SScott.Carter@Sun.COM 	}
7488561SScott.Carter@Sun.COM 
7498561SScott.Carter@Sun.COM 	/*
7508561SScott.Carter@Sun.COM 	 * Process ADD notifications.
7518561SScott.Carter@Sun.COM 	 *
7528561SScott.Carter@Sun.COM 	 * This is the last use of the scratch list, so empty it.
7538561SScott.Carter@Sun.COM 	 */
7548561SScott.Carter@Sun.COM 	while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) {
7558561SScott.Carter@Sun.COM 		if (req_p->ireq_navail > req_p->ireq_scratch) {
7568561SScott.Carter@Sun.COM 			(void) i_ddi_irm_notify(pool_p, req_p);
7578561SScott.Carter@Sun.COM 		}
7588561SScott.Carter@Sun.COM 	}
7598561SScott.Carter@Sun.COM 
7608561SScott.Carter@Sun.COM #ifdef	DEBUG	/* Restore size and policy settings */
7618561SScott.Carter@Sun.COM 	if (debug_totsz != 0)
7628561SScott.Carter@Sun.COM 		pool_p->ipool_totsz = debug_totsz;
7638561SScott.Carter@Sun.COM 	if (debug_policy != 0)
7648561SScott.Carter@Sun.COM 		pool_p->ipool_policy = debug_policy;
7658561SScott.Carter@Sun.COM #endif	/* DEBUG */
7668561SScott.Carter@Sun.COM }
7678561SScott.Carter@Sun.COM 
7688561SScott.Carter@Sun.COM /*
7698561SScott.Carter@Sun.COM  * i_ddi_irm_reduce()
7708561SScott.Carter@Sun.COM  *
7718561SScott.Carter@Sun.COM  *	Use reduction algorithms to correct an imbalance in a pool.
7728561SScott.Carter@Sun.COM  */
7738561SScott.Carter@Sun.COM static void
7748561SScott.Carter@Sun.COM i_ddi_irm_reduce(ddi_irm_pool_t *pool_p)
7758561SScott.Carter@Sun.COM {
77610173SEvan.Yan@Sun.COM 	int	imbalance;
7778561SScott.Carter@Sun.COM 
7788561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
7798561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
7808561SScott.Carter@Sun.COM 	ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy));
7818561SScott.Carter@Sun.COM 
7828561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n",
7838561SScott.Carter@Sun.COM 	    (void *)pool_p));
7848561SScott.Carter@Sun.COM 
7858561SScott.Carter@Sun.COM 	/* Compute the imbalance.  Do nothing if already balanced. */
7868561SScott.Carter@Sun.COM 	if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0)
7878561SScott.Carter@Sun.COM 		return;
7888561SScott.Carter@Sun.COM 
7898561SScott.Carter@Sun.COM 	/*
79010173SEvan.Yan@Sun.COM 	 * Try policy based reduction first. If it failed, then
7918561SScott.Carter@Sun.COM 	 * possibly reduce new requests as a last resort.
7928561SScott.Carter@Sun.COM 	 */
79310173SEvan.Yan@Sun.COM 	if (i_ddi_irm_reduce_by_policy(pool_p, imbalance, pool_p->ipool_policy)
79410173SEvan.Yan@Sun.COM 	    != DDI_SUCCESS) {
7958561SScott.Carter@Sun.COM 
7968561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
7978561SScott.Carter@Sun.COM 		    "i_ddi_irm_reduce: policy reductions failed.\n"));
7988561SScott.Carter@Sun.COM 
7998561SScott.Carter@Sun.COM 		/* Compute remaining imbalance */
8008561SScott.Carter@Sun.COM 		imbalance = pool_p->ipool_resno - pool_p->ipool_totsz;
8018561SScott.Carter@Sun.COM 
8028561SScott.Carter@Sun.COM 		ASSERT(imbalance > 0);
8038561SScott.Carter@Sun.COM 
8048561SScott.Carter@Sun.COM 		i_ddi_irm_reduce_new(pool_p, imbalance);
8058561SScott.Carter@Sun.COM 	}
8068561SScott.Carter@Sun.COM }
8078561SScott.Carter@Sun.COM 
8088561SScott.Carter@Sun.COM /*
8098561SScott.Carter@Sun.COM  * i_ddi_irm_enqueue()
8108561SScott.Carter@Sun.COM  *
8118561SScott.Carter@Sun.COM  *	Queue a pool to be balanced.  Signals the balancing thread to wake
8128561SScott.Carter@Sun.COM  *	up and process the pool.  If 'wait_flag' is true, then the current
8138561SScott.Carter@Sun.COM  *	thread becomes a waiter and blocks until the balance is completed.
8148561SScott.Carter@Sun.COM  */
8158561SScott.Carter@Sun.COM static void
8168561SScott.Carter@Sun.COM i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag)
8178561SScott.Carter@Sun.COM {
8188561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
8198561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
8208561SScott.Carter@Sun.COM 
8218561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n",
8228561SScott.Carter@Sun.COM 	    (void *)pool_p, (int)wait_flag));
8238561SScott.Carter@Sun.COM 
8248561SScott.Carter@Sun.COM 	/* Do nothing if pool is already balanced */
8258561SScott.Carter@Sun.COM #ifndef	DEBUG
8268561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno)) {
8278561SScott.Carter@Sun.COM #else
8288561SScott.Carter@Sun.COM 	if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) {
8298561SScott.Carter@Sun.COM #endif	/* DEBUG */
8308561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
8318561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool already balanced\n"));
8328561SScott.Carter@Sun.COM 		return;
8338561SScott.Carter@Sun.COM 	}
8348561SScott.Carter@Sun.COM 
8358561SScott.Carter@Sun.COM 	/* Avoid deadlocks when IRM is not active */
8368561SScott.Carter@Sun.COM 	if (!irm_active && wait_flag) {
8378561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT,
8388561SScott.Carter@Sun.COM 		    "i_ddi_irm_enqueue: pool not active.\n"));
8398561SScott.Carter@Sun.COM 		return;
8408561SScott.Carter@Sun.COM 	}
8418561SScott.Carter@Sun.COM 
8428561SScott.Carter@Sun.COM 	if (wait_flag)
8438561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS;
8448561SScott.Carter@Sun.COM 
8458561SScott.Carter@Sun.COM 	if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) {
8468561SScott.Carter@Sun.COM 		pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED;
8478561SScott.Carter@Sun.COM 		cv_signal(&pool_p->ipool_cv);
8488561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n"));
8498561SScott.Carter@Sun.COM 	}
8508561SScott.Carter@Sun.COM 
8518561SScott.Carter@Sun.COM 	if (wait_flag) {
8528561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n"));
8538561SScott.Carter@Sun.COM 		cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock);
8548561SScott.Carter@Sun.COM 	}
8558561SScott.Carter@Sun.COM }
8568561SScott.Carter@Sun.COM 
8578561SScott.Carter@Sun.COM /*
85810173SEvan.Yan@Sun.COM  * i_ddi_irm_reduce_by_policy()
8598561SScott.Carter@Sun.COM  *
86010173SEvan.Yan@Sun.COM  *	Reduces requests based on reduction policies.
8618561SScott.Carter@Sun.COM  *
86210173SEvan.Yan@Sun.COM  *	For the DDI_IRM_POLICY_LARGE reduction policy, the algorithm
86310173SEvan.Yan@Sun.COM  *	generally reduces larger requests first, before advancing
86410173SEvan.Yan@Sun.COM  *	to smaller requests.
86510173SEvan.Yan@Sun.COM  *	For the DDI_IRM_POLICY_EVEN reduction policy, the algorithm
86610173SEvan.Yan@Sun.COM  *	reduces requests evenly, without giving a specific preference
86710173SEvan.Yan@Sun.COM  *	to smaller or larger requests. Each iteration reduces all
86810173SEvan.Yan@Sun.COM  *	reducible requests by the same amount until the imbalance is
86910173SEvan.Yan@Sun.COM  *	corrected.
87010173SEvan.Yan@Sun.COM  *
87110173SEvan.Yan@Sun.COM  *	The scratch list is initially sorted in descending order by current
87210173SEvan.Yan@Sun.COM  *	navail values, which are maximized prior to reduction. This sorted
87310173SEvan.Yan@Sun.COM  *	order is preserved.  It avoids reducing requests below the threshold
87410173SEvan.Yan@Sun.COM  *	of the interrupt pool's default allocation size.
8758561SScott.Carter@Sun.COM  *
8768561SScott.Carter@Sun.COM  *	Optimizations in this algorithm include trying to reduce multiple
87710173SEvan.Yan@Sun.COM  *	requests together.  And the algorithm attempts to reduce in larger
87810173SEvan.Yan@Sun.COM  *	increments when possible to minimize the total number of iterations.
8798561SScott.Carter@Sun.COM  */
8808561SScott.Carter@Sun.COM static int
88110173SEvan.Yan@Sun.COM i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *pool_p, int imbalance, int policy)
8828561SScott.Carter@Sun.COM {
8838561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
8848561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
8858561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
8868561SScott.Carter@Sun.COM 
8878561SScott.Carter@Sun.COM 	while (imbalance > 0) {
88810173SEvan.Yan@Sun.COM 		list_t		*slist_p = &pool_p->ipool_scratch_list;
88910173SEvan.Yan@Sun.COM 		ddi_irm_req_t	*req_p = list_head(slist_p), *last_p;
89010173SEvan.Yan@Sun.COM 		uint_t		nreduce = 0, nremain = 0, stop_navail;
89110173SEvan.Yan@Sun.COM 		uint_t		pool_defsz = pool_p->ipool_defsz;
89210173SEvan.Yan@Sun.COM 		uint_t		reduction, max_redu;
8938561SScott.Carter@Sun.COM 
89410173SEvan.Yan@Sun.COM 		/* Fail if none are reducible */
89510173SEvan.Yan@Sun.COM 		if (!req_p || req_p->ireq_navail <= pool_defsz) {
8968561SScott.Carter@Sun.COM 			DDI_INTR_IRMDBG((CE_CONT,
89710173SEvan.Yan@Sun.COM 			    "i_ddi_irm_reduce_by_policy: Failure. "
8988925SEvan.Yan@Sun.COM 			    "All requests have downsized to low limit.\n"));
8998561SScott.Carter@Sun.COM 			return (DDI_FAILURE);
9008561SScott.Carter@Sun.COM 		}
9018561SScott.Carter@Sun.COM 
9028561SScott.Carter@Sun.COM 		/* Count reducible requests */
90310173SEvan.Yan@Sun.COM 		stop_navail = (policy == DDI_IRM_POLICY_LARGE) ?
90410173SEvan.Yan@Sun.COM 		    req_p->ireq_navail - 1 : pool_defsz;
90510173SEvan.Yan@Sun.COM 		for (; req_p; req_p = list_next(slist_p, req_p)) {
90610173SEvan.Yan@Sun.COM 			if (req_p->ireq_navail <= stop_navail)
9078561SScott.Carter@Sun.COM 				break;
9088561SScott.Carter@Sun.COM 			nreduce++;
9098561SScott.Carter@Sun.COM 		}
9108561SScott.Carter@Sun.COM 
91110173SEvan.Yan@Sun.COM 		/* Compute reduction */
91210173SEvan.Yan@Sun.COM 		last_p = req_p ? list_prev(slist_p, req_p) : list_tail(slist_p);
91310173SEvan.Yan@Sun.COM 		if ((policy == DDI_IRM_POLICY_LARGE) && req_p &&
91410173SEvan.Yan@Sun.COM 		    req_p->ireq_navail > pool_defsz)
91510173SEvan.Yan@Sun.COM 			reduction = last_p->ireq_navail - req_p->ireq_navail;
91610173SEvan.Yan@Sun.COM 		else
91710173SEvan.Yan@Sun.COM 			reduction = last_p->ireq_navail - pool_defsz;
91810173SEvan.Yan@Sun.COM 
91910173SEvan.Yan@Sun.COM 		if ((max_redu = reduction * nreduce) > imbalance) {
92010173SEvan.Yan@Sun.COM 			reduction = imbalance / nreduce;
92110173SEvan.Yan@Sun.COM 			nremain = imbalance % nreduce;
92210173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= imbalance;
92310173SEvan.Yan@Sun.COM 			imbalance = 0;
92410173SEvan.Yan@Sun.COM 		} else {
92510173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= max_redu;
92610173SEvan.Yan@Sun.COM 			imbalance -= max_redu;
9278561SScott.Carter@Sun.COM 		}
9288561SScott.Carter@Sun.COM 
92910173SEvan.Yan@Sun.COM 		/* Reduce */
93010173SEvan.Yan@Sun.COM 		for (req_p = list_head(slist_p); (reduction != 0) && nreduce--;
93110173SEvan.Yan@Sun.COM 		    req_p = list_next(slist_p, req_p)) {
93210173SEvan.Yan@Sun.COM 			req_p->ireq_navail -= reduction;
9338561SScott.Carter@Sun.COM 		}
9348561SScott.Carter@Sun.COM 
93510173SEvan.Yan@Sun.COM 		for (req_p = last_p; nremain--;
93610173SEvan.Yan@Sun.COM 		    req_p = list_prev(slist_p, req_p)) {
93710173SEvan.Yan@Sun.COM 			req_p->ireq_navail--;
9388561SScott.Carter@Sun.COM 		}
9398561SScott.Carter@Sun.COM 	}
9408561SScott.Carter@Sun.COM 
9418561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
9428561SScott.Carter@Sun.COM }
9438561SScott.Carter@Sun.COM 
9448561SScott.Carter@Sun.COM /*
9458561SScott.Carter@Sun.COM  * i_ddi_irm_reduce_new()
9468561SScott.Carter@Sun.COM  *
9478925SEvan.Yan@Sun.COM  *	Reduces new requests.  This is only used as a last resort
9488925SEvan.Yan@Sun.COM  *	after another reduction algorithm failed.
94910173SEvan.Yan@Sun.COM  *
95010173SEvan.Yan@Sun.COM  *	NOTE: The pool locking in i_ddi_irm_insert() ensures
95110173SEvan.Yan@Sun.COM  *	there can be only one new request at a time in a pool.
9528561SScott.Carter@Sun.COM  */
9538561SScott.Carter@Sun.COM static void
9548561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance)
9558561SScott.Carter@Sun.COM {
9568561SScott.Carter@Sun.COM 	ddi_irm_req_t	*req_p;
9578561SScott.Carter@Sun.COM 
9588561SScott.Carter@Sun.COM 	ASSERT(pool_p != NULL);
9598561SScott.Carter@Sun.COM 	ASSERT(imbalance > 0);
9608561SScott.Carter@Sun.COM 	ASSERT(MUTEX_HELD(&pool_p->ipool_lock));
9618561SScott.Carter@Sun.COM 
96210173SEvan.Yan@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT,
96310173SEvan.Yan@Sun.COM 	    "i_ddi_irm_reduce_new: pool_p %p imbalance %d\n",
96410173SEvan.Yan@Sun.COM 	    (void *)pool_p, imbalance));
96510173SEvan.Yan@Sun.COM 
96610173SEvan.Yan@Sun.COM 	for (req_p = list_head(&pool_p->ipool_scratch_list); req_p;
96710173SEvan.Yan@Sun.COM 	    req_p = list_next(&pool_p->ipool_scratch_list, req_p)) {
96810173SEvan.Yan@Sun.COM 		if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) {
96910173SEvan.Yan@Sun.COM 			ASSERT(req_p->ireq_navail >= imbalance);
97010173SEvan.Yan@Sun.COM 			req_p->ireq_navail -= imbalance;
97110173SEvan.Yan@Sun.COM 			pool_p->ipool_resno -= imbalance;
97210173SEvan.Yan@Sun.COM 			return;
9738925SEvan.Yan@Sun.COM 		}
9748925SEvan.Yan@Sun.COM 	}
9758925SEvan.Yan@Sun.COM 
97610173SEvan.Yan@Sun.COM 	/* should never go here */
97710173SEvan.Yan@Sun.COM 	ASSERT(B_FALSE);
9788561SScott.Carter@Sun.COM }
9798561SScott.Carter@Sun.COM 
9808561SScott.Carter@Sun.COM /*
9818561SScott.Carter@Sun.COM  * Miscellaneous Helper Functions
9828561SScott.Carter@Sun.COM  */
9838561SScott.Carter@Sun.COM 
9848561SScott.Carter@Sun.COM /*
9858561SScott.Carter@Sun.COM  * i_ddi_intr_get_pool()
9868561SScott.Carter@Sun.COM  *
9878561SScott.Carter@Sun.COM  *	Get an IRM pool that supplies interrupts of a specified type.
9888561SScott.Carter@Sun.COM  *	Invokes a DDI_INTROP_GETPOOL to the bus nexus driver.  Fails
9898561SScott.Carter@Sun.COM  *	if no pool exists.
9908561SScott.Carter@Sun.COM  */
9918561SScott.Carter@Sun.COM ddi_irm_pool_t *
9928561SScott.Carter@Sun.COM i_ddi_intr_get_pool(dev_info_t *dip, int type)
9938561SScott.Carter@Sun.COM {
9948561SScott.Carter@Sun.COM 	devinfo_intr_t		*intr_p;
9958561SScott.Carter@Sun.COM 	ddi_irm_pool_t		*pool_p;
9968561SScott.Carter@Sun.COM 	ddi_irm_req_t		*req_p;
9978561SScott.Carter@Sun.COM 	ddi_intr_handle_impl_t	hdl;
9988561SScott.Carter@Sun.COM 
9998561SScott.Carter@Sun.COM 	ASSERT(dip != NULL);
10008561SScott.Carter@Sun.COM 	ASSERT(DDI_INTR_TYPE_FLAG_VALID(type));
10018561SScott.Carter@Sun.COM 
10028561SScott.Carter@Sun.COM 	if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) &&
10038561SScott.Carter@Sun.COM 	    ((req_p = intr_p->devi_irm_req_p) != NULL) &&
10048561SScott.Carter@Sun.COM 	    ((pool_p = req_p->ireq_pool_p) != NULL) &&
10058561SScott.Carter@Sun.COM 	    (pool_p->ipool_types & type)) {
10068561SScott.Carter@Sun.COM 		return (pool_p);
10078561SScott.Carter@Sun.COM 	}
10088561SScott.Carter@Sun.COM 
10098561SScott.Carter@Sun.COM 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
10108561SScott.Carter@Sun.COM 	hdl.ih_dip = dip;
10118561SScott.Carter@Sun.COM 	hdl.ih_type = type;
10128561SScott.Carter@Sun.COM 
10138561SScott.Carter@Sun.COM 	if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL,
10148561SScott.Carter@Sun.COM 	    &hdl, (void *)&pool_p) == DDI_SUCCESS)
10158561SScott.Carter@Sun.COM 		return (pool_p);
10168561SScott.Carter@Sun.COM 
10178561SScott.Carter@Sun.COM 	return (NULL);
10188561SScott.Carter@Sun.COM }
10198561SScott.Carter@Sun.COM 
10208561SScott.Carter@Sun.COM /*
10218561SScott.Carter@Sun.COM  * i_ddi_irm_insertion_sort()
10228561SScott.Carter@Sun.COM  *
10238561SScott.Carter@Sun.COM  *	Use the insertion sort method to insert a request into a list.
10248561SScott.Carter@Sun.COM  *	The list is sorted in descending order by request size.
10258561SScott.Carter@Sun.COM  */
10268561SScott.Carter@Sun.COM static void
10278561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p)
10288561SScott.Carter@Sun.COM {
10298561SScott.Carter@Sun.COM 	ddi_irm_req_t	*next_p;
10308561SScott.Carter@Sun.COM 
10318561SScott.Carter@Sun.COM 	next_p = list_head(req_list);
10328561SScott.Carter@Sun.COM 
10338561SScott.Carter@Sun.COM 	while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq))
10348561SScott.Carter@Sun.COM 		next_p = list_next(req_list, next_p);
10358561SScott.Carter@Sun.COM 
10368561SScott.Carter@Sun.COM 	list_insert_before(req_list, next_p, req_p);
10378561SScott.Carter@Sun.COM }
10388561SScott.Carter@Sun.COM 
10398561SScott.Carter@Sun.COM /*
10408561SScott.Carter@Sun.COM  * i_ddi_irm_notify()
10418561SScott.Carter@Sun.COM  *
10428561SScott.Carter@Sun.COM  *	Notify a driver of changes to its interrupt request using the
10438561SScott.Carter@Sun.COM  *	generic callback mechanism.  Checks for errors in processing.
10448561SScott.Carter@Sun.COM  */
10458561SScott.Carter@Sun.COM static int
10468561SScott.Carter@Sun.COM i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
10478561SScott.Carter@Sun.COM {
10488561SScott.Carter@Sun.COM 	ddi_cb_action_t	action;
10498561SScott.Carter@Sun.COM 	ddi_cb_t	*cb_p;
10508561SScott.Carter@Sun.COM 	uint_t		nintrs;
10518561SScott.Carter@Sun.COM 	int		ret, count;
10528561SScott.Carter@Sun.COM 
10538561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n",
10548561SScott.Carter@Sun.COM 	    (void *)pool_p, (void *)req_p));
10558561SScott.Carter@Sun.COM 
10568561SScott.Carter@Sun.COM 	/* Do not notify new or unchanged requests */
10578561SScott.Carter@Sun.COM 	if ((req_p->ireq_navail == req_p->ireq_scratch) ||
10588561SScott.Carter@Sun.COM 	    (req_p->ireq_flags & DDI_IRM_FLAG_NEW))
10598561SScott.Carter@Sun.COM 		return (DDI_SUCCESS);
10608561SScott.Carter@Sun.COM 
10618561SScott.Carter@Sun.COM 	/* Determine action and count */
10628561SScott.Carter@Sun.COM 	if (req_p->ireq_navail > req_p->ireq_scratch) {
10638561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_ADD;
10648561SScott.Carter@Sun.COM 		count = req_p->ireq_navail - req_p->ireq_scratch;
10658561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n",
10668561SScott.Carter@Sun.COM 		    count));
10678561SScott.Carter@Sun.COM 	} else {
10688561SScott.Carter@Sun.COM 		action = DDI_CB_INTR_REMOVE;
10698561SScott.Carter@Sun.COM 		count = req_p->ireq_scratch - req_p->ireq_navail;
10708561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n",
10718561SScott.Carter@Sun.COM 		    count));
10728561SScott.Carter@Sun.COM 	}
10738561SScott.Carter@Sun.COM 
10748561SScott.Carter@Sun.COM 	/* Lookup driver callback */
10758561SScott.Carter@Sun.COM 	if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) {
10768561SScott.Carter@Sun.COM 		DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n"));
10778561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
10788561SScott.Carter@Sun.COM 	}
10798561SScott.Carter@Sun.COM 
10808561SScott.Carter@Sun.COM 	/* Do callback */
10818561SScott.Carter@Sun.COM 	ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count,
10828561SScott.Carter@Sun.COM 	    cb_p->cb_arg1, cb_p->cb_arg2);
10838561SScott.Carter@Sun.COM 
10848561SScott.Carter@Sun.COM 	/* Log callback errors */
10858561SScott.Carter@Sun.COM 	if (ret != DDI_SUCCESS) {
10868561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
10878561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
10888561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), (int)action, ret);
10898561SScott.Carter@Sun.COM 	}
10908561SScott.Carter@Sun.COM 
10918561SScott.Carter@Sun.COM 	/* Check if the driver exceeds its availability */
10928561SScott.Carter@Sun.COM 	nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip);
10938561SScott.Carter@Sun.COM 	if (nintrs > req_p->ireq_navail) {
10948561SScott.Carter@Sun.COM 		cmn_err(CE_WARN, "%s%d: failed to release interrupts "
10958561SScott.Carter@Sun.COM 		    "(nintrs=%d, navail=%d).\n",
10968561SScott.Carter@Sun.COM 		    ddi_driver_name(req_p->ireq_dip),
10978561SScott.Carter@Sun.COM 		    ddi_get_instance(req_p->ireq_dip), nintrs,
10988561SScott.Carter@Sun.COM 		    req_p->ireq_navail);
10998561SScott.Carter@Sun.COM 		pool_p->ipool_resno += (nintrs - req_p->ireq_navail);
11008561SScott.Carter@Sun.COM 		req_p->ireq_navail = nintrs;
11018561SScott.Carter@Sun.COM 		return (DDI_FAILURE);
11028561SScott.Carter@Sun.COM 	}
11038561SScott.Carter@Sun.COM 
11048561SScott.Carter@Sun.COM 	/* Update request */
11058561SScott.Carter@Sun.COM 	req_p->ireq_scratch = req_p->ireq_navail;
11068561SScott.Carter@Sun.COM 
11078561SScott.Carter@Sun.COM 	return (DDI_SUCCESS);
11088561SScott.Carter@Sun.COM }
11098561SScott.Carter@Sun.COM 
11108561SScott.Carter@Sun.COM /*
11118561SScott.Carter@Sun.COM  * i_ddi_irm_debug_balance()
11128561SScott.Carter@Sun.COM  *
11138561SScott.Carter@Sun.COM  *	A debug/test only routine to force the immediate,
11148561SScott.Carter@Sun.COM  *	synchronous rebalancing of an interrupt pool.
11158561SScott.Carter@Sun.COM  */
11168561SScott.Carter@Sun.COM #ifdef	DEBUG
11178561SScott.Carter@Sun.COM void
11188561SScott.Carter@Sun.COM i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag)
11198561SScott.Carter@Sun.COM {
11208561SScott.Carter@Sun.COM 	ddi_irm_pool_t	*pool_p;
11218561SScott.Carter@Sun.COM 	int		type;
11228561SScott.Carter@Sun.COM 
11238561SScott.Carter@Sun.COM 	DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n",
11248561SScott.Carter@Sun.COM 	    (void *)dip, (int)wait_flag));
11258561SScott.Carter@Sun.COM 
11268561SScott.Carter@Sun.COM 	if (((type = i_ddi_intr_get_current_type(dip)) != 0) &&
11278561SScott.Carter@Sun.COM 	    ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) {
11288561SScott.Carter@Sun.COM 		mutex_enter(&pool_p->ipool_lock);
11298561SScott.Carter@Sun.COM 		i_ddi_irm_enqueue(pool_p, wait_flag);
11308561SScott.Carter@Sun.COM 		mutex_exit(&pool_p->ipool_lock);
11318561SScott.Carter@Sun.COM 	}
11328561SScott.Carter@Sun.COM }
11338561SScott.Carter@Sun.COM #endif
1134