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