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 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 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 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 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 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 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 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 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 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 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 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 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 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