18561SScott.Carter@Sun.COM /* 28561SScott.Carter@Sun.COM * CDDL HEADER START 38561SScott.Carter@Sun.COM * 48561SScott.Carter@Sun.COM * The contents of this file are subject to the terms of the 58561SScott.Carter@Sun.COM * Common Development and Distribution License (the "License"). 68561SScott.Carter@Sun.COM * You may not use this file except in compliance with the License. 78561SScott.Carter@Sun.COM * 88561SScott.Carter@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98561SScott.Carter@Sun.COM * or http://www.opensolaris.org/os/licensing. 108561SScott.Carter@Sun.COM * See the License for the specific language governing permissions 118561SScott.Carter@Sun.COM * and limitations under the License. 128561SScott.Carter@Sun.COM * 138561SScott.Carter@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148561SScott.Carter@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158561SScott.Carter@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168561SScott.Carter@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178561SScott.Carter@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188561SScott.Carter@Sun.COM * 198561SScott.Carter@Sun.COM * CDDL HEADER END 208561SScott.Carter@Sun.COM */ 218561SScott.Carter@Sun.COM /* 2212473SScott.Carter@Oracle.COM * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 238561SScott.Carter@Sun.COM */ 248561SScott.Carter@Sun.COM 258561SScott.Carter@Sun.COM #include <sys/note.h> 268561SScott.Carter@Sun.COM #include <sys/sysmacros.h> 278561SScott.Carter@Sun.COM #include <sys/types.h> 288561SScott.Carter@Sun.COM #include <sys/param.h> 298561SScott.Carter@Sun.COM #include <sys/systm.h> 308561SScott.Carter@Sun.COM #include <sys/kmem.h> 318561SScott.Carter@Sun.COM #include <sys/cmn_err.h> 328561SScott.Carter@Sun.COM #include <sys/debug.h> 338561SScott.Carter@Sun.COM #include <sys/ddi.h> 348561SScott.Carter@Sun.COM #include <sys/sunndi.h> 358561SScott.Carter@Sun.COM #include <sys/ndi_impldefs.h> /* include prototypes */ 368561SScott.Carter@Sun.COM 37*12683SJimmy.Vetayases@oracle.com #if defined(__i386) || defined(__amd64) 38*12683SJimmy.Vetayases@oracle.com /* 39*12683SJimmy.Vetayases@oracle.com * MSI-X allocation limit. 40*12683SJimmy.Vetayases@oracle.com */ 41*12683SJimmy.Vetayases@oracle.com extern uint_t ddi_msix_alloc_limit; 42*12683SJimmy.Vetayases@oracle.com #endif 43*12683SJimmy.Vetayases@oracle.com 448561SScott.Carter@Sun.COM /* 458561SScott.Carter@Sun.COM * Interrupt Resource Management (IRM). 468561SScott.Carter@Sun.COM */ 478561SScott.Carter@Sun.COM 488561SScott.Carter@Sun.COM #define DDI_IRM_BALANCE_DELAY (60) /* In seconds */ 498561SScott.Carter@Sun.COM 508561SScott.Carter@Sun.COM #define DDI_IRM_HAS_CB(c) ((c) && (c->cb_flags & DDI_CB_FLAG_INTR)) 518561SScott.Carter@Sun.COM 528561SScott.Carter@Sun.COM #define DDI_IRM_IS_REDUCIBLE(r) (((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \ 538561SScott.Carter@Sun.COM (r->ireq_type == DDI_INTR_TYPE_MSIX)) || \ 548561SScott.Carter@Sun.COM (r->ireq_flags & DDI_IRM_FLAG_NEW)) 558561SScott.Carter@Sun.COM 568561SScott.Carter@Sun.COM extern pri_t minclsyspri; 578561SScott.Carter@Sun.COM 588561SScott.Carter@Sun.COM /* Global policies */ 598561SScott.Carter@Sun.COM int irm_enable = 1; 608561SScott.Carter@Sun.COM boolean_t irm_active = B_FALSE; 618561SScott.Carter@Sun.COM int irm_default_policy = DDI_IRM_POLICY_LARGE; 628561SScott.Carter@Sun.COM uint_t irm_balance_delay = DDI_IRM_BALANCE_DELAY; 638561SScott.Carter@Sun.COM 648561SScott.Carter@Sun.COM /* Global list of interrupt pools */ 658561SScott.Carter@Sun.COM kmutex_t irm_pools_lock; 668561SScott.Carter@Sun.COM list_t irm_pools_list; 678561SScott.Carter@Sun.COM 688561SScott.Carter@Sun.COM /* Global debug tunables */ 698561SScott.Carter@Sun.COM #ifdef DEBUG 708561SScott.Carter@Sun.COM int irm_debug_policy = 0; 718561SScott.Carter@Sun.COM uint_t irm_debug_size = 0; 728561SScott.Carter@Sun.COM #endif /* DEBUG */ 738561SScott.Carter@Sun.COM 748561SScott.Carter@Sun.COM static void irm_balance_thread(ddi_irm_pool_t *); 758561SScott.Carter@Sun.COM static void i_ddi_irm_balance(ddi_irm_pool_t *); 768561SScott.Carter@Sun.COM static void i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t); 778561SScott.Carter@Sun.COM static void i_ddi_irm_reduce(ddi_irm_pool_t *pool); 7810173SEvan.Yan@Sun.COM static int i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *, int, int); 798561SScott.Carter@Sun.COM static void i_ddi_irm_reduce_new(ddi_irm_pool_t *, int); 808561SScott.Carter@Sun.COM static void i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *); 818561SScott.Carter@Sun.COM static int i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *); 8212473SScott.Carter@Oracle.COM static int i_ddi_irm_modify_increase(ddi_irm_req_t *, int); 838561SScott.Carter@Sun.COM 848561SScott.Carter@Sun.COM /* 858561SScott.Carter@Sun.COM * OS Initialization Routines 868561SScott.Carter@Sun.COM */ 878561SScott.Carter@Sun.COM 888561SScott.Carter@Sun.COM /* 898561SScott.Carter@Sun.COM * irm_init() 908561SScott.Carter@Sun.COM * 918561SScott.Carter@Sun.COM * Initialize IRM subsystem before any drivers are attached. 928561SScott.Carter@Sun.COM */ 938561SScott.Carter@Sun.COM void 948561SScott.Carter@Sun.COM irm_init(void) 958561SScott.Carter@Sun.COM { 968561SScott.Carter@Sun.COM /* Do nothing if IRM is disabled */ 978561SScott.Carter@Sun.COM if (!irm_enable) 988561SScott.Carter@Sun.COM return; 998561SScott.Carter@Sun.COM 1008561SScott.Carter@Sun.COM /* Verify that the default balancing policy is valid */ 1018561SScott.Carter@Sun.COM if (!DDI_IRM_POLICY_VALID(irm_default_policy)) 1028561SScott.Carter@Sun.COM irm_default_policy = DDI_IRM_POLICY_LARGE; 1038561SScott.Carter@Sun.COM 1048561SScott.Carter@Sun.COM /* Initialize the global list of interrupt pools */ 1058561SScott.Carter@Sun.COM mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL); 1068561SScott.Carter@Sun.COM list_create(&irm_pools_list, sizeof (ddi_irm_pool_t), 1078561SScott.Carter@Sun.COM offsetof(ddi_irm_pool_t, ipool_link)); 1088561SScott.Carter@Sun.COM } 1098561SScott.Carter@Sun.COM 1108561SScott.Carter@Sun.COM /* 1118561SScott.Carter@Sun.COM * i_ddi_irm_poststartup() 1128561SScott.Carter@Sun.COM * 1138561SScott.Carter@Sun.COM * IRM is not activated until after the IO subsystem is initialized. 1148561SScott.Carter@Sun.COM * When activated, per-pool balancing threads are spawned and a flag 1158561SScott.Carter@Sun.COM * is set so that all future pools will be activated when created. 1168561SScott.Carter@Sun.COM * 1178561SScott.Carter@Sun.COM * NOTE: the global variable 'irm_enable' disables IRM if zero. 1188561SScott.Carter@Sun.COM */ 1198561SScott.Carter@Sun.COM void 1208561SScott.Carter@Sun.COM i_ddi_irm_poststartup(void) 1218561SScott.Carter@Sun.COM { 1228561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 1238561SScott.Carter@Sun.COM 1248561SScott.Carter@Sun.COM /* Do nothing if IRM is disabled */ 1258561SScott.Carter@Sun.COM if (!irm_enable) 1268561SScott.Carter@Sun.COM return; 1278561SScott.Carter@Sun.COM 1288561SScott.Carter@Sun.COM /* Lock the global list */ 1298561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 1308561SScott.Carter@Sun.COM 1318561SScott.Carter@Sun.COM /* Activate all defined pools */ 1328561SScott.Carter@Sun.COM for (pool_p = list_head(&irm_pools_list); pool_p; 1338561SScott.Carter@Sun.COM pool_p = list_next(&irm_pools_list, pool_p)) 1348561SScott.Carter@Sun.COM pool_p->ipool_thread = thread_create(NULL, 0, 1358561SScott.Carter@Sun.COM irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 1368561SScott.Carter@Sun.COM 1378561SScott.Carter@Sun.COM /* Set future pools to be active */ 1388561SScott.Carter@Sun.COM irm_active = B_TRUE; 1398561SScott.Carter@Sun.COM 1408561SScott.Carter@Sun.COM /* Unlock the global list */ 1418561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 1428561SScott.Carter@Sun.COM } 1438561SScott.Carter@Sun.COM 1448561SScott.Carter@Sun.COM /* 1458561SScott.Carter@Sun.COM * NDI interfaces for creating/destroying IRM pools. 1468561SScott.Carter@Sun.COM */ 1478561SScott.Carter@Sun.COM 1488561SScott.Carter@Sun.COM /* 1498561SScott.Carter@Sun.COM * ndi_irm_create() 1508561SScott.Carter@Sun.COM * 1518561SScott.Carter@Sun.COM * Nexus interface to create an IRM pool. Create the new 1528561SScott.Carter@Sun.COM * pool and add it to the global list of interrupt pools. 1538561SScott.Carter@Sun.COM */ 1548561SScott.Carter@Sun.COM int 1558561SScott.Carter@Sun.COM ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp, 1568561SScott.Carter@Sun.COM ddi_irm_pool_t **pool_retp) 1578561SScott.Carter@Sun.COM { 1588561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 1598561SScott.Carter@Sun.COM 1608561SScott.Carter@Sun.COM ASSERT(dip != NULL); 1618561SScott.Carter@Sun.COM ASSERT(paramsp != NULL); 1628561SScott.Carter@Sun.COM ASSERT(pool_retp != NULL); 1638561SScott.Carter@Sun.COM ASSERT(paramsp->iparams_total >= 1); 1648561SScott.Carter@Sun.COM ASSERT(paramsp->iparams_types != 0); 1658561SScott.Carter@Sun.COM 1668561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip)); 1678561SScott.Carter@Sun.COM 1688561SScott.Carter@Sun.COM /* Check if IRM is enabled */ 1698561SScott.Carter@Sun.COM if (!irm_enable) 1708561SScott.Carter@Sun.COM return (NDI_FAILURE); 1718561SScott.Carter@Sun.COM 1728561SScott.Carter@Sun.COM /* Validate parameters */ 1738561SScott.Carter@Sun.COM if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) || 1748925SEvan.Yan@Sun.COM (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0)) 1758561SScott.Carter@Sun.COM return (NDI_FAILURE); 1768561SScott.Carter@Sun.COM 1778561SScott.Carter@Sun.COM /* Allocate and initialize the pool */ 1788561SScott.Carter@Sun.COM pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP); 1798561SScott.Carter@Sun.COM pool_p->ipool_owner = dip; 1808561SScott.Carter@Sun.COM pool_p->ipool_policy = irm_default_policy; 1818561SScott.Carter@Sun.COM pool_p->ipool_types = paramsp->iparams_types; 1828561SScott.Carter@Sun.COM pool_p->ipool_totsz = paramsp->iparams_total; 1838925SEvan.Yan@Sun.COM pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC, 1848925SEvan.Yan@Sun.COM paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER)); 1858561SScott.Carter@Sun.COM list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t), 1868561SScott.Carter@Sun.COM offsetof(ddi_irm_req_t, ireq_link)); 1878561SScott.Carter@Sun.COM list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t), 1888561SScott.Carter@Sun.COM offsetof(ddi_irm_req_t, ireq_scratch_link)); 1898561SScott.Carter@Sun.COM cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL); 1908561SScott.Carter@Sun.COM mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL); 1918561SScott.Carter@Sun.COM mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL); 1928561SScott.Carter@Sun.COM 1938561SScott.Carter@Sun.COM /* Add to global list of pools */ 1948561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 1958561SScott.Carter@Sun.COM list_insert_tail(&irm_pools_list, pool_p); 1968561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 1978561SScott.Carter@Sun.COM 1988561SScott.Carter@Sun.COM /* If IRM is active, then activate the pool */ 1998561SScott.Carter@Sun.COM if (irm_active) 2008561SScott.Carter@Sun.COM pool_p->ipool_thread = thread_create(NULL, 0, 2018561SScott.Carter@Sun.COM irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 2028561SScott.Carter@Sun.COM 2038561SScott.Carter@Sun.COM *pool_retp = pool_p; 2048561SScott.Carter@Sun.COM return (NDI_SUCCESS); 2058561SScott.Carter@Sun.COM } 2068561SScott.Carter@Sun.COM 2078561SScott.Carter@Sun.COM /* 208*12683SJimmy.Vetayases@oracle.com * ndi_irm_resize_pool() 209*12683SJimmy.Vetayases@oracle.com * 210*12683SJimmy.Vetayases@oracle.com * Nexus interface to resize IRM pool. If the pool size drops 211*12683SJimmy.Vetayases@oracle.com * below the allocated number of vectors then initiate rebalance 212*12683SJimmy.Vetayases@oracle.com * operation before resizing the pool. If rebalance operation fails 213*12683SJimmy.Vetayases@oracle.com * then return NDI_FAILURE. 214*12683SJimmy.Vetayases@oracle.com */ 215*12683SJimmy.Vetayases@oracle.com int 216*12683SJimmy.Vetayases@oracle.com ndi_irm_resize_pool(ddi_irm_pool_t *pool_p, uint_t new_size) 217*12683SJimmy.Vetayases@oracle.com { 218*12683SJimmy.Vetayases@oracle.com uint_t prev_size; 219*12683SJimmy.Vetayases@oracle.com 220*12683SJimmy.Vetayases@oracle.com ASSERT(pool_p != NULL); 221*12683SJimmy.Vetayases@oracle.com 222*12683SJimmy.Vetayases@oracle.com DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 223*12683SJimmy.Vetayases@oracle.com " current-size 0x%x new-size 0x%x\n", 224*12683SJimmy.Vetayases@oracle.com (void *)pool_p, pool_p->ipool_totsz, new_size)); 225*12683SJimmy.Vetayases@oracle.com 226*12683SJimmy.Vetayases@oracle.com if (pool_p == NULL) 227*12683SJimmy.Vetayases@oracle.com return (NDI_EINVAL); 228*12683SJimmy.Vetayases@oracle.com 229*12683SJimmy.Vetayases@oracle.com /* Check if IRM is enabled */ 230*12683SJimmy.Vetayases@oracle.com if (!irm_enable) 231*12683SJimmy.Vetayases@oracle.com return (NDI_FAILURE); 232*12683SJimmy.Vetayases@oracle.com 233*12683SJimmy.Vetayases@oracle.com mutex_enter(&pool_p->ipool_lock); 234*12683SJimmy.Vetayases@oracle.com 235*12683SJimmy.Vetayases@oracle.com /* 236*12683SJimmy.Vetayases@oracle.com * If we are increasing the pool size or if the reserved 237*12683SJimmy.Vetayases@oracle.com * number of vectors is <= the new pool size then simply 238*12683SJimmy.Vetayases@oracle.com * update the pool size and enqueue a reblance operation 239*12683SJimmy.Vetayases@oracle.com * if necessary to use the new vectors. 240*12683SJimmy.Vetayases@oracle.com */ 241*12683SJimmy.Vetayases@oracle.com if ((pool_p->ipool_totsz < new_size) || 242*12683SJimmy.Vetayases@oracle.com (pool_p->ipool_resno <= new_size)) { 243*12683SJimmy.Vetayases@oracle.com /* set new pool size */ 244*12683SJimmy.Vetayases@oracle.com pool_p->ipool_totsz = new_size; 245*12683SJimmy.Vetayases@oracle.com /* adjust the default allocation limit */ 246*12683SJimmy.Vetayases@oracle.com pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, 247*12683SJimmy.Vetayases@oracle.com MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER)); 248*12683SJimmy.Vetayases@oracle.com /* queue a rebalance operation to use the new vectors */ 249*12683SJimmy.Vetayases@oracle.com if (pool_p->ipool_reqno > pool_p->ipool_resno) 250*12683SJimmy.Vetayases@oracle.com i_ddi_irm_enqueue(pool_p, B_FALSE); 251*12683SJimmy.Vetayases@oracle.com mutex_exit(&pool_p->ipool_lock); 252*12683SJimmy.Vetayases@oracle.com return (NDI_SUCCESS); 253*12683SJimmy.Vetayases@oracle.com } 254*12683SJimmy.Vetayases@oracle.com 255*12683SJimmy.Vetayases@oracle.com DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 256*12683SJimmy.Vetayases@oracle.com " needs a rebalance operation\n", (void *)pool_p)); 257*12683SJimmy.Vetayases@oracle.com 258*12683SJimmy.Vetayases@oracle.com /* 259*12683SJimmy.Vetayases@oracle.com * requires a rebalance operation 260*12683SJimmy.Vetayases@oracle.com */ 261*12683SJimmy.Vetayases@oracle.com /* save the current pool size */ 262*12683SJimmy.Vetayases@oracle.com prev_size = pool_p->ipool_totsz; 263*12683SJimmy.Vetayases@oracle.com /* set the pool size to the desired new value */ 264*12683SJimmy.Vetayases@oracle.com pool_p->ipool_totsz = new_size; 265*12683SJimmy.Vetayases@oracle.com /* perform the rebalance operation */ 266*12683SJimmy.Vetayases@oracle.com i_ddi_irm_enqueue(pool_p, B_TRUE); 267*12683SJimmy.Vetayases@oracle.com 268*12683SJimmy.Vetayases@oracle.com /* 269*12683SJimmy.Vetayases@oracle.com * If rebalance operation couldn't free up enough 270*12683SJimmy.Vetayases@oracle.com * vectors then fail the resize operation. 271*12683SJimmy.Vetayases@oracle.com */ 272*12683SJimmy.Vetayases@oracle.com if (pool_p->ipool_resno > new_size) { /* rebalance failed */ 273*12683SJimmy.Vetayases@oracle.com /* restore the pool size to the previous value */ 274*12683SJimmy.Vetayases@oracle.com pool_p->ipool_totsz = prev_size; 275*12683SJimmy.Vetayases@oracle.com /* enqueue a rebalance operation for the original pool size */ 276*12683SJimmy.Vetayases@oracle.com i_ddi_irm_enqueue(pool_p, B_FALSE); 277*12683SJimmy.Vetayases@oracle.com mutex_exit(&pool_p->ipool_lock); 278*12683SJimmy.Vetayases@oracle.com return (NDI_FAILURE); 279*12683SJimmy.Vetayases@oracle.com } else { /* rebalance worked */ 280*12683SJimmy.Vetayases@oracle.com /* adjust the default allocation limit */ 281*12683SJimmy.Vetayases@oracle.com pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, 282*12683SJimmy.Vetayases@oracle.com MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER)); 283*12683SJimmy.Vetayases@oracle.com mutex_exit(&pool_p->ipool_lock); 284*12683SJimmy.Vetayases@oracle.com DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 285*12683SJimmy.Vetayases@oracle.com " resized from %x to %x\n", 286*12683SJimmy.Vetayases@oracle.com (void *)pool_p, prev_size, pool_p->ipool_totsz)); 287*12683SJimmy.Vetayases@oracle.com return (NDI_SUCCESS); 288*12683SJimmy.Vetayases@oracle.com } 289*12683SJimmy.Vetayases@oracle.com } 290*12683SJimmy.Vetayases@oracle.com 291*12683SJimmy.Vetayases@oracle.com /* 2928561SScott.Carter@Sun.COM * ndi_irm_destroy() 2938561SScott.Carter@Sun.COM * 2948561SScott.Carter@Sun.COM * Nexus interface to destroy an IRM pool. Destroy the pool 2958561SScott.Carter@Sun.COM * and remove it from the global list of interrupt pools. 2968561SScott.Carter@Sun.COM */ 2978561SScott.Carter@Sun.COM int 2988561SScott.Carter@Sun.COM ndi_irm_destroy(ddi_irm_pool_t *pool_p) 2998561SScott.Carter@Sun.COM { 3008561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 3018561SScott.Carter@Sun.COM ASSERT(pool_p->ipool_resno == 0); 3028561SScott.Carter@Sun.COM 3038561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n", 3048561SScott.Carter@Sun.COM (void *)pool_p)); 3058561SScott.Carter@Sun.COM 3068561SScott.Carter@Sun.COM /* Validate parameters */ 3078561SScott.Carter@Sun.COM if (pool_p == NULL) 3088561SScott.Carter@Sun.COM return (NDI_FAILURE); 3098561SScott.Carter@Sun.COM 3108561SScott.Carter@Sun.COM /* Validate that pool is empty */ 3118561SScott.Carter@Sun.COM if (pool_p->ipool_resno != 0) 3128561SScott.Carter@Sun.COM return (NDI_BUSY); 3138561SScott.Carter@Sun.COM 3148561SScott.Carter@Sun.COM /* Remove the pool from the global list */ 3158561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 3168561SScott.Carter@Sun.COM list_remove(&irm_pools_list, pool_p); 3178561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 3188561SScott.Carter@Sun.COM 3198561SScott.Carter@Sun.COM /* Terminate the balancing thread */ 3208561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 3218561SScott.Carter@Sun.COM if (pool_p->ipool_thread && 3228561SScott.Carter@Sun.COM (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) { 3238561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT; 3248561SScott.Carter@Sun.COM cv_signal(&pool_p->ipool_cv); 3258885SJustin.Frank@Sun.COM mutex_exit(&pool_p->ipool_lock); 3268561SScott.Carter@Sun.COM thread_join(pool_p->ipool_thread->t_did); 3278885SJustin.Frank@Sun.COM } else 3288885SJustin.Frank@Sun.COM mutex_exit(&pool_p->ipool_lock); 3298561SScott.Carter@Sun.COM 3308561SScott.Carter@Sun.COM /* Destroy the pool */ 3318561SScott.Carter@Sun.COM cv_destroy(&pool_p->ipool_cv); 3328561SScott.Carter@Sun.COM mutex_destroy(&pool_p->ipool_lock); 3338561SScott.Carter@Sun.COM mutex_destroy(&pool_p->ipool_navail_lock); 3348561SScott.Carter@Sun.COM list_destroy(&pool_p->ipool_req_list); 3358561SScott.Carter@Sun.COM list_destroy(&pool_p->ipool_scratch_list); 3368561SScott.Carter@Sun.COM kmem_free(pool_p, sizeof (ddi_irm_pool_t)); 3378561SScott.Carter@Sun.COM 3388561SScott.Carter@Sun.COM return (NDI_SUCCESS); 3398561SScott.Carter@Sun.COM } 3408561SScott.Carter@Sun.COM 3418561SScott.Carter@Sun.COM /* 3428561SScott.Carter@Sun.COM * Insert/Modify/Remove Interrupt Requests 3438561SScott.Carter@Sun.COM */ 3448561SScott.Carter@Sun.COM 3458561SScott.Carter@Sun.COM /* 3468561SScott.Carter@Sun.COM * i_ddi_irm_insert() 3478561SScott.Carter@Sun.COM * 3488561SScott.Carter@Sun.COM * Insert a new request into an interrupt pool, and balance the pool. 3498561SScott.Carter@Sun.COM */ 3508561SScott.Carter@Sun.COM int 3518561SScott.Carter@Sun.COM i_ddi_irm_insert(dev_info_t *dip, int type, int count) 3528561SScott.Carter@Sun.COM { 3538561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 3548561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 3558561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 3568561SScott.Carter@Sun.COM uint_t nreq, nmin, npartial; 3578561SScott.Carter@Sun.COM boolean_t irm_flag = B_FALSE; 3588561SScott.Carter@Sun.COM 3598561SScott.Carter@Sun.COM ASSERT(dip != NULL); 3608561SScott.Carter@Sun.COM ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 3618561SScott.Carter@Sun.COM ASSERT(count > 0); 3628561SScott.Carter@Sun.COM 3638561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n", 3648561SScott.Carter@Sun.COM (void *)dip, type, count)); 3658561SScott.Carter@Sun.COM 3668561SScott.Carter@Sun.COM /* Validate parameters */ 3678561SScott.Carter@Sun.COM if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) { 3688561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n")); 3698561SScott.Carter@Sun.COM return (DDI_EINVAL); 3708561SScott.Carter@Sun.COM } 3718561SScott.Carter@Sun.COM 3728561SScott.Carter@Sun.COM /* Check for an existing request */ 3738561SScott.Carter@Sun.COM if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 3748561SScott.Carter@Sun.COM (intr_p->devi_irm_req_p != NULL)) 3758561SScott.Carter@Sun.COM return (DDI_SUCCESS); 3768561SScott.Carter@Sun.COM 3778561SScott.Carter@Sun.COM /* Check for IRM support from the system */ 3788561SScott.Carter@Sun.COM if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) { 3798561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n")); 3808561SScott.Carter@Sun.COM return (DDI_ENOTSUP); 3818561SScott.Carter@Sun.COM } 3828561SScott.Carter@Sun.COM 3838561SScott.Carter@Sun.COM /* Check for IRM support from the driver */ 38412473SScott.Carter@Oracle.COM if (i_ddi_irm_supported(dip, type) == DDI_SUCCESS) 3858561SScott.Carter@Sun.COM irm_flag = B_TRUE; 3868561SScott.Carter@Sun.COM 3878561SScott.Carter@Sun.COM /* Determine request size */ 3889205SEvan.Yan@Sun.COM nreq = (irm_flag) ? count : 38912473SScott.Carter@Oracle.COM MIN(count, i_ddi_intr_get_limit(dip, type, pool_p)); 3908561SScott.Carter@Sun.COM nmin = (irm_flag) ? 1 : nreq; 3918561SScott.Carter@Sun.COM npartial = MIN(nreq, pool_p->ipool_defsz); 3928561SScott.Carter@Sun.COM 3938561SScott.Carter@Sun.COM /* Allocate and initialize the request */ 3948561SScott.Carter@Sun.COM req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP); 3958561SScott.Carter@Sun.COM req_p->ireq_type = type; 3968561SScott.Carter@Sun.COM req_p->ireq_dip = dip; 3978561SScott.Carter@Sun.COM req_p->ireq_pool_p = pool_p; 3988561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 3998561SScott.Carter@Sun.COM req_p->ireq_flags = DDI_IRM_FLAG_NEW; 40012473SScott.Carter@Oracle.COM if (irm_flag) 4018561SScott.Carter@Sun.COM req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 4028561SScott.Carter@Sun.COM 4038561SScott.Carter@Sun.COM /* Lock the pool */ 4048561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 4058561SScott.Carter@Sun.COM 4068561SScott.Carter@Sun.COM /* Check for minimal fit before inserting */ 4078561SScott.Carter@Sun.COM if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) { 4088561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 4098561SScott.Carter@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip)); 4108561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 4118561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 4128561SScott.Carter@Sun.COM return (DDI_EAGAIN); 4138561SScott.Carter@Sun.COM } 4148561SScott.Carter@Sun.COM 4158561SScott.Carter@Sun.COM /* Insert the request into the pool */ 4168561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 4178561SScott.Carter@Sun.COM pool_p->ipool_minno += nmin; 4188561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 4198561SScott.Carter@Sun.COM 4208561SScott.Carter@Sun.COM /* 4218561SScott.Carter@Sun.COM * Try to fulfill the request. 4228561SScott.Carter@Sun.COM * 4238561SScott.Carter@Sun.COM * If all the interrupts are available, and either the request 4248561SScott.Carter@Sun.COM * is static or the pool is active, then just take them directly. 4258561SScott.Carter@Sun.COM * 4268561SScott.Carter@Sun.COM * If only some of the interrupts are available, and the request 4278561SScott.Carter@Sun.COM * can receive future callbacks, then take some now but queue the 4288561SScott.Carter@Sun.COM * pool to be rebalanced later. 4298561SScott.Carter@Sun.COM * 4308561SScott.Carter@Sun.COM * Otherwise, immediately rebalance the pool and wait. 4318561SScott.Carter@Sun.COM */ 4328561SScott.Carter@Sun.COM if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) && 4338561SScott.Carter@Sun.COM ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) { 4348561SScott.Carter@Sun.COM 4358561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 4368561SScott.Carter@Sun.COM "request completely fulfilled.\n")); 4378561SScott.Carter@Sun.COM pool_p->ipool_resno += nreq; 4388561SScott.Carter@Sun.COM req_p->ireq_navail = nreq; 4398561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 4408561SScott.Carter@Sun.COM 4418561SScott.Carter@Sun.COM } else if (irm_flag && 4428561SScott.Carter@Sun.COM ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) { 4438561SScott.Carter@Sun.COM 4448561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 4458561SScott.Carter@Sun.COM "request partially fulfilled.\n")); 4468561SScott.Carter@Sun.COM pool_p->ipool_resno += npartial; 4478561SScott.Carter@Sun.COM req_p->ireq_navail = npartial; 4488561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 4498561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 4508561SScott.Carter@Sun.COM 4518561SScott.Carter@Sun.COM } else { 4528561SScott.Carter@Sun.COM 4538561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 4548561SScott.Carter@Sun.COM "request needs immediate rebalance.\n")); 4558561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_TRUE); 4568561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 4578561SScott.Carter@Sun.COM } 4588561SScott.Carter@Sun.COM 4598561SScott.Carter@Sun.COM /* Fail if the request cannot be fulfilled at all */ 4608561SScott.Carter@Sun.COM if (req_p->ireq_navail == 0) { 4618561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 4628561SScott.Carter@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip)); 4638561SScott.Carter@Sun.COM pool_p->ipool_reqno -= nreq; 4648561SScott.Carter@Sun.COM pool_p->ipool_minno -= nmin; 4658561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 46610173SEvan.Yan@Sun.COM mutex_exit(&pool_p->ipool_lock); 4678561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 4688561SScott.Carter@Sun.COM return (DDI_EAGAIN); 4698561SScott.Carter@Sun.COM } 4708561SScott.Carter@Sun.COM 4718561SScott.Carter@Sun.COM /* Unlock the pool */ 4728561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 4738561SScott.Carter@Sun.COM 4748561SScott.Carter@Sun.COM intr_p->devi_irm_req_p = req_p; 4758561SScott.Carter@Sun.COM return (DDI_SUCCESS); 4768561SScott.Carter@Sun.COM } 4778561SScott.Carter@Sun.COM 4788561SScott.Carter@Sun.COM /* 4798561SScott.Carter@Sun.COM * i_ddi_irm_modify() 4808561SScott.Carter@Sun.COM * 4818561SScott.Carter@Sun.COM * Modify an existing request in an interrupt pool, and balance the pool. 4828561SScott.Carter@Sun.COM */ 4838561SScott.Carter@Sun.COM int 4848561SScott.Carter@Sun.COM i_ddi_irm_modify(dev_info_t *dip, int nreq) 4858561SScott.Carter@Sun.COM { 4868561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 4878561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 4888561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 48912473SScott.Carter@Oracle.COM int type; 49012473SScott.Carter@Oracle.COM int retval = DDI_SUCCESS; 4918561SScott.Carter@Sun.COM 4928561SScott.Carter@Sun.COM ASSERT(dip != NULL); 49312473SScott.Carter@Oracle.COM ASSERT(nreq > 0); 4948561SScott.Carter@Sun.COM 4958561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n", 4968561SScott.Carter@Sun.COM (void *)dip, nreq)); 4978561SScott.Carter@Sun.COM 4988561SScott.Carter@Sun.COM /* Validate parameters */ 4998561SScott.Carter@Sun.COM if ((dip == NULL) || (nreq < 1)) { 5008561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 5018561SScott.Carter@Sun.COM return (DDI_EINVAL); 5028561SScott.Carter@Sun.COM } 5038561SScott.Carter@Sun.COM 50412473SScott.Carter@Oracle.COM /* Do nothing if not mapped to an IRM pool */ 50512473SScott.Carter@Oracle.COM if (((intr_p = DEVI(dip)->devi_intr_p) == NULL) || 50612473SScott.Carter@Oracle.COM ((req_p = intr_p->devi_irm_req_p) == NULL)) 50712473SScott.Carter@Oracle.COM return (DDI_SUCCESS); 50812473SScott.Carter@Oracle.COM 50912473SScott.Carter@Oracle.COM /* Do nothing if new size is the same */ 51012473SScott.Carter@Oracle.COM if (nreq == req_p->ireq_nreq) 51112473SScott.Carter@Oracle.COM return (DDI_SUCCESS); 51212473SScott.Carter@Oracle.COM 51312473SScott.Carter@Oracle.COM /* Do not allow MSI requests to be resized */ 51412473SScott.Carter@Oracle.COM if ((type = req_p->ireq_type) == DDI_INTR_TYPE_MSI) { 51512473SScott.Carter@Oracle.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid type\n")); 5168561SScott.Carter@Sun.COM return (DDI_ENOTSUP); 5178561SScott.Carter@Sun.COM } 5188561SScott.Carter@Sun.COM 51912473SScott.Carter@Oracle.COM /* Select the pool */ 52012473SScott.Carter@Oracle.COM if ((pool_p = req_p->ireq_pool_p) == NULL) { 52112473SScott.Carter@Oracle.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: missing pool\n")); 52212473SScott.Carter@Oracle.COM return (DDI_FAILURE); 52312473SScott.Carter@Oracle.COM } 52412473SScott.Carter@Oracle.COM 5258561SScott.Carter@Sun.COM /* Validate request size is not too large */ 52612473SScott.Carter@Oracle.COM if (nreq > i_ddi_intr_get_limit(dip, type, pool_p)) { 5278561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 5288561SScott.Carter@Sun.COM return (DDI_EINVAL); 5298561SScott.Carter@Sun.COM } 5308561SScott.Carter@Sun.COM 53112473SScott.Carter@Oracle.COM /* Lock the pool */ 53212473SScott.Carter@Oracle.COM mutex_enter(&pool_p->ipool_lock); 53312473SScott.Carter@Oracle.COM 5348561SScott.Carter@Sun.COM /* 53512473SScott.Carter@Oracle.COM * Process the modification. 53612473SScott.Carter@Oracle.COM * 53712473SScott.Carter@Oracle.COM * - To increase a non-IRM request, call the implementation in 53812473SScott.Carter@Oracle.COM * i_ddi_irm_modify_increase(). 53912473SScott.Carter@Oracle.COM * 54012473SScott.Carter@Oracle.COM * - To decrease a non-IRM request, directly update the pool and 54112473SScott.Carter@Oracle.COM * request, then queue the pool for later rebalancing. 54212473SScott.Carter@Oracle.COM * 54312473SScott.Carter@Oracle.COM * - To modify an IRM request, always queue the pool for later 54412473SScott.Carter@Oracle.COM * rebalancing. IRM consumers rely upon callbacks for changes. 5458561SScott.Carter@Sun.COM */ 54612473SScott.Carter@Oracle.COM if ((nreq > req_p->ireq_nreq) && 54712473SScott.Carter@Oracle.COM (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)) { 5488561SScott.Carter@Sun.COM 54912473SScott.Carter@Oracle.COM retval = i_ddi_irm_modify_increase(req_p, nreq); 55012473SScott.Carter@Oracle.COM 55112473SScott.Carter@Oracle.COM } else { 5528561SScott.Carter@Sun.COM 5538561SScott.Carter@Sun.COM /* Update pool and request */ 5548561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 5558561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 55612473SScott.Carter@Oracle.COM if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS) { 55712473SScott.Carter@Oracle.COM pool_p->ipool_minno -= req_p->ireq_navail; 55812473SScott.Carter@Oracle.COM pool_p->ipool_resno -= req_p->ireq_navail; 55912473SScott.Carter@Oracle.COM pool_p->ipool_minno += nreq; 56012473SScott.Carter@Oracle.COM pool_p->ipool_resno += nreq; 56112473SScott.Carter@Oracle.COM req_p->ireq_navail = nreq; 56212473SScott.Carter@Oracle.COM } 5638561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 5648561SScott.Carter@Sun.COM 56512473SScott.Carter@Oracle.COM /* Re-sort request into the pool */ 5668561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 5678561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 5688561SScott.Carter@Sun.COM 56912473SScott.Carter@Oracle.COM /* Queue pool for asynchronous rebalance */ 5708561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 57112473SScott.Carter@Oracle.COM } 57212473SScott.Carter@Oracle.COM 57312473SScott.Carter@Oracle.COM /* Unlock the pool */ 57412473SScott.Carter@Oracle.COM mutex_exit(&pool_p->ipool_lock); 57512473SScott.Carter@Oracle.COM 57612473SScott.Carter@Oracle.COM return (retval); 57712473SScott.Carter@Oracle.COM } 57812473SScott.Carter@Oracle.COM 57912473SScott.Carter@Oracle.COM /* 58012473SScott.Carter@Oracle.COM * i_ddi_irm_modify_increase() 58112473SScott.Carter@Oracle.COM * 58212473SScott.Carter@Oracle.COM * Increase a non-IRM request. The additional interrupts are 58312473SScott.Carter@Oracle.COM * directly taken from the pool when possible. Otherwise, an 58412473SScott.Carter@Oracle.COM * immediate, synchronous rebalance is performed. A temporary 58512473SScott.Carter@Oracle.COM * proxy request is used for any rebalance operation to ensure 58612473SScott.Carter@Oracle.COM * the request is not reduced below its current allocation. 58712473SScott.Carter@Oracle.COM * 58812473SScott.Carter@Oracle.COM * NOTE: pool must already be locked. 58912473SScott.Carter@Oracle.COM */ 59012473SScott.Carter@Oracle.COM static int 59112473SScott.Carter@Oracle.COM i_ddi_irm_modify_increase(ddi_irm_req_t *req_p, int nreq) 59212473SScott.Carter@Oracle.COM { 59312473SScott.Carter@Oracle.COM dev_info_t *dip = req_p->ireq_dip; 59412473SScott.Carter@Oracle.COM ddi_irm_pool_t *pool_p = req_p->ireq_pool_p; 59512473SScott.Carter@Oracle.COM ddi_irm_req_t new_req; 59612473SScott.Carter@Oracle.COM int count, delta; 59712473SScott.Carter@Oracle.COM 59812473SScott.Carter@Oracle.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 59912473SScott.Carter@Oracle.COM 60012473SScott.Carter@Oracle.COM /* Compute number of additional vectors */ 60112473SScott.Carter@Oracle.COM count = nreq - req_p->ireq_nreq; 60212473SScott.Carter@Oracle.COM 60312473SScott.Carter@Oracle.COM /* Check for minimal fit */ 60412473SScott.Carter@Oracle.COM if ((pool_p->ipool_minno + count) > pool_p->ipool_totsz) { 60512473SScott.Carter@Oracle.COM cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 60612473SScott.Carter@Oracle.COM ddi_driver_name(dip), ddi_get_instance(dip)); 60712473SScott.Carter@Oracle.COM return (DDI_EAGAIN); 60812473SScott.Carter@Oracle.COM } 6098561SScott.Carter@Sun.COM 61012473SScott.Carter@Oracle.COM /* Update the pool */ 61112473SScott.Carter@Oracle.COM pool_p->ipool_reqno += count; 61212473SScott.Carter@Oracle.COM pool_p->ipool_minno += count; 61312473SScott.Carter@Oracle.COM 61412473SScott.Carter@Oracle.COM /* Attempt direct implementation */ 61512473SScott.Carter@Oracle.COM if ((pool_p->ipool_resno + count) <= pool_p->ipool_totsz) { 61612473SScott.Carter@Oracle.COM req_p->ireq_nreq += count; 61712473SScott.Carter@Oracle.COM req_p->ireq_navail += count; 61812473SScott.Carter@Oracle.COM pool_p->ipool_resno += count; 61912473SScott.Carter@Oracle.COM return (DDI_SUCCESS); 62012473SScott.Carter@Oracle.COM } 62112473SScott.Carter@Oracle.COM 62212473SScott.Carter@Oracle.COM /* Rebalance required: fail if pool is not active */ 62312473SScott.Carter@Oracle.COM if ((pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE) == 0) { 62412473SScott.Carter@Oracle.COM pool_p->ipool_reqno -= count; 62512473SScott.Carter@Oracle.COM pool_p->ipool_minno -= count; 62612473SScott.Carter@Oracle.COM return (DDI_EAGAIN); 6278561SScott.Carter@Sun.COM } 6288561SScott.Carter@Sun.COM 62912473SScott.Carter@Oracle.COM /* Insert temporary proxy request */ 63012473SScott.Carter@Oracle.COM bzero(&new_req, sizeof (ddi_irm_req_t)); 63112473SScott.Carter@Oracle.COM new_req.ireq_dip = dip; 63212473SScott.Carter@Oracle.COM new_req.ireq_nreq = count; 63312473SScott.Carter@Oracle.COM new_req.ireq_pool_p = pool_p; 63412473SScott.Carter@Oracle.COM new_req.ireq_type = req_p->ireq_type; 63512473SScott.Carter@Oracle.COM new_req.ireq_flags = DDI_IRM_FLAG_NEW; 63612473SScott.Carter@Oracle.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, &new_req); 63712473SScott.Carter@Oracle.COM 63812473SScott.Carter@Oracle.COM /* Synchronously rebalance */ 63912473SScott.Carter@Oracle.COM i_ddi_irm_enqueue(pool_p, B_TRUE); 64012473SScott.Carter@Oracle.COM 64112473SScott.Carter@Oracle.COM /* Remove proxy request, and merge into original request */ 64212473SScott.Carter@Oracle.COM req_p->ireq_nreq += count; 64312473SScott.Carter@Oracle.COM if ((delta = (count - new_req.ireq_navail)) > 0) { 64412473SScott.Carter@Oracle.COM req_p->ireq_nreq -= delta; 64512473SScott.Carter@Oracle.COM pool_p->ipool_reqno -= delta; 64612473SScott.Carter@Oracle.COM pool_p->ipool_minno -= delta; 64712473SScott.Carter@Oracle.COM } 64812473SScott.Carter@Oracle.COM req_p->ireq_navail += new_req.ireq_navail; 64912473SScott.Carter@Oracle.COM list_remove(&pool_p->ipool_req_list, req_p); 65012473SScott.Carter@Oracle.COM list_remove(&pool_p->ipool_req_list, &new_req); 65112473SScott.Carter@Oracle.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 65212473SScott.Carter@Oracle.COM 6538561SScott.Carter@Sun.COM return (DDI_SUCCESS); 6548561SScott.Carter@Sun.COM } 6558561SScott.Carter@Sun.COM 6568561SScott.Carter@Sun.COM /* 6578561SScott.Carter@Sun.COM * i_ddi_irm_remove() 6588561SScott.Carter@Sun.COM * 6598561SScott.Carter@Sun.COM * Remove a request from an interrupt pool, and balance the pool. 6608561SScott.Carter@Sun.COM */ 6618561SScott.Carter@Sun.COM int 6628561SScott.Carter@Sun.COM i_ddi_irm_remove(dev_info_t *dip) 6638561SScott.Carter@Sun.COM { 6648561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 6658561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 6668561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 6678561SScott.Carter@Sun.COM uint_t nmin; 6688561SScott.Carter@Sun.COM 6698561SScott.Carter@Sun.COM ASSERT(dip != NULL); 6708561SScott.Carter@Sun.COM 6718561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip)); 6728561SScott.Carter@Sun.COM 6738561SScott.Carter@Sun.COM /* Validate parameters */ 6748561SScott.Carter@Sun.COM if (dip == NULL) { 6758561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n")); 6768561SScott.Carter@Sun.COM return (DDI_EINVAL); 6778561SScott.Carter@Sun.COM } 6788561SScott.Carter@Sun.COM 6798561SScott.Carter@Sun.COM /* Check if the device has a request */ 6808561SScott.Carter@Sun.COM if (!(intr_p = DEVI(dip)->devi_intr_p) || 6818561SScott.Carter@Sun.COM !(req_p = intr_p->devi_irm_req_p)) { 6828561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n")); 6838561SScott.Carter@Sun.COM return (DDI_EINVAL); 6848561SScott.Carter@Sun.COM } 6858561SScott.Carter@Sun.COM 6868561SScott.Carter@Sun.COM /* Lock the pool */ 6878561SScott.Carter@Sun.COM pool_p = req_p->ireq_pool_p; 6888561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 6898561SScott.Carter@Sun.COM 6908561SScott.Carter@Sun.COM /* Remove request */ 6918561SScott.Carter@Sun.COM nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq; 6928561SScott.Carter@Sun.COM pool_p->ipool_minno -= nmin; 6938561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 6948561SScott.Carter@Sun.COM pool_p->ipool_resno -= req_p->ireq_navail; 6958561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 6968561SScott.Carter@Sun.COM 6978561SScott.Carter@Sun.COM /* Queue pool to be rebalanced */ 6988561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 6998561SScott.Carter@Sun.COM 7008561SScott.Carter@Sun.COM /* Unlock the pool */ 7018561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 7028561SScott.Carter@Sun.COM 7038561SScott.Carter@Sun.COM /* Destroy the request */ 7048561SScott.Carter@Sun.COM intr_p->devi_irm_req_p = NULL; 7058561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 7068561SScott.Carter@Sun.COM 7078561SScott.Carter@Sun.COM return (DDI_SUCCESS); 7088561SScott.Carter@Sun.COM } 7098561SScott.Carter@Sun.COM 7108561SScott.Carter@Sun.COM /* 7118561SScott.Carter@Sun.COM * i_ddi_irm_set_cb() 7128561SScott.Carter@Sun.COM * 7138561SScott.Carter@Sun.COM * Change the callback flag for a request, in response to 7148561SScott.Carter@Sun.COM * a change in its callback registration. Then rebalance 7158561SScott.Carter@Sun.COM * the interrupt pool. 7168561SScott.Carter@Sun.COM * 7178561SScott.Carter@Sun.COM * NOTE: the request is not locked because the navail value 7188561SScott.Carter@Sun.COM * is not directly affected. The balancing thread may 7198561SScott.Carter@Sun.COM * modify the navail value in the background after it 7208561SScott.Carter@Sun.COM * locks the request itself. 7218561SScott.Carter@Sun.COM */ 7228561SScott.Carter@Sun.COM void 7238561SScott.Carter@Sun.COM i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag) 7248561SScott.Carter@Sun.COM { 7258561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 7268561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 7278561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 7288561SScott.Carter@Sun.COM uint_t nreq; 7298561SScott.Carter@Sun.COM 7308561SScott.Carter@Sun.COM ASSERT(dip != NULL); 7318561SScott.Carter@Sun.COM 7328561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n", 7338561SScott.Carter@Sun.COM (void *)dip, (int)has_cb_flag)); 7348561SScott.Carter@Sun.COM 7358561SScott.Carter@Sun.COM /* Validate parameters */ 7368561SScott.Carter@Sun.COM if (dip == NULL) 7378561SScott.Carter@Sun.COM return; 7388561SScott.Carter@Sun.COM 7398561SScott.Carter@Sun.COM /* Check for association with interrupt pool */ 7408561SScott.Carter@Sun.COM if (!(intr_p = DEVI(dip)->devi_intr_p) || 7418561SScott.Carter@Sun.COM !(req_p = intr_p->devi_irm_req_p)) { 7428561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n")); 7438561SScott.Carter@Sun.COM return; 7448561SScott.Carter@Sun.COM } 7458561SScott.Carter@Sun.COM 7468561SScott.Carter@Sun.COM /* Lock the pool */ 7478561SScott.Carter@Sun.COM pool_p = req_p->ireq_pool_p; 7488561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 7498561SScott.Carter@Sun.COM 7508561SScott.Carter@Sun.COM /* 7518561SScott.Carter@Sun.COM * Update the request and the pool 7528561SScott.Carter@Sun.COM */ 7538561SScott.Carter@Sun.COM if (has_cb_flag) { 7548561SScott.Carter@Sun.COM 7558561SScott.Carter@Sun.COM /* Update pool statistics */ 7568561SScott.Carter@Sun.COM if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) 7578561SScott.Carter@Sun.COM pool_p->ipool_minno -= (req_p->ireq_nreq - 1); 7588561SScott.Carter@Sun.COM 7598561SScott.Carter@Sun.COM /* Update request */ 7608561SScott.Carter@Sun.COM req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 7618561SScott.Carter@Sun.COM 7628561SScott.Carter@Sun.COM /* Rebalance in background */ 7638561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 7648561SScott.Carter@Sun.COM 7658561SScott.Carter@Sun.COM } else { 7668561SScott.Carter@Sun.COM 7678561SScott.Carter@Sun.COM /* Determine new request size */ 7688561SScott.Carter@Sun.COM nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz); 7698561SScott.Carter@Sun.COM 770*12683SJimmy.Vetayases@oracle.com #if defined(__i386) || defined(__amd64) 771*12683SJimmy.Vetayases@oracle.com /* Use the default static limit for non-IRM drivers */ 772*12683SJimmy.Vetayases@oracle.com if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) 773*12683SJimmy.Vetayases@oracle.com nreq = MIN(nreq, ddi_msix_alloc_limit); 774*12683SJimmy.Vetayases@oracle.com #endif 775*12683SJimmy.Vetayases@oracle.com 7768561SScott.Carter@Sun.COM /* Update pool statistics */ 7778561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 7788561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 7798561SScott.Carter@Sun.COM if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) { 7808561SScott.Carter@Sun.COM pool_p->ipool_minno -= 1; 7818561SScott.Carter@Sun.COM pool_p->ipool_minno += nreq; 7828561SScott.Carter@Sun.COM } else { 7838561SScott.Carter@Sun.COM pool_p->ipool_minno -= req_p->ireq_nreq; 7848561SScott.Carter@Sun.COM pool_p->ipool_minno += nreq; 7858561SScott.Carter@Sun.COM } 7868561SScott.Carter@Sun.COM 7878561SScott.Carter@Sun.COM /* Update request size, and re-sort in pool */ 7888561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 7898561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 7908561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 7918561SScott.Carter@Sun.COM 7928561SScott.Carter@Sun.COM /* Rebalance synchronously, before losing callback */ 7938561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_TRUE); 7948561SScott.Carter@Sun.COM 7958561SScott.Carter@Sun.COM /* Remove callback flag */ 7968561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK); 7978561SScott.Carter@Sun.COM } 7988561SScott.Carter@Sun.COM 7998561SScott.Carter@Sun.COM /* Unlock the pool */ 8008561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 8018561SScott.Carter@Sun.COM } 8028561SScott.Carter@Sun.COM 8038561SScott.Carter@Sun.COM /* 80412473SScott.Carter@Oracle.COM * i_ddi_irm_supported() 80512473SScott.Carter@Oracle.COM * 80612473SScott.Carter@Oracle.COM * Query if IRM is supported by a driver using a specific interrupt type. 80712473SScott.Carter@Oracle.COM * Notice that IRM is limited to MSI-X users with registered callbacks. 80812473SScott.Carter@Oracle.COM */ 80912473SScott.Carter@Oracle.COM int 81012473SScott.Carter@Oracle.COM i_ddi_irm_supported(dev_info_t *dip, int type) 81112473SScott.Carter@Oracle.COM { 81212473SScott.Carter@Oracle.COM ddi_cb_t *cb_p = DEVI(dip)->devi_cb_p; 81312473SScott.Carter@Oracle.COM 81412473SScott.Carter@Oracle.COM return ((DDI_IRM_HAS_CB(cb_p) && (type == DDI_INTR_TYPE_MSIX)) ? 81512473SScott.Carter@Oracle.COM DDI_SUCCESS : DDI_ENOTSUP); 81612473SScott.Carter@Oracle.COM } 81712473SScott.Carter@Oracle.COM 81812473SScott.Carter@Oracle.COM /* 8198561SScott.Carter@Sun.COM * Interrupt Pool Balancing 8208561SScott.Carter@Sun.COM */ 8218561SScott.Carter@Sun.COM 8228561SScott.Carter@Sun.COM /* 8238561SScott.Carter@Sun.COM * irm_balance_thread() 8248561SScott.Carter@Sun.COM * 8258561SScott.Carter@Sun.COM * One instance of this thread operates per each defined IRM pool. 8268561SScott.Carter@Sun.COM * It does the initial activation of the pool, as well as balancing 8278561SScott.Carter@Sun.COM * any requests that were queued up before the pool was active. 8288561SScott.Carter@Sun.COM * Once active, it waits forever to service balance operations. 8298561SScott.Carter@Sun.COM */ 8308561SScott.Carter@Sun.COM static void 8318561SScott.Carter@Sun.COM irm_balance_thread(ddi_irm_pool_t *pool_p) 8328561SScott.Carter@Sun.COM { 83311066Srafael.vanoni@sun.com clock_t interval; 8348561SScott.Carter@Sun.COM 8358561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n", 8368561SScott.Carter@Sun.COM (void *)pool_p)); 8378561SScott.Carter@Sun.COM 8388561SScott.Carter@Sun.COM /* Lock the pool */ 8398561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 8408561SScott.Carter@Sun.COM 8418561SScott.Carter@Sun.COM /* Perform initial balance if required */ 8428561SScott.Carter@Sun.COM if (pool_p->ipool_reqno > pool_p->ipool_resno) 8438561SScott.Carter@Sun.COM i_ddi_irm_balance(pool_p); 8448561SScott.Carter@Sun.COM 8458561SScott.Carter@Sun.COM /* Activate the pool */ 8468561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE; 8478561SScott.Carter@Sun.COM 8488561SScott.Carter@Sun.COM /* Main loop */ 8498561SScott.Carter@Sun.COM for (;;) { 8508561SScott.Carter@Sun.COM 8518561SScott.Carter@Sun.COM /* Compute the delay interval */ 8528561SScott.Carter@Sun.COM interval = drv_usectohz(irm_balance_delay * 1000000); 8538561SScott.Carter@Sun.COM 8548561SScott.Carter@Sun.COM /* Sleep until queued */ 8558561SScott.Carter@Sun.COM cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 8568561SScott.Carter@Sun.COM 8578561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n")); 8588561SScott.Carter@Sun.COM 8598561SScott.Carter@Sun.COM /* Wait one interval, or until there are waiters */ 8608561SScott.Carter@Sun.COM if ((interval > 0) && 8618561SScott.Carter@Sun.COM !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) && 8628561SScott.Carter@Sun.COM !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) { 86311066Srafael.vanoni@sun.com (void) cv_reltimedwait(&pool_p->ipool_cv, 86411066Srafael.vanoni@sun.com &pool_p->ipool_lock, interval, TR_CLOCK_TICK); 8658561SScott.Carter@Sun.COM } 8668561SScott.Carter@Sun.COM 8678561SScott.Carter@Sun.COM /* Check if awakened to exit */ 8688561SScott.Carter@Sun.COM if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) { 8698561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 8708561SScott.Carter@Sun.COM "irm_balance_thread: exiting...\n")); 8718561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 8728561SScott.Carter@Sun.COM thread_exit(); 8738561SScott.Carter@Sun.COM } 8748561SScott.Carter@Sun.COM 8758561SScott.Carter@Sun.COM /* Balance the pool */ 8768561SScott.Carter@Sun.COM i_ddi_irm_balance(pool_p); 8778561SScott.Carter@Sun.COM 8788561SScott.Carter@Sun.COM /* Notify waiters */ 8798561SScott.Carter@Sun.COM if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) { 8808561SScott.Carter@Sun.COM cv_broadcast(&pool_p->ipool_cv); 8818561SScott.Carter@Sun.COM pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS); 8828561SScott.Carter@Sun.COM } 8838561SScott.Carter@Sun.COM 8848561SScott.Carter@Sun.COM /* Clear QUEUED condition */ 8858561SScott.Carter@Sun.COM pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED); 8868561SScott.Carter@Sun.COM } 8878561SScott.Carter@Sun.COM } 8888561SScott.Carter@Sun.COM 8898561SScott.Carter@Sun.COM /* 8908561SScott.Carter@Sun.COM * i_ddi_irm_balance() 8918561SScott.Carter@Sun.COM * 8928561SScott.Carter@Sun.COM * Balance a pool. The general algorithm is to first reset all 8938561SScott.Carter@Sun.COM * requests to their maximum size, use reduction algorithms to 8948561SScott.Carter@Sun.COM * solve any imbalance, and then notify affected drivers. 8958561SScott.Carter@Sun.COM */ 8968561SScott.Carter@Sun.COM static void 8978561SScott.Carter@Sun.COM i_ddi_irm_balance(ddi_irm_pool_t *pool_p) 8988561SScott.Carter@Sun.COM { 8998561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 9008561SScott.Carter@Sun.COM 9018561SScott.Carter@Sun.COM #ifdef DEBUG 9028561SScott.Carter@Sun.COM uint_t debug_totsz = 0; 9038561SScott.Carter@Sun.COM int debug_policy = 0; 9048561SScott.Carter@Sun.COM #endif /* DEBUG */ 9058561SScott.Carter@Sun.COM 9068561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 9078561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 9088561SScott.Carter@Sun.COM 9098561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n", 9108561SScott.Carter@Sun.COM (void *)pool_p)); 9118561SScott.Carter@Sun.COM 9128561SScott.Carter@Sun.COM #ifdef DEBUG /* Adjust size and policy settings */ 9138561SScott.Carter@Sun.COM if (irm_debug_size > pool_p->ipool_minno) { 9148561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n", 9158561SScott.Carter@Sun.COM irm_debug_size)); 9168561SScott.Carter@Sun.COM debug_totsz = pool_p->ipool_totsz; 9178561SScott.Carter@Sun.COM pool_p->ipool_totsz = irm_debug_size; 9188561SScott.Carter@Sun.COM } 9198561SScott.Carter@Sun.COM if (DDI_IRM_POLICY_VALID(irm_debug_policy)) { 9208561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 9218561SScott.Carter@Sun.COM "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy)); 9228561SScott.Carter@Sun.COM debug_policy = pool_p->ipool_policy; 9238561SScott.Carter@Sun.COM pool_p->ipool_policy = irm_debug_policy; 9248561SScott.Carter@Sun.COM } 9258561SScott.Carter@Sun.COM #endif /* DEBUG */ 9268561SScott.Carter@Sun.COM 9278561SScott.Carter@Sun.COM /* Lock the availability lock */ 9288561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_navail_lock); 9298561SScott.Carter@Sun.COM 9308561SScott.Carter@Sun.COM /* 9318561SScott.Carter@Sun.COM * Put all of the reducible requests into a scratch list. 9328561SScott.Carter@Sun.COM * Reset each one of them to their maximum availability. 9338561SScott.Carter@Sun.COM */ 9348561SScott.Carter@Sun.COM for (req_p = list_head(&pool_p->ipool_req_list); req_p; 9358561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_req_list, req_p)) { 9368561SScott.Carter@Sun.COM if (DDI_IRM_IS_REDUCIBLE(req_p)) { 9378561SScott.Carter@Sun.COM pool_p->ipool_resno -= req_p->ireq_navail; 9388561SScott.Carter@Sun.COM req_p->ireq_scratch = req_p->ireq_navail; 9398561SScott.Carter@Sun.COM req_p->ireq_navail = req_p->ireq_nreq; 9408561SScott.Carter@Sun.COM pool_p->ipool_resno += req_p->ireq_navail; 9418561SScott.Carter@Sun.COM list_insert_tail(&pool_p->ipool_scratch_list, req_p); 9428561SScott.Carter@Sun.COM } 9438561SScott.Carter@Sun.COM } 9448561SScott.Carter@Sun.COM 9458561SScott.Carter@Sun.COM /* Balance the requests */ 9468561SScott.Carter@Sun.COM i_ddi_irm_reduce(pool_p); 9478561SScott.Carter@Sun.COM 9488561SScott.Carter@Sun.COM /* Unlock the availability lock */ 9498561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_navail_lock); 9508561SScott.Carter@Sun.COM 9518561SScott.Carter@Sun.COM /* 9528561SScott.Carter@Sun.COM * Process REMOVE notifications. 9538561SScott.Carter@Sun.COM * 9548561SScott.Carter@Sun.COM * If a driver fails to release interrupts: exclude it from 9558561SScott.Carter@Sun.COM * further processing, correct the resulting imbalance, and 9568561SScott.Carter@Sun.COM * start over again at the head of the scratch list. 9578561SScott.Carter@Sun.COM */ 9588561SScott.Carter@Sun.COM req_p = list_head(&pool_p->ipool_scratch_list); 9598561SScott.Carter@Sun.COM while (req_p) { 9608561SScott.Carter@Sun.COM if ((req_p->ireq_navail < req_p->ireq_scratch) && 9618561SScott.Carter@Sun.COM (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) { 9628561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_scratch_list, req_p); 9638561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_navail_lock); 9648561SScott.Carter@Sun.COM i_ddi_irm_reduce(pool_p); 9658561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_navail_lock); 9668561SScott.Carter@Sun.COM req_p = list_head(&pool_p->ipool_scratch_list); 9678561SScott.Carter@Sun.COM } else { 9688561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p); 9698561SScott.Carter@Sun.COM } 9708561SScott.Carter@Sun.COM } 9718561SScott.Carter@Sun.COM 9728561SScott.Carter@Sun.COM /* 9738561SScott.Carter@Sun.COM * Process ADD notifications. 9748561SScott.Carter@Sun.COM * 9758561SScott.Carter@Sun.COM * This is the last use of the scratch list, so empty it. 9768561SScott.Carter@Sun.COM */ 9778561SScott.Carter@Sun.COM while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) { 9788561SScott.Carter@Sun.COM if (req_p->ireq_navail > req_p->ireq_scratch) { 9798561SScott.Carter@Sun.COM (void) i_ddi_irm_notify(pool_p, req_p); 9808561SScott.Carter@Sun.COM } 9818561SScott.Carter@Sun.COM } 9828561SScott.Carter@Sun.COM 9838561SScott.Carter@Sun.COM #ifdef DEBUG /* Restore size and policy settings */ 9848561SScott.Carter@Sun.COM if (debug_totsz != 0) 9858561SScott.Carter@Sun.COM pool_p->ipool_totsz = debug_totsz; 9868561SScott.Carter@Sun.COM if (debug_policy != 0) 9878561SScott.Carter@Sun.COM pool_p->ipool_policy = debug_policy; 9888561SScott.Carter@Sun.COM #endif /* DEBUG */ 9898561SScott.Carter@Sun.COM } 9908561SScott.Carter@Sun.COM 9918561SScott.Carter@Sun.COM /* 9928561SScott.Carter@Sun.COM * i_ddi_irm_reduce() 9938561SScott.Carter@Sun.COM * 9948561SScott.Carter@Sun.COM * Use reduction algorithms to correct an imbalance in a pool. 9958561SScott.Carter@Sun.COM */ 9968561SScott.Carter@Sun.COM static void 9978561SScott.Carter@Sun.COM i_ddi_irm_reduce(ddi_irm_pool_t *pool_p) 9988561SScott.Carter@Sun.COM { 99910173SEvan.Yan@Sun.COM int imbalance; 10008561SScott.Carter@Sun.COM 10018561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 10028561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 10038561SScott.Carter@Sun.COM ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy)); 10048561SScott.Carter@Sun.COM 10058561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n", 10068561SScott.Carter@Sun.COM (void *)pool_p)); 10078561SScott.Carter@Sun.COM 10088561SScott.Carter@Sun.COM /* Compute the imbalance. Do nothing if already balanced. */ 10098561SScott.Carter@Sun.COM if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0) 10108561SScott.Carter@Sun.COM return; 10118561SScott.Carter@Sun.COM 10128561SScott.Carter@Sun.COM /* 101310173SEvan.Yan@Sun.COM * Try policy based reduction first. If it failed, then 10148561SScott.Carter@Sun.COM * possibly reduce new requests as a last resort. 10158561SScott.Carter@Sun.COM */ 101610173SEvan.Yan@Sun.COM if (i_ddi_irm_reduce_by_policy(pool_p, imbalance, pool_p->ipool_policy) 101710173SEvan.Yan@Sun.COM != DDI_SUCCESS) { 10188561SScott.Carter@Sun.COM 10198561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 10208561SScott.Carter@Sun.COM "i_ddi_irm_reduce: policy reductions failed.\n")); 10218561SScott.Carter@Sun.COM 10228561SScott.Carter@Sun.COM /* Compute remaining imbalance */ 10238561SScott.Carter@Sun.COM imbalance = pool_p->ipool_resno - pool_p->ipool_totsz; 10248561SScott.Carter@Sun.COM 10258561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 10268561SScott.Carter@Sun.COM 10278561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(pool_p, imbalance); 10288561SScott.Carter@Sun.COM } 10298561SScott.Carter@Sun.COM } 10308561SScott.Carter@Sun.COM 10318561SScott.Carter@Sun.COM /* 10328561SScott.Carter@Sun.COM * i_ddi_irm_enqueue() 10338561SScott.Carter@Sun.COM * 10348561SScott.Carter@Sun.COM * Queue a pool to be balanced. Signals the balancing thread to wake 10358561SScott.Carter@Sun.COM * up and process the pool. If 'wait_flag' is true, then the current 10368561SScott.Carter@Sun.COM * thread becomes a waiter and blocks until the balance is completed. 10378561SScott.Carter@Sun.COM */ 10388561SScott.Carter@Sun.COM static void 10398561SScott.Carter@Sun.COM i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag) 10408561SScott.Carter@Sun.COM { 10418561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 10428561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 10438561SScott.Carter@Sun.COM 10448561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n", 10458561SScott.Carter@Sun.COM (void *)pool_p, (int)wait_flag)); 10468561SScott.Carter@Sun.COM 10478561SScott.Carter@Sun.COM /* Do nothing if pool is already balanced */ 10488561SScott.Carter@Sun.COM #ifndef DEBUG 10498561SScott.Carter@Sun.COM if ((pool_p->ipool_reqno == pool_p->ipool_resno)) { 10508561SScott.Carter@Sun.COM #else 10518561SScott.Carter@Sun.COM if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) { 10528561SScott.Carter@Sun.COM #endif /* DEBUG */ 10538561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 10548561SScott.Carter@Sun.COM "i_ddi_irm_enqueue: pool already balanced\n")); 10558561SScott.Carter@Sun.COM return; 10568561SScott.Carter@Sun.COM } 10578561SScott.Carter@Sun.COM 10588561SScott.Carter@Sun.COM /* Avoid deadlocks when IRM is not active */ 10598561SScott.Carter@Sun.COM if (!irm_active && wait_flag) { 10608561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 10618561SScott.Carter@Sun.COM "i_ddi_irm_enqueue: pool not active.\n")); 10628561SScott.Carter@Sun.COM return; 10638561SScott.Carter@Sun.COM } 10648561SScott.Carter@Sun.COM 10658561SScott.Carter@Sun.COM if (wait_flag) 10668561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS; 10678561SScott.Carter@Sun.COM 10688561SScott.Carter@Sun.COM if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) { 10698561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED; 10708561SScott.Carter@Sun.COM cv_signal(&pool_p->ipool_cv); 10718561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n")); 10728561SScott.Carter@Sun.COM } 10738561SScott.Carter@Sun.COM 10748561SScott.Carter@Sun.COM if (wait_flag) { 10758561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n")); 10768561SScott.Carter@Sun.COM cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 10778561SScott.Carter@Sun.COM } 10788561SScott.Carter@Sun.COM } 10798561SScott.Carter@Sun.COM 10808561SScott.Carter@Sun.COM /* 108110173SEvan.Yan@Sun.COM * i_ddi_irm_reduce_by_policy() 10828561SScott.Carter@Sun.COM * 108310173SEvan.Yan@Sun.COM * Reduces requests based on reduction policies. 10848561SScott.Carter@Sun.COM * 108510173SEvan.Yan@Sun.COM * For the DDI_IRM_POLICY_LARGE reduction policy, the algorithm 108610173SEvan.Yan@Sun.COM * generally reduces larger requests first, before advancing 108710173SEvan.Yan@Sun.COM * to smaller requests. 108810173SEvan.Yan@Sun.COM * For the DDI_IRM_POLICY_EVEN reduction policy, the algorithm 108910173SEvan.Yan@Sun.COM * reduces requests evenly, without giving a specific preference 109010173SEvan.Yan@Sun.COM * to smaller or larger requests. Each iteration reduces all 109110173SEvan.Yan@Sun.COM * reducible requests by the same amount until the imbalance is 109210173SEvan.Yan@Sun.COM * corrected. 109310173SEvan.Yan@Sun.COM * 109410173SEvan.Yan@Sun.COM * The scratch list is initially sorted in descending order by current 109510173SEvan.Yan@Sun.COM * navail values, which are maximized prior to reduction. This sorted 109610173SEvan.Yan@Sun.COM * order is preserved. It avoids reducing requests below the threshold 109710173SEvan.Yan@Sun.COM * of the interrupt pool's default allocation size. 10988561SScott.Carter@Sun.COM * 10998561SScott.Carter@Sun.COM * Optimizations in this algorithm include trying to reduce multiple 110010173SEvan.Yan@Sun.COM * requests together. And the algorithm attempts to reduce in larger 110110173SEvan.Yan@Sun.COM * increments when possible to minimize the total number of iterations. 11028561SScott.Carter@Sun.COM */ 11038561SScott.Carter@Sun.COM static int 110410173SEvan.Yan@Sun.COM i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *pool_p, int imbalance, int policy) 11058561SScott.Carter@Sun.COM { 11068561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 11078561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 11088561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 11098561SScott.Carter@Sun.COM 11108561SScott.Carter@Sun.COM while (imbalance > 0) { 111110173SEvan.Yan@Sun.COM list_t *slist_p = &pool_p->ipool_scratch_list; 111210173SEvan.Yan@Sun.COM ddi_irm_req_t *req_p = list_head(slist_p), *last_p; 111310173SEvan.Yan@Sun.COM uint_t nreduce = 0, nremain = 0, stop_navail; 111410173SEvan.Yan@Sun.COM uint_t pool_defsz = pool_p->ipool_defsz; 111510173SEvan.Yan@Sun.COM uint_t reduction, max_redu; 11168561SScott.Carter@Sun.COM 111710173SEvan.Yan@Sun.COM /* Fail if none are reducible */ 111810173SEvan.Yan@Sun.COM if (!req_p || req_p->ireq_navail <= pool_defsz) { 11198561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 112010173SEvan.Yan@Sun.COM "i_ddi_irm_reduce_by_policy: Failure. " 11218925SEvan.Yan@Sun.COM "All requests have downsized to low limit.\n")); 11228561SScott.Carter@Sun.COM return (DDI_FAILURE); 11238561SScott.Carter@Sun.COM } 11248561SScott.Carter@Sun.COM 11258561SScott.Carter@Sun.COM /* Count reducible requests */ 112610173SEvan.Yan@Sun.COM stop_navail = (policy == DDI_IRM_POLICY_LARGE) ? 112710173SEvan.Yan@Sun.COM req_p->ireq_navail - 1 : pool_defsz; 112810173SEvan.Yan@Sun.COM for (; req_p; req_p = list_next(slist_p, req_p)) { 112910173SEvan.Yan@Sun.COM if (req_p->ireq_navail <= stop_navail) 11308561SScott.Carter@Sun.COM break; 11318561SScott.Carter@Sun.COM nreduce++; 11328561SScott.Carter@Sun.COM } 11338561SScott.Carter@Sun.COM 113410173SEvan.Yan@Sun.COM /* Compute reduction */ 113510173SEvan.Yan@Sun.COM last_p = req_p ? list_prev(slist_p, req_p) : list_tail(slist_p); 113610173SEvan.Yan@Sun.COM if ((policy == DDI_IRM_POLICY_LARGE) && req_p && 113710173SEvan.Yan@Sun.COM req_p->ireq_navail > pool_defsz) 113810173SEvan.Yan@Sun.COM reduction = last_p->ireq_navail - req_p->ireq_navail; 113910173SEvan.Yan@Sun.COM else 114010173SEvan.Yan@Sun.COM reduction = last_p->ireq_navail - pool_defsz; 114110173SEvan.Yan@Sun.COM 114210173SEvan.Yan@Sun.COM if ((max_redu = reduction * nreduce) > imbalance) { 114310173SEvan.Yan@Sun.COM reduction = imbalance / nreduce; 114410173SEvan.Yan@Sun.COM nremain = imbalance % nreduce; 114510173SEvan.Yan@Sun.COM pool_p->ipool_resno -= imbalance; 114610173SEvan.Yan@Sun.COM imbalance = 0; 114710173SEvan.Yan@Sun.COM } else { 114810173SEvan.Yan@Sun.COM pool_p->ipool_resno -= max_redu; 114910173SEvan.Yan@Sun.COM imbalance -= max_redu; 11508561SScott.Carter@Sun.COM } 11518561SScott.Carter@Sun.COM 115210173SEvan.Yan@Sun.COM /* Reduce */ 115310173SEvan.Yan@Sun.COM for (req_p = list_head(slist_p); (reduction != 0) && nreduce--; 115410173SEvan.Yan@Sun.COM req_p = list_next(slist_p, req_p)) { 115510173SEvan.Yan@Sun.COM req_p->ireq_navail -= reduction; 11568561SScott.Carter@Sun.COM } 11578561SScott.Carter@Sun.COM 115810173SEvan.Yan@Sun.COM for (req_p = last_p; nremain--; 115910173SEvan.Yan@Sun.COM req_p = list_prev(slist_p, req_p)) { 116010173SEvan.Yan@Sun.COM req_p->ireq_navail--; 11618561SScott.Carter@Sun.COM } 11628561SScott.Carter@Sun.COM } 11638561SScott.Carter@Sun.COM 11648561SScott.Carter@Sun.COM return (DDI_SUCCESS); 11658561SScott.Carter@Sun.COM } 11668561SScott.Carter@Sun.COM 11678561SScott.Carter@Sun.COM /* 11688561SScott.Carter@Sun.COM * i_ddi_irm_reduce_new() 11698561SScott.Carter@Sun.COM * 11708925SEvan.Yan@Sun.COM * Reduces new requests. This is only used as a last resort 11718925SEvan.Yan@Sun.COM * after another reduction algorithm failed. 117210173SEvan.Yan@Sun.COM * 117310173SEvan.Yan@Sun.COM * NOTE: The pool locking in i_ddi_irm_insert() ensures 117410173SEvan.Yan@Sun.COM * there can be only one new request at a time in a pool. 11758561SScott.Carter@Sun.COM */ 11768561SScott.Carter@Sun.COM static void 11778561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance) 11788561SScott.Carter@Sun.COM { 11798561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 11808561SScott.Carter@Sun.COM 11818561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 11828561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 11838561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 11848561SScott.Carter@Sun.COM 118510173SEvan.Yan@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 118610173SEvan.Yan@Sun.COM "i_ddi_irm_reduce_new: pool_p %p imbalance %d\n", 118710173SEvan.Yan@Sun.COM (void *)pool_p, imbalance)); 118810173SEvan.Yan@Sun.COM 118910173SEvan.Yan@Sun.COM for (req_p = list_head(&pool_p->ipool_scratch_list); req_p; 119010173SEvan.Yan@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p)) { 119110173SEvan.Yan@Sun.COM if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) { 119210173SEvan.Yan@Sun.COM ASSERT(req_p->ireq_navail >= imbalance); 119310173SEvan.Yan@Sun.COM req_p->ireq_navail -= imbalance; 119410173SEvan.Yan@Sun.COM pool_p->ipool_resno -= imbalance; 119510173SEvan.Yan@Sun.COM return; 11968925SEvan.Yan@Sun.COM } 11978925SEvan.Yan@Sun.COM } 11988925SEvan.Yan@Sun.COM 119910173SEvan.Yan@Sun.COM /* should never go here */ 120010173SEvan.Yan@Sun.COM ASSERT(B_FALSE); 12018561SScott.Carter@Sun.COM } 12028561SScott.Carter@Sun.COM 12038561SScott.Carter@Sun.COM /* 12048561SScott.Carter@Sun.COM * Miscellaneous Helper Functions 12058561SScott.Carter@Sun.COM */ 12068561SScott.Carter@Sun.COM 12078561SScott.Carter@Sun.COM /* 12088561SScott.Carter@Sun.COM * i_ddi_intr_get_pool() 12098561SScott.Carter@Sun.COM * 12108561SScott.Carter@Sun.COM * Get an IRM pool that supplies interrupts of a specified type. 12118561SScott.Carter@Sun.COM * Invokes a DDI_INTROP_GETPOOL to the bus nexus driver. Fails 12128561SScott.Carter@Sun.COM * if no pool exists. 12138561SScott.Carter@Sun.COM */ 12148561SScott.Carter@Sun.COM ddi_irm_pool_t * 12158561SScott.Carter@Sun.COM i_ddi_intr_get_pool(dev_info_t *dip, int type) 12168561SScott.Carter@Sun.COM { 12178561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 12188561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 12198561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 12208561SScott.Carter@Sun.COM ddi_intr_handle_impl_t hdl; 12218561SScott.Carter@Sun.COM 12228561SScott.Carter@Sun.COM ASSERT(dip != NULL); 12238561SScott.Carter@Sun.COM ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 12248561SScott.Carter@Sun.COM 12258561SScott.Carter@Sun.COM if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 12268561SScott.Carter@Sun.COM ((req_p = intr_p->devi_irm_req_p) != NULL) && 12278561SScott.Carter@Sun.COM ((pool_p = req_p->ireq_pool_p) != NULL) && 12288561SScott.Carter@Sun.COM (pool_p->ipool_types & type)) { 12298561SScott.Carter@Sun.COM return (pool_p); 12308561SScott.Carter@Sun.COM } 12318561SScott.Carter@Sun.COM 12328561SScott.Carter@Sun.COM bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 12338561SScott.Carter@Sun.COM hdl.ih_dip = dip; 12348561SScott.Carter@Sun.COM hdl.ih_type = type; 12358561SScott.Carter@Sun.COM 12368561SScott.Carter@Sun.COM if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL, 12378561SScott.Carter@Sun.COM &hdl, (void *)&pool_p) == DDI_SUCCESS) 12388561SScott.Carter@Sun.COM return (pool_p); 12398561SScott.Carter@Sun.COM 12408561SScott.Carter@Sun.COM return (NULL); 12418561SScott.Carter@Sun.COM } 12428561SScott.Carter@Sun.COM 12438561SScott.Carter@Sun.COM /* 12448561SScott.Carter@Sun.COM * i_ddi_irm_insertion_sort() 12458561SScott.Carter@Sun.COM * 12468561SScott.Carter@Sun.COM * Use the insertion sort method to insert a request into a list. 12478561SScott.Carter@Sun.COM * The list is sorted in descending order by request size. 12488561SScott.Carter@Sun.COM */ 12498561SScott.Carter@Sun.COM static void 12508561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p) 12518561SScott.Carter@Sun.COM { 12528561SScott.Carter@Sun.COM ddi_irm_req_t *next_p; 12538561SScott.Carter@Sun.COM 12548561SScott.Carter@Sun.COM next_p = list_head(req_list); 12558561SScott.Carter@Sun.COM 12568561SScott.Carter@Sun.COM while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq)) 12578561SScott.Carter@Sun.COM next_p = list_next(req_list, next_p); 12588561SScott.Carter@Sun.COM 12598561SScott.Carter@Sun.COM list_insert_before(req_list, next_p, req_p); 12608561SScott.Carter@Sun.COM } 12618561SScott.Carter@Sun.COM 12628561SScott.Carter@Sun.COM /* 12638561SScott.Carter@Sun.COM * i_ddi_irm_notify() 12648561SScott.Carter@Sun.COM * 12658561SScott.Carter@Sun.COM * Notify a driver of changes to its interrupt request using the 12668561SScott.Carter@Sun.COM * generic callback mechanism. Checks for errors in processing. 12678561SScott.Carter@Sun.COM */ 12688561SScott.Carter@Sun.COM static int 12698561SScott.Carter@Sun.COM i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p) 12708561SScott.Carter@Sun.COM { 12718561SScott.Carter@Sun.COM ddi_cb_action_t action; 12728561SScott.Carter@Sun.COM ddi_cb_t *cb_p; 12738561SScott.Carter@Sun.COM uint_t nintrs; 12748561SScott.Carter@Sun.COM int ret, count; 12758561SScott.Carter@Sun.COM 12768561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n", 12778561SScott.Carter@Sun.COM (void *)pool_p, (void *)req_p)); 12788561SScott.Carter@Sun.COM 12798561SScott.Carter@Sun.COM /* Do not notify new or unchanged requests */ 12808561SScott.Carter@Sun.COM if ((req_p->ireq_navail == req_p->ireq_scratch) || 12818561SScott.Carter@Sun.COM (req_p->ireq_flags & DDI_IRM_FLAG_NEW)) 12828561SScott.Carter@Sun.COM return (DDI_SUCCESS); 12838561SScott.Carter@Sun.COM 12848561SScott.Carter@Sun.COM /* Determine action and count */ 12858561SScott.Carter@Sun.COM if (req_p->ireq_navail > req_p->ireq_scratch) { 12868561SScott.Carter@Sun.COM action = DDI_CB_INTR_ADD; 12878561SScott.Carter@Sun.COM count = req_p->ireq_navail - req_p->ireq_scratch; 12888561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n", 12898561SScott.Carter@Sun.COM count)); 12908561SScott.Carter@Sun.COM } else { 12918561SScott.Carter@Sun.COM action = DDI_CB_INTR_REMOVE; 12928561SScott.Carter@Sun.COM count = req_p->ireq_scratch - req_p->ireq_navail; 12938561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n", 12948561SScott.Carter@Sun.COM count)); 12958561SScott.Carter@Sun.COM } 12968561SScott.Carter@Sun.COM 12978561SScott.Carter@Sun.COM /* Lookup driver callback */ 12988561SScott.Carter@Sun.COM if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) { 12998561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n")); 13008561SScott.Carter@Sun.COM return (DDI_FAILURE); 13018561SScott.Carter@Sun.COM } 13028561SScott.Carter@Sun.COM 13038561SScott.Carter@Sun.COM /* Do callback */ 13048561SScott.Carter@Sun.COM ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count, 13058561SScott.Carter@Sun.COM cb_p->cb_arg1, cb_p->cb_arg2); 13068561SScott.Carter@Sun.COM 13078561SScott.Carter@Sun.COM /* Log callback errors */ 13088561SScott.Carter@Sun.COM if (ret != DDI_SUCCESS) { 13098561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n", 13108561SScott.Carter@Sun.COM ddi_driver_name(req_p->ireq_dip), 13118561SScott.Carter@Sun.COM ddi_get_instance(req_p->ireq_dip), (int)action, ret); 13128561SScott.Carter@Sun.COM } 13138561SScott.Carter@Sun.COM 13148561SScott.Carter@Sun.COM /* Check if the driver exceeds its availability */ 13158561SScott.Carter@Sun.COM nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip); 13168561SScott.Carter@Sun.COM if (nintrs > req_p->ireq_navail) { 13178561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: failed to release interrupts " 13188561SScott.Carter@Sun.COM "(nintrs=%d, navail=%d).\n", 13198561SScott.Carter@Sun.COM ddi_driver_name(req_p->ireq_dip), 13208561SScott.Carter@Sun.COM ddi_get_instance(req_p->ireq_dip), nintrs, 13218561SScott.Carter@Sun.COM req_p->ireq_navail); 13228561SScott.Carter@Sun.COM pool_p->ipool_resno += (nintrs - req_p->ireq_navail); 13238561SScott.Carter@Sun.COM req_p->ireq_navail = nintrs; 13248561SScott.Carter@Sun.COM return (DDI_FAILURE); 13258561SScott.Carter@Sun.COM } 13268561SScott.Carter@Sun.COM 13278561SScott.Carter@Sun.COM /* Update request */ 13288561SScott.Carter@Sun.COM req_p->ireq_scratch = req_p->ireq_navail; 13298561SScott.Carter@Sun.COM 13308561SScott.Carter@Sun.COM return (DDI_SUCCESS); 13318561SScott.Carter@Sun.COM } 13328561SScott.Carter@Sun.COM 13338561SScott.Carter@Sun.COM /* 13348561SScott.Carter@Sun.COM * i_ddi_irm_debug_balance() 13358561SScott.Carter@Sun.COM * 13368561SScott.Carter@Sun.COM * A debug/test only routine to force the immediate, 13378561SScott.Carter@Sun.COM * synchronous rebalancing of an interrupt pool. 13388561SScott.Carter@Sun.COM */ 13398561SScott.Carter@Sun.COM #ifdef DEBUG 13408561SScott.Carter@Sun.COM void 13418561SScott.Carter@Sun.COM i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag) 13428561SScott.Carter@Sun.COM { 13438561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 13448561SScott.Carter@Sun.COM int type; 13458561SScott.Carter@Sun.COM 13468561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n", 13478561SScott.Carter@Sun.COM (void *)dip, (int)wait_flag)); 13488561SScott.Carter@Sun.COM 13498561SScott.Carter@Sun.COM if (((type = i_ddi_intr_get_current_type(dip)) != 0) && 13508561SScott.Carter@Sun.COM ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) { 13518561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 13528561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, wait_flag); 13538561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 13548561SScott.Carter@Sun.COM } 13558561SScott.Carter@Sun.COM } 13568561SScott.Carter@Sun.COM #endif 1357