18561SScott.Carter@Sun.COM /* 28561SScott.Carter@Sun.COM * CDDL HEADER START 38561SScott.Carter@Sun.COM * 48561SScott.Carter@Sun.COM * The contents of this file are subject to the terms of the 58561SScott.Carter@Sun.COM * Common Development and Distribution License (the "License"). 68561SScott.Carter@Sun.COM * You may not use this file except in compliance with the License. 78561SScott.Carter@Sun.COM * 88561SScott.Carter@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98561SScott.Carter@Sun.COM * or http://www.opensolaris.org/os/licensing. 108561SScott.Carter@Sun.COM * See the License for the specific language governing permissions 118561SScott.Carter@Sun.COM * and limitations under the License. 128561SScott.Carter@Sun.COM * 138561SScott.Carter@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148561SScott.Carter@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158561SScott.Carter@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168561SScott.Carter@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178561SScott.Carter@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188561SScott.Carter@Sun.COM * 198561SScott.Carter@Sun.COM * CDDL HEADER END 208561SScott.Carter@Sun.COM */ 218561SScott.Carter@Sun.COM /* 228561SScott.Carter@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238561SScott.Carter@Sun.COM * Use is subject to license terms. 248561SScott.Carter@Sun.COM */ 258561SScott.Carter@Sun.COM 268561SScott.Carter@Sun.COM #include <sys/note.h> 278561SScott.Carter@Sun.COM #include <sys/sysmacros.h> 288561SScott.Carter@Sun.COM #include <sys/types.h> 298561SScott.Carter@Sun.COM #include <sys/param.h> 308561SScott.Carter@Sun.COM #include <sys/systm.h> 318561SScott.Carter@Sun.COM #include <sys/kmem.h> 328561SScott.Carter@Sun.COM #include <sys/cmn_err.h> 338561SScott.Carter@Sun.COM #include <sys/debug.h> 348561SScott.Carter@Sun.COM #include <sys/ddi.h> 358561SScott.Carter@Sun.COM #include <sys/sunndi.h> 368561SScott.Carter@Sun.COM #include <sys/ndi_impldefs.h> /* include prototypes */ 378561SScott.Carter@Sun.COM 388561SScott.Carter@Sun.COM /* 398561SScott.Carter@Sun.COM * Interrupt Resource Management (IRM). 408561SScott.Carter@Sun.COM */ 418561SScott.Carter@Sun.COM 428561SScott.Carter@Sun.COM #define DDI_IRM_BALANCE_DELAY (60) /* In seconds */ 438561SScott.Carter@Sun.COM 448561SScott.Carter@Sun.COM #define DDI_IRM_HAS_CB(c) ((c) && (c->cb_flags & DDI_CB_FLAG_INTR)) 458561SScott.Carter@Sun.COM 468561SScott.Carter@Sun.COM #define DDI_IRM_IS_REDUCIBLE(r) (((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \ 478561SScott.Carter@Sun.COM (r->ireq_type == DDI_INTR_TYPE_MSIX)) || \ 488561SScott.Carter@Sun.COM (r->ireq_flags & DDI_IRM_FLAG_NEW)) 498561SScott.Carter@Sun.COM 508561SScott.Carter@Sun.COM extern pri_t minclsyspri; 518561SScott.Carter@Sun.COM 528561SScott.Carter@Sun.COM /* Global policies */ 538561SScott.Carter@Sun.COM int irm_enable = 1; 548561SScott.Carter@Sun.COM boolean_t irm_active = B_FALSE; 558561SScott.Carter@Sun.COM int irm_default_policy = DDI_IRM_POLICY_LARGE; 568561SScott.Carter@Sun.COM uint_t irm_balance_delay = DDI_IRM_BALANCE_DELAY; 578561SScott.Carter@Sun.COM 588561SScott.Carter@Sun.COM /* Global list of interrupt pools */ 598561SScott.Carter@Sun.COM kmutex_t irm_pools_lock; 608561SScott.Carter@Sun.COM list_t irm_pools_list; 618561SScott.Carter@Sun.COM 628561SScott.Carter@Sun.COM /* Global debug tunables */ 638561SScott.Carter@Sun.COM #ifdef DEBUG 648561SScott.Carter@Sun.COM int irm_debug_policy = 0; 658561SScott.Carter@Sun.COM uint_t irm_debug_size = 0; 668561SScott.Carter@Sun.COM #endif /* DEBUG */ 678561SScott.Carter@Sun.COM 688561SScott.Carter@Sun.COM static void irm_balance_thread(ddi_irm_pool_t *); 698561SScott.Carter@Sun.COM static void i_ddi_irm_balance(ddi_irm_pool_t *); 708561SScott.Carter@Sun.COM static void i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t); 718561SScott.Carter@Sun.COM static void i_ddi_irm_reduce(ddi_irm_pool_t *pool); 728561SScott.Carter@Sun.COM static int i_ddi_irm_reduce_large(ddi_irm_pool_t *, int); 738561SScott.Carter@Sun.COM static void i_ddi_irm_reduce_large_resort(ddi_irm_pool_t *); 748561SScott.Carter@Sun.COM static int i_ddi_irm_reduce_even(ddi_irm_pool_t *, int); 758561SScott.Carter@Sun.COM static void i_ddi_irm_reduce_new(ddi_irm_pool_t *, int); 768561SScott.Carter@Sun.COM static void i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *); 778561SScott.Carter@Sun.COM static int i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *); 788561SScott.Carter@Sun.COM 798561SScott.Carter@Sun.COM /* 808561SScott.Carter@Sun.COM * OS Initialization Routines 818561SScott.Carter@Sun.COM */ 828561SScott.Carter@Sun.COM 838561SScott.Carter@Sun.COM /* 848561SScott.Carter@Sun.COM * irm_init() 858561SScott.Carter@Sun.COM * 868561SScott.Carter@Sun.COM * Initialize IRM subsystem before any drivers are attached. 878561SScott.Carter@Sun.COM */ 888561SScott.Carter@Sun.COM void 898561SScott.Carter@Sun.COM irm_init(void) 908561SScott.Carter@Sun.COM { 918561SScott.Carter@Sun.COM /* Do nothing if IRM is disabled */ 928561SScott.Carter@Sun.COM if (!irm_enable) 938561SScott.Carter@Sun.COM return; 948561SScott.Carter@Sun.COM 958561SScott.Carter@Sun.COM /* Verify that the default balancing policy is valid */ 968561SScott.Carter@Sun.COM if (!DDI_IRM_POLICY_VALID(irm_default_policy)) 978561SScott.Carter@Sun.COM irm_default_policy = DDI_IRM_POLICY_LARGE; 988561SScott.Carter@Sun.COM 998561SScott.Carter@Sun.COM /* Initialize the global list of interrupt pools */ 1008561SScott.Carter@Sun.COM mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL); 1018561SScott.Carter@Sun.COM list_create(&irm_pools_list, sizeof (ddi_irm_pool_t), 1028561SScott.Carter@Sun.COM offsetof(ddi_irm_pool_t, ipool_link)); 1038561SScott.Carter@Sun.COM } 1048561SScott.Carter@Sun.COM 1058561SScott.Carter@Sun.COM /* 1068561SScott.Carter@Sun.COM * i_ddi_irm_poststartup() 1078561SScott.Carter@Sun.COM * 1088561SScott.Carter@Sun.COM * IRM is not activated until after the IO subsystem is initialized. 1098561SScott.Carter@Sun.COM * When activated, per-pool balancing threads are spawned and a flag 1108561SScott.Carter@Sun.COM * is set so that all future pools will be activated when created. 1118561SScott.Carter@Sun.COM * 1128561SScott.Carter@Sun.COM * NOTE: the global variable 'irm_enable' disables IRM if zero. 1138561SScott.Carter@Sun.COM */ 1148561SScott.Carter@Sun.COM void 1158561SScott.Carter@Sun.COM i_ddi_irm_poststartup(void) 1168561SScott.Carter@Sun.COM { 1178561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 1188561SScott.Carter@Sun.COM 1198561SScott.Carter@Sun.COM /* Do nothing if IRM is disabled */ 1208561SScott.Carter@Sun.COM if (!irm_enable) 1218561SScott.Carter@Sun.COM return; 1228561SScott.Carter@Sun.COM 1238561SScott.Carter@Sun.COM /* Lock the global list */ 1248561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 1258561SScott.Carter@Sun.COM 1268561SScott.Carter@Sun.COM /* Activate all defined pools */ 1278561SScott.Carter@Sun.COM for (pool_p = list_head(&irm_pools_list); pool_p; 1288561SScott.Carter@Sun.COM pool_p = list_next(&irm_pools_list, pool_p)) 1298561SScott.Carter@Sun.COM pool_p->ipool_thread = thread_create(NULL, 0, 1308561SScott.Carter@Sun.COM irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 1318561SScott.Carter@Sun.COM 1328561SScott.Carter@Sun.COM /* Set future pools to be active */ 1338561SScott.Carter@Sun.COM irm_active = B_TRUE; 1348561SScott.Carter@Sun.COM 1358561SScott.Carter@Sun.COM /* Unlock the global list */ 1368561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 1378561SScott.Carter@Sun.COM } 1388561SScott.Carter@Sun.COM 1398561SScott.Carter@Sun.COM /* 1408561SScott.Carter@Sun.COM * NDI interfaces for creating/destroying IRM pools. 1418561SScott.Carter@Sun.COM */ 1428561SScott.Carter@Sun.COM 1438561SScott.Carter@Sun.COM /* 1448561SScott.Carter@Sun.COM * ndi_irm_create() 1458561SScott.Carter@Sun.COM * 1468561SScott.Carter@Sun.COM * Nexus interface to create an IRM pool. Create the new 1478561SScott.Carter@Sun.COM * pool and add it to the global list of interrupt pools. 1488561SScott.Carter@Sun.COM */ 1498561SScott.Carter@Sun.COM int 1508561SScott.Carter@Sun.COM ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp, 1518561SScott.Carter@Sun.COM ddi_irm_pool_t **pool_retp) 1528561SScott.Carter@Sun.COM { 1538561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 1548561SScott.Carter@Sun.COM 1558561SScott.Carter@Sun.COM ASSERT(dip != NULL); 1568561SScott.Carter@Sun.COM ASSERT(paramsp != NULL); 1578561SScott.Carter@Sun.COM ASSERT(pool_retp != NULL); 1588561SScott.Carter@Sun.COM ASSERT(paramsp->iparams_total >= 1); 1598561SScott.Carter@Sun.COM ASSERT(paramsp->iparams_types != 0); 1608561SScott.Carter@Sun.COM 1618561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip)); 1628561SScott.Carter@Sun.COM 1638561SScott.Carter@Sun.COM /* Check if IRM is enabled */ 1648561SScott.Carter@Sun.COM if (!irm_enable) 1658561SScott.Carter@Sun.COM return (NDI_FAILURE); 1668561SScott.Carter@Sun.COM 1678561SScott.Carter@Sun.COM /* Validate parameters */ 1688561SScott.Carter@Sun.COM if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) || 1698925SEvan.Yan@Sun.COM (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0)) 1708561SScott.Carter@Sun.COM return (NDI_FAILURE); 1718561SScott.Carter@Sun.COM 1728561SScott.Carter@Sun.COM /* Allocate and initialize the pool */ 1738561SScott.Carter@Sun.COM pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP); 1748561SScott.Carter@Sun.COM pool_p->ipool_owner = dip; 1758561SScott.Carter@Sun.COM pool_p->ipool_policy = irm_default_policy; 1768561SScott.Carter@Sun.COM pool_p->ipool_types = paramsp->iparams_types; 1778561SScott.Carter@Sun.COM pool_p->ipool_totsz = paramsp->iparams_total; 1788925SEvan.Yan@Sun.COM pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC, 1798925SEvan.Yan@Sun.COM paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER)); 1808561SScott.Carter@Sun.COM list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t), 1818561SScott.Carter@Sun.COM offsetof(ddi_irm_req_t, ireq_link)); 1828561SScott.Carter@Sun.COM list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t), 1838561SScott.Carter@Sun.COM offsetof(ddi_irm_req_t, ireq_scratch_link)); 1848561SScott.Carter@Sun.COM cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL); 1858561SScott.Carter@Sun.COM mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL); 1868561SScott.Carter@Sun.COM mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL); 1878561SScott.Carter@Sun.COM 1888561SScott.Carter@Sun.COM /* Add to global list of pools */ 1898561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 1908561SScott.Carter@Sun.COM list_insert_tail(&irm_pools_list, pool_p); 1918561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 1928561SScott.Carter@Sun.COM 1938561SScott.Carter@Sun.COM /* If IRM is active, then activate the pool */ 1948561SScott.Carter@Sun.COM if (irm_active) 1958561SScott.Carter@Sun.COM pool_p->ipool_thread = thread_create(NULL, 0, 1968561SScott.Carter@Sun.COM irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 1978561SScott.Carter@Sun.COM 1988561SScott.Carter@Sun.COM *pool_retp = pool_p; 1998561SScott.Carter@Sun.COM return (NDI_SUCCESS); 2008561SScott.Carter@Sun.COM } 2018561SScott.Carter@Sun.COM 2028561SScott.Carter@Sun.COM /* 2038561SScott.Carter@Sun.COM * ndi_irm_destroy() 2048561SScott.Carter@Sun.COM * 2058561SScott.Carter@Sun.COM * Nexus interface to destroy an IRM pool. Destroy the pool 2068561SScott.Carter@Sun.COM * and remove it from the global list of interrupt pools. 2078561SScott.Carter@Sun.COM */ 2088561SScott.Carter@Sun.COM int 2098561SScott.Carter@Sun.COM ndi_irm_destroy(ddi_irm_pool_t *pool_p) 2108561SScott.Carter@Sun.COM { 2118561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 2128561SScott.Carter@Sun.COM ASSERT(pool_p->ipool_resno == 0); 2138561SScott.Carter@Sun.COM 2148561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n", 2158561SScott.Carter@Sun.COM (void *)pool_p)); 2168561SScott.Carter@Sun.COM 2178561SScott.Carter@Sun.COM /* Validate parameters */ 2188561SScott.Carter@Sun.COM if (pool_p == NULL) 2198561SScott.Carter@Sun.COM return (NDI_FAILURE); 2208561SScott.Carter@Sun.COM 2218561SScott.Carter@Sun.COM /* Validate that pool is empty */ 2228561SScott.Carter@Sun.COM if (pool_p->ipool_resno != 0) 2238561SScott.Carter@Sun.COM return (NDI_BUSY); 2248561SScott.Carter@Sun.COM 2258561SScott.Carter@Sun.COM /* Remove the pool from the global list */ 2268561SScott.Carter@Sun.COM mutex_enter(&irm_pools_lock); 2278561SScott.Carter@Sun.COM list_remove(&irm_pools_list, pool_p); 2288561SScott.Carter@Sun.COM mutex_exit(&irm_pools_lock); 2298561SScott.Carter@Sun.COM 2308561SScott.Carter@Sun.COM /* Terminate the balancing thread */ 2318561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 2328561SScott.Carter@Sun.COM if (pool_p->ipool_thread && 2338561SScott.Carter@Sun.COM (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) { 2348561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT; 2358561SScott.Carter@Sun.COM cv_signal(&pool_p->ipool_cv); 2368885SJustin.Frank@Sun.COM mutex_exit(&pool_p->ipool_lock); 2378561SScott.Carter@Sun.COM thread_join(pool_p->ipool_thread->t_did); 2388885SJustin.Frank@Sun.COM } else 2398885SJustin.Frank@Sun.COM mutex_exit(&pool_p->ipool_lock); 2408561SScott.Carter@Sun.COM 2418561SScott.Carter@Sun.COM /* Destroy the pool */ 2428561SScott.Carter@Sun.COM cv_destroy(&pool_p->ipool_cv); 2438561SScott.Carter@Sun.COM mutex_destroy(&pool_p->ipool_lock); 2448561SScott.Carter@Sun.COM mutex_destroy(&pool_p->ipool_navail_lock); 2458561SScott.Carter@Sun.COM list_destroy(&pool_p->ipool_req_list); 2468561SScott.Carter@Sun.COM list_destroy(&pool_p->ipool_scratch_list); 2478561SScott.Carter@Sun.COM kmem_free(pool_p, sizeof (ddi_irm_pool_t)); 2488561SScott.Carter@Sun.COM 2498561SScott.Carter@Sun.COM return (NDI_SUCCESS); 2508561SScott.Carter@Sun.COM } 2518561SScott.Carter@Sun.COM 2528561SScott.Carter@Sun.COM /* 2538561SScott.Carter@Sun.COM * Insert/Modify/Remove Interrupt Requests 2548561SScott.Carter@Sun.COM */ 2558561SScott.Carter@Sun.COM 2568561SScott.Carter@Sun.COM /* 2578561SScott.Carter@Sun.COM * i_ddi_irm_insert() 2588561SScott.Carter@Sun.COM * 2598561SScott.Carter@Sun.COM * Insert a new request into an interrupt pool, and balance the pool. 2608561SScott.Carter@Sun.COM */ 2618561SScott.Carter@Sun.COM int 2628561SScott.Carter@Sun.COM i_ddi_irm_insert(dev_info_t *dip, int type, int count) 2638561SScott.Carter@Sun.COM { 2648561SScott.Carter@Sun.COM ddi_cb_t *cb_p; 2658561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 2668561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 2678561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 2688561SScott.Carter@Sun.COM uint_t nreq, nmin, npartial; 2698561SScott.Carter@Sun.COM boolean_t irm_flag = B_FALSE; 2708561SScott.Carter@Sun.COM 2718561SScott.Carter@Sun.COM ASSERT(dip != NULL); 2728561SScott.Carter@Sun.COM ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 2738561SScott.Carter@Sun.COM ASSERT(count > 0); 2748561SScott.Carter@Sun.COM 2758561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n", 2768561SScott.Carter@Sun.COM (void *)dip, type, count)); 2778561SScott.Carter@Sun.COM 2788561SScott.Carter@Sun.COM /* Validate parameters */ 2798561SScott.Carter@Sun.COM if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) { 2808561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n")); 2818561SScott.Carter@Sun.COM return (DDI_EINVAL); 2828561SScott.Carter@Sun.COM } 2838561SScott.Carter@Sun.COM 2848561SScott.Carter@Sun.COM /* Check for an existing request */ 2858561SScott.Carter@Sun.COM if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 2868561SScott.Carter@Sun.COM (intr_p->devi_irm_req_p != NULL)) 2878561SScott.Carter@Sun.COM return (DDI_SUCCESS); 2888561SScott.Carter@Sun.COM 2898561SScott.Carter@Sun.COM /* Check for IRM support from the system */ 2908561SScott.Carter@Sun.COM if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) { 2918561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n")); 2928561SScott.Carter@Sun.COM return (DDI_ENOTSUP); 2938561SScott.Carter@Sun.COM } 2948561SScott.Carter@Sun.COM 2958561SScott.Carter@Sun.COM /* Check for IRM support from the driver */ 2968561SScott.Carter@Sun.COM if (((cb_p = DEVI(dip)->devi_cb_p) != NULL) && DDI_IRM_HAS_CB(cb_p) && 2978561SScott.Carter@Sun.COM (type == DDI_INTR_TYPE_MSIX)) 2988561SScott.Carter@Sun.COM irm_flag = B_TRUE; 2998561SScott.Carter@Sun.COM 3008561SScott.Carter@Sun.COM /* Determine request size */ 301*9205SEvan.Yan@Sun.COM nreq = (irm_flag) ? count : 302*9205SEvan.Yan@Sun.COM MIN(count, i_ddi_intr_get_current_navail(dip, type)); 3038561SScott.Carter@Sun.COM nmin = (irm_flag) ? 1 : nreq; 3048561SScott.Carter@Sun.COM npartial = MIN(nreq, pool_p->ipool_defsz); 3058561SScott.Carter@Sun.COM 3068561SScott.Carter@Sun.COM /* Allocate and initialize the request */ 3078561SScott.Carter@Sun.COM req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP); 3088561SScott.Carter@Sun.COM req_p->ireq_type = type; 3098561SScott.Carter@Sun.COM req_p->ireq_dip = dip; 3108561SScott.Carter@Sun.COM req_p->ireq_pool_p = pool_p; 3118561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 3128561SScott.Carter@Sun.COM req_p->ireq_flags = DDI_IRM_FLAG_NEW; 3138561SScott.Carter@Sun.COM if (DDI_IRM_HAS_CB(cb_p)) 3148561SScott.Carter@Sun.COM req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 3158561SScott.Carter@Sun.COM 3168561SScott.Carter@Sun.COM /* Lock the pool */ 3178561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 3188561SScott.Carter@Sun.COM 3198561SScott.Carter@Sun.COM /* Check for minimal fit before inserting */ 3208561SScott.Carter@Sun.COM if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) { 3218561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 3228561SScott.Carter@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip)); 3238561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 3248561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 3258561SScott.Carter@Sun.COM return (DDI_EAGAIN); 3268561SScott.Carter@Sun.COM } 3278561SScott.Carter@Sun.COM 3288561SScott.Carter@Sun.COM /* Insert the request into the pool */ 3298561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 3308561SScott.Carter@Sun.COM pool_p->ipool_minno += nmin; 3318561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 3328561SScott.Carter@Sun.COM 3338561SScott.Carter@Sun.COM /* 3348561SScott.Carter@Sun.COM * Try to fulfill the request. 3358561SScott.Carter@Sun.COM * 3368561SScott.Carter@Sun.COM * If all the interrupts are available, and either the request 3378561SScott.Carter@Sun.COM * is static or the pool is active, then just take them directly. 3388561SScott.Carter@Sun.COM * 3398561SScott.Carter@Sun.COM * If only some of the interrupts are available, and the request 3408561SScott.Carter@Sun.COM * can receive future callbacks, then take some now but queue the 3418561SScott.Carter@Sun.COM * pool to be rebalanced later. 3428561SScott.Carter@Sun.COM * 3438561SScott.Carter@Sun.COM * Otherwise, immediately rebalance the pool and wait. 3448561SScott.Carter@Sun.COM */ 3458561SScott.Carter@Sun.COM if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) && 3468561SScott.Carter@Sun.COM ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) { 3478561SScott.Carter@Sun.COM 3488561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 3498561SScott.Carter@Sun.COM "request completely fulfilled.\n")); 3508561SScott.Carter@Sun.COM pool_p->ipool_resno += nreq; 3518561SScott.Carter@Sun.COM req_p->ireq_navail = nreq; 3528561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 3538561SScott.Carter@Sun.COM 3548561SScott.Carter@Sun.COM } else if (irm_flag && 3558561SScott.Carter@Sun.COM ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) { 3568561SScott.Carter@Sun.COM 3578561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 3588561SScott.Carter@Sun.COM "request partially fulfilled.\n")); 3598561SScott.Carter@Sun.COM pool_p->ipool_resno += npartial; 3608561SScott.Carter@Sun.COM req_p->ireq_navail = npartial; 3618561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 3628561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 3638561SScott.Carter@Sun.COM 3648561SScott.Carter@Sun.COM } else { 3658561SScott.Carter@Sun.COM 3668561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 3678561SScott.Carter@Sun.COM "request needs immediate rebalance.\n")); 3688561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_TRUE); 3698561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 3708561SScott.Carter@Sun.COM } 3718561SScott.Carter@Sun.COM 3728561SScott.Carter@Sun.COM /* Fail if the request cannot be fulfilled at all */ 3738561SScott.Carter@Sun.COM if (req_p->ireq_navail == 0) { 3748561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 3758561SScott.Carter@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip)); 3768561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 3778561SScott.Carter@Sun.COM pool_p->ipool_reqno -= nreq; 3788561SScott.Carter@Sun.COM pool_p->ipool_minno -= nmin; 3798561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 3808561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 3818561SScott.Carter@Sun.COM return (DDI_EAGAIN); 3828561SScott.Carter@Sun.COM } 3838561SScott.Carter@Sun.COM 3848561SScott.Carter@Sun.COM /* Unlock the pool */ 3858561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 3868561SScott.Carter@Sun.COM 3878561SScott.Carter@Sun.COM intr_p->devi_irm_req_p = req_p; 3888561SScott.Carter@Sun.COM return (DDI_SUCCESS); 3898561SScott.Carter@Sun.COM } 3908561SScott.Carter@Sun.COM 3918561SScott.Carter@Sun.COM /* 3928561SScott.Carter@Sun.COM * i_ddi_irm_modify() 3938561SScott.Carter@Sun.COM * 3948561SScott.Carter@Sun.COM * Modify an existing request in an interrupt pool, and balance the pool. 3958561SScott.Carter@Sun.COM */ 3968561SScott.Carter@Sun.COM int 3978561SScott.Carter@Sun.COM i_ddi_irm_modify(dev_info_t *dip, int nreq) 3988561SScott.Carter@Sun.COM { 3998561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 4008561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 4018561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 4028561SScott.Carter@Sun.COM 4038561SScott.Carter@Sun.COM ASSERT(dip != NULL); 4048561SScott.Carter@Sun.COM 4058561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n", 4068561SScott.Carter@Sun.COM (void *)dip, nreq)); 4078561SScott.Carter@Sun.COM 4088561SScott.Carter@Sun.COM /* Validate parameters */ 4098561SScott.Carter@Sun.COM if ((dip == NULL) || (nreq < 1)) { 4108561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 4118561SScott.Carter@Sun.COM return (DDI_EINVAL); 4128561SScott.Carter@Sun.COM } 4138561SScott.Carter@Sun.COM 4148561SScott.Carter@Sun.COM /* Check that the operation is supported */ 4158561SScott.Carter@Sun.COM if (!(intr_p = DEVI(dip)->devi_intr_p) || 4168561SScott.Carter@Sun.COM !(req_p = intr_p->devi_irm_req_p) || 4178561SScott.Carter@Sun.COM !DDI_IRM_IS_REDUCIBLE(req_p)) { 4188561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not supported\n")); 4198561SScott.Carter@Sun.COM return (DDI_ENOTSUP); 4208561SScott.Carter@Sun.COM } 4218561SScott.Carter@Sun.COM 4228561SScott.Carter@Sun.COM /* Validate request size is not too large */ 4238561SScott.Carter@Sun.COM if (nreq > intr_p->devi_intr_sup_nintrs) { 4248561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 4258561SScott.Carter@Sun.COM return (DDI_EINVAL); 4268561SScott.Carter@Sun.COM } 4278561SScott.Carter@Sun.COM 4288561SScott.Carter@Sun.COM /* 4298561SScott.Carter@Sun.COM * Modify request, but only if new size is different. 4308561SScott.Carter@Sun.COM */ 4318561SScott.Carter@Sun.COM if (nreq != req_p->ireq_nreq) { 4328561SScott.Carter@Sun.COM 4338561SScott.Carter@Sun.COM /* Lock the pool */ 4348561SScott.Carter@Sun.COM pool_p = req_p->ireq_pool_p; 4358561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 4368561SScott.Carter@Sun.COM 4378561SScott.Carter@Sun.COM /* Update pool and request */ 4388561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 4398561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 4408561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 4418561SScott.Carter@Sun.COM 4428561SScott.Carter@Sun.COM /* Re-sort request in the pool */ 4438561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 4448561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 4458561SScott.Carter@Sun.COM 4468561SScott.Carter@Sun.COM /* Queue pool to be rebalanced */ 4478561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 4488561SScott.Carter@Sun.COM 4498561SScott.Carter@Sun.COM /* Unlock the pool */ 4508561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 4518561SScott.Carter@Sun.COM } 4528561SScott.Carter@Sun.COM 4538561SScott.Carter@Sun.COM return (DDI_SUCCESS); 4548561SScott.Carter@Sun.COM } 4558561SScott.Carter@Sun.COM 4568561SScott.Carter@Sun.COM /* 4578561SScott.Carter@Sun.COM * i_ddi_irm_remove() 4588561SScott.Carter@Sun.COM * 4598561SScott.Carter@Sun.COM * Remove a request from an interrupt pool, and balance the pool. 4608561SScott.Carter@Sun.COM */ 4618561SScott.Carter@Sun.COM int 4628561SScott.Carter@Sun.COM i_ddi_irm_remove(dev_info_t *dip) 4638561SScott.Carter@Sun.COM { 4648561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 4658561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 4668561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 4678561SScott.Carter@Sun.COM uint_t nmin; 4688561SScott.Carter@Sun.COM 4698561SScott.Carter@Sun.COM ASSERT(dip != NULL); 4708561SScott.Carter@Sun.COM 4718561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip)); 4728561SScott.Carter@Sun.COM 4738561SScott.Carter@Sun.COM /* Validate parameters */ 4748561SScott.Carter@Sun.COM if (dip == NULL) { 4758561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n")); 4768561SScott.Carter@Sun.COM return (DDI_EINVAL); 4778561SScott.Carter@Sun.COM } 4788561SScott.Carter@Sun.COM 4798561SScott.Carter@Sun.COM /* Check if the device has a request */ 4808561SScott.Carter@Sun.COM if (!(intr_p = DEVI(dip)->devi_intr_p) || 4818561SScott.Carter@Sun.COM !(req_p = intr_p->devi_irm_req_p)) { 4828561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n")); 4838561SScott.Carter@Sun.COM return (DDI_EINVAL); 4848561SScott.Carter@Sun.COM } 4858561SScott.Carter@Sun.COM 4868561SScott.Carter@Sun.COM /* Lock the pool */ 4878561SScott.Carter@Sun.COM pool_p = req_p->ireq_pool_p; 4888561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 4898561SScott.Carter@Sun.COM 4908561SScott.Carter@Sun.COM /* Remove request */ 4918561SScott.Carter@Sun.COM nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq; 4928561SScott.Carter@Sun.COM pool_p->ipool_minno -= nmin; 4938561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 4948561SScott.Carter@Sun.COM pool_p->ipool_resno -= req_p->ireq_navail; 4958561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 4968561SScott.Carter@Sun.COM 4978561SScott.Carter@Sun.COM /* Queue pool to be rebalanced */ 4988561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 4998561SScott.Carter@Sun.COM 5008561SScott.Carter@Sun.COM /* Unlock the pool */ 5018561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 5028561SScott.Carter@Sun.COM 5038561SScott.Carter@Sun.COM /* Destroy the request */ 5048561SScott.Carter@Sun.COM intr_p->devi_irm_req_p = NULL; 5058561SScott.Carter@Sun.COM kmem_free(req_p, sizeof (ddi_irm_req_t)); 5068561SScott.Carter@Sun.COM 5078561SScott.Carter@Sun.COM return (DDI_SUCCESS); 5088561SScott.Carter@Sun.COM } 5098561SScott.Carter@Sun.COM 5108561SScott.Carter@Sun.COM /* 5118561SScott.Carter@Sun.COM * i_ddi_irm_set_cb() 5128561SScott.Carter@Sun.COM * 5138561SScott.Carter@Sun.COM * Change the callback flag for a request, in response to 5148561SScott.Carter@Sun.COM * a change in its callback registration. Then rebalance 5158561SScott.Carter@Sun.COM * the interrupt pool. 5168561SScott.Carter@Sun.COM * 5178561SScott.Carter@Sun.COM * NOTE: the request is not locked because the navail value 5188561SScott.Carter@Sun.COM * is not directly affected. The balancing thread may 5198561SScott.Carter@Sun.COM * modify the navail value in the background after it 5208561SScott.Carter@Sun.COM * locks the request itself. 5218561SScott.Carter@Sun.COM */ 5228561SScott.Carter@Sun.COM void 5238561SScott.Carter@Sun.COM i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag) 5248561SScott.Carter@Sun.COM { 5258561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 5268561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 5278561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 5288561SScott.Carter@Sun.COM uint_t nreq; 5298561SScott.Carter@Sun.COM 5308561SScott.Carter@Sun.COM ASSERT(dip != NULL); 5318561SScott.Carter@Sun.COM 5328561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n", 5338561SScott.Carter@Sun.COM (void *)dip, (int)has_cb_flag)); 5348561SScott.Carter@Sun.COM 5358561SScott.Carter@Sun.COM /* Validate parameters */ 5368561SScott.Carter@Sun.COM if (dip == NULL) 5378561SScott.Carter@Sun.COM return; 5388561SScott.Carter@Sun.COM 5398561SScott.Carter@Sun.COM /* Check for association with interrupt pool */ 5408561SScott.Carter@Sun.COM if (!(intr_p = DEVI(dip)->devi_intr_p) || 5418561SScott.Carter@Sun.COM !(req_p = intr_p->devi_irm_req_p)) { 5428561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n")); 5438561SScott.Carter@Sun.COM return; 5448561SScott.Carter@Sun.COM } 5458561SScott.Carter@Sun.COM 5468561SScott.Carter@Sun.COM /* Lock the pool */ 5478561SScott.Carter@Sun.COM pool_p = req_p->ireq_pool_p; 5488561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 5498561SScott.Carter@Sun.COM 5508561SScott.Carter@Sun.COM /* 5518561SScott.Carter@Sun.COM * Update the request and the pool 5528561SScott.Carter@Sun.COM */ 5538561SScott.Carter@Sun.COM if (has_cb_flag) { 5548561SScott.Carter@Sun.COM 5558561SScott.Carter@Sun.COM /* Update pool statistics */ 5568561SScott.Carter@Sun.COM if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) 5578561SScott.Carter@Sun.COM pool_p->ipool_minno -= (req_p->ireq_nreq - 1); 5588561SScott.Carter@Sun.COM 5598561SScott.Carter@Sun.COM /* Update request */ 5608561SScott.Carter@Sun.COM req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 5618561SScott.Carter@Sun.COM 5628561SScott.Carter@Sun.COM /* Rebalance in background */ 5638561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_FALSE); 5648561SScott.Carter@Sun.COM 5658561SScott.Carter@Sun.COM } else { 5668561SScott.Carter@Sun.COM 5678561SScott.Carter@Sun.COM /* Determine new request size */ 5688561SScott.Carter@Sun.COM nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz); 5698561SScott.Carter@Sun.COM 5708561SScott.Carter@Sun.COM /* Update pool statistics */ 5718561SScott.Carter@Sun.COM pool_p->ipool_reqno -= req_p->ireq_nreq; 5728561SScott.Carter@Sun.COM pool_p->ipool_reqno += nreq; 5738561SScott.Carter@Sun.COM if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) { 5748561SScott.Carter@Sun.COM pool_p->ipool_minno -= 1; 5758561SScott.Carter@Sun.COM pool_p->ipool_minno += nreq; 5768561SScott.Carter@Sun.COM } else { 5778561SScott.Carter@Sun.COM pool_p->ipool_minno -= req_p->ireq_nreq; 5788561SScott.Carter@Sun.COM pool_p->ipool_minno += nreq; 5798561SScott.Carter@Sun.COM } 5808561SScott.Carter@Sun.COM 5818561SScott.Carter@Sun.COM /* Update request size, and re-sort in pool */ 5828561SScott.Carter@Sun.COM req_p->ireq_nreq = nreq; 5838561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_req_list, req_p); 5848561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 5858561SScott.Carter@Sun.COM 5868561SScott.Carter@Sun.COM /* Rebalance synchronously, before losing callback */ 5878561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, B_TRUE); 5888561SScott.Carter@Sun.COM 5898561SScott.Carter@Sun.COM /* Remove callback flag */ 5908561SScott.Carter@Sun.COM req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK); 5918561SScott.Carter@Sun.COM } 5928561SScott.Carter@Sun.COM 5938561SScott.Carter@Sun.COM /* Unlock the pool */ 5948561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 5958561SScott.Carter@Sun.COM } 5968561SScott.Carter@Sun.COM 5978561SScott.Carter@Sun.COM /* 5988561SScott.Carter@Sun.COM * Interrupt Pool Balancing 5998561SScott.Carter@Sun.COM */ 6008561SScott.Carter@Sun.COM 6018561SScott.Carter@Sun.COM /* 6028561SScott.Carter@Sun.COM * irm_balance_thread() 6038561SScott.Carter@Sun.COM * 6048561SScott.Carter@Sun.COM * One instance of this thread operates per each defined IRM pool. 6058561SScott.Carter@Sun.COM * It does the initial activation of the pool, as well as balancing 6068561SScott.Carter@Sun.COM * any requests that were queued up before the pool was active. 6078561SScott.Carter@Sun.COM * Once active, it waits forever to service balance operations. 6088561SScott.Carter@Sun.COM */ 6098561SScott.Carter@Sun.COM static void 6108561SScott.Carter@Sun.COM irm_balance_thread(ddi_irm_pool_t *pool_p) 6118561SScott.Carter@Sun.COM { 6128561SScott.Carter@Sun.COM clock_t interval, wakeup; 6138561SScott.Carter@Sun.COM 6148561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n", 6158561SScott.Carter@Sun.COM (void *)pool_p)); 6168561SScott.Carter@Sun.COM 6178561SScott.Carter@Sun.COM /* Lock the pool */ 6188561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 6198561SScott.Carter@Sun.COM 6208561SScott.Carter@Sun.COM /* Perform initial balance if required */ 6218561SScott.Carter@Sun.COM if (pool_p->ipool_reqno > pool_p->ipool_resno) 6228561SScott.Carter@Sun.COM i_ddi_irm_balance(pool_p); 6238561SScott.Carter@Sun.COM 6248561SScott.Carter@Sun.COM /* Activate the pool */ 6258561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE; 6268561SScott.Carter@Sun.COM 6278561SScott.Carter@Sun.COM /* Main loop */ 6288561SScott.Carter@Sun.COM for (;;) { 6298561SScott.Carter@Sun.COM 6308561SScott.Carter@Sun.COM /* Compute the delay interval */ 6318561SScott.Carter@Sun.COM interval = drv_usectohz(irm_balance_delay * 1000000); 6328561SScott.Carter@Sun.COM 6338561SScott.Carter@Sun.COM /* Sleep until queued */ 6348561SScott.Carter@Sun.COM cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 6358561SScott.Carter@Sun.COM 6368561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n")); 6378561SScott.Carter@Sun.COM 6388561SScott.Carter@Sun.COM /* Wait one interval, or until there are waiters */ 6398561SScott.Carter@Sun.COM if ((interval > 0) && 6408561SScott.Carter@Sun.COM !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) && 6418561SScott.Carter@Sun.COM !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) { 6428561SScott.Carter@Sun.COM wakeup = ddi_get_lbolt() + interval; 6438561SScott.Carter@Sun.COM (void) cv_timedwait(&pool_p->ipool_cv, 6448561SScott.Carter@Sun.COM &pool_p->ipool_lock, wakeup); 6458561SScott.Carter@Sun.COM } 6468561SScott.Carter@Sun.COM 6478561SScott.Carter@Sun.COM /* Check if awakened to exit */ 6488561SScott.Carter@Sun.COM if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) { 6498561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 6508561SScott.Carter@Sun.COM "irm_balance_thread: exiting...\n")); 6518561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 6528561SScott.Carter@Sun.COM thread_exit(); 6538561SScott.Carter@Sun.COM } 6548561SScott.Carter@Sun.COM 6558561SScott.Carter@Sun.COM /* Balance the pool */ 6568561SScott.Carter@Sun.COM i_ddi_irm_balance(pool_p); 6578561SScott.Carter@Sun.COM 6588561SScott.Carter@Sun.COM /* Notify waiters */ 6598561SScott.Carter@Sun.COM if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) { 6608561SScott.Carter@Sun.COM cv_broadcast(&pool_p->ipool_cv); 6618561SScott.Carter@Sun.COM pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS); 6628561SScott.Carter@Sun.COM } 6638561SScott.Carter@Sun.COM 6648561SScott.Carter@Sun.COM /* Clear QUEUED condition */ 6658561SScott.Carter@Sun.COM pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED); 6668561SScott.Carter@Sun.COM } 6678561SScott.Carter@Sun.COM } 6688561SScott.Carter@Sun.COM 6698561SScott.Carter@Sun.COM /* 6708561SScott.Carter@Sun.COM * i_ddi_irm_balance() 6718561SScott.Carter@Sun.COM * 6728561SScott.Carter@Sun.COM * Balance a pool. The general algorithm is to first reset all 6738561SScott.Carter@Sun.COM * requests to their maximum size, use reduction algorithms to 6748561SScott.Carter@Sun.COM * solve any imbalance, and then notify affected drivers. 6758561SScott.Carter@Sun.COM */ 6768561SScott.Carter@Sun.COM static void 6778561SScott.Carter@Sun.COM i_ddi_irm_balance(ddi_irm_pool_t *pool_p) 6788561SScott.Carter@Sun.COM { 6798561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 6808561SScott.Carter@Sun.COM 6818561SScott.Carter@Sun.COM #ifdef DEBUG 6828561SScott.Carter@Sun.COM uint_t debug_totsz = 0; 6838561SScott.Carter@Sun.COM int debug_policy = 0; 6848561SScott.Carter@Sun.COM #endif /* DEBUG */ 6858561SScott.Carter@Sun.COM 6868561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 6878561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 6888561SScott.Carter@Sun.COM 6898561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n", 6908561SScott.Carter@Sun.COM (void *)pool_p)); 6918561SScott.Carter@Sun.COM 6928561SScott.Carter@Sun.COM #ifdef DEBUG /* Adjust size and policy settings */ 6938561SScott.Carter@Sun.COM if (irm_debug_size > pool_p->ipool_minno) { 6948561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n", 6958561SScott.Carter@Sun.COM irm_debug_size)); 6968561SScott.Carter@Sun.COM debug_totsz = pool_p->ipool_totsz; 6978561SScott.Carter@Sun.COM pool_p->ipool_totsz = irm_debug_size; 6988561SScott.Carter@Sun.COM } 6998561SScott.Carter@Sun.COM if (DDI_IRM_POLICY_VALID(irm_debug_policy)) { 7008561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 7018561SScott.Carter@Sun.COM "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy)); 7028561SScott.Carter@Sun.COM debug_policy = pool_p->ipool_policy; 7038561SScott.Carter@Sun.COM pool_p->ipool_policy = irm_debug_policy; 7048561SScott.Carter@Sun.COM } 7058561SScott.Carter@Sun.COM #endif /* DEBUG */ 7068561SScott.Carter@Sun.COM 7078561SScott.Carter@Sun.COM /* Lock the availability lock */ 7088561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_navail_lock); 7098561SScott.Carter@Sun.COM 7108561SScott.Carter@Sun.COM /* 7118561SScott.Carter@Sun.COM * Put all of the reducible requests into a scratch list. 7128561SScott.Carter@Sun.COM * Reset each one of them to their maximum availability. 7138561SScott.Carter@Sun.COM */ 7148561SScott.Carter@Sun.COM for (req_p = list_head(&pool_p->ipool_req_list); req_p; 7158561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_req_list, req_p)) { 7168561SScott.Carter@Sun.COM if (DDI_IRM_IS_REDUCIBLE(req_p)) { 7178561SScott.Carter@Sun.COM pool_p->ipool_resno -= req_p->ireq_navail; 7188561SScott.Carter@Sun.COM req_p->ireq_scratch = req_p->ireq_navail; 7198561SScott.Carter@Sun.COM req_p->ireq_navail = req_p->ireq_nreq; 7208561SScott.Carter@Sun.COM pool_p->ipool_resno += req_p->ireq_navail; 7218561SScott.Carter@Sun.COM list_insert_tail(&pool_p->ipool_scratch_list, req_p); 7228561SScott.Carter@Sun.COM } 7238561SScott.Carter@Sun.COM } 7248561SScott.Carter@Sun.COM 7258561SScott.Carter@Sun.COM /* Balance the requests */ 7268561SScott.Carter@Sun.COM i_ddi_irm_reduce(pool_p); 7278561SScott.Carter@Sun.COM 7288561SScott.Carter@Sun.COM /* Unlock the availability lock */ 7298561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_navail_lock); 7308561SScott.Carter@Sun.COM 7318561SScott.Carter@Sun.COM /* 7328561SScott.Carter@Sun.COM * Process REMOVE notifications. 7338561SScott.Carter@Sun.COM * 7348561SScott.Carter@Sun.COM * If a driver fails to release interrupts: exclude it from 7358561SScott.Carter@Sun.COM * further processing, correct the resulting imbalance, and 7368561SScott.Carter@Sun.COM * start over again at the head of the scratch list. 7378561SScott.Carter@Sun.COM */ 7388561SScott.Carter@Sun.COM req_p = list_head(&pool_p->ipool_scratch_list); 7398561SScott.Carter@Sun.COM while (req_p) { 7408561SScott.Carter@Sun.COM if ((req_p->ireq_navail < req_p->ireq_scratch) && 7418561SScott.Carter@Sun.COM (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) { 7428561SScott.Carter@Sun.COM list_remove(&pool_p->ipool_scratch_list, req_p); 7438561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_navail_lock); 7448561SScott.Carter@Sun.COM i_ddi_irm_reduce(pool_p); 7458561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_navail_lock); 7468561SScott.Carter@Sun.COM req_p = list_head(&pool_p->ipool_scratch_list); 7478561SScott.Carter@Sun.COM } else { 7488561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p); 7498561SScott.Carter@Sun.COM } 7508561SScott.Carter@Sun.COM } 7518561SScott.Carter@Sun.COM 7528561SScott.Carter@Sun.COM /* 7538561SScott.Carter@Sun.COM * Process ADD notifications. 7548561SScott.Carter@Sun.COM * 7558561SScott.Carter@Sun.COM * This is the last use of the scratch list, so empty it. 7568561SScott.Carter@Sun.COM */ 7578561SScott.Carter@Sun.COM while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) { 7588561SScott.Carter@Sun.COM if (req_p->ireq_navail > req_p->ireq_scratch) { 7598561SScott.Carter@Sun.COM (void) i_ddi_irm_notify(pool_p, req_p); 7608561SScott.Carter@Sun.COM } 7618561SScott.Carter@Sun.COM } 7628561SScott.Carter@Sun.COM 7638561SScott.Carter@Sun.COM #ifdef DEBUG /* Restore size and policy settings */ 7648561SScott.Carter@Sun.COM if (debug_totsz != 0) 7658561SScott.Carter@Sun.COM pool_p->ipool_totsz = debug_totsz; 7668561SScott.Carter@Sun.COM if (debug_policy != 0) 7678561SScott.Carter@Sun.COM pool_p->ipool_policy = debug_policy; 7688561SScott.Carter@Sun.COM #endif /* DEBUG */ 7698561SScott.Carter@Sun.COM } 7708561SScott.Carter@Sun.COM 7718561SScott.Carter@Sun.COM /* 7728561SScott.Carter@Sun.COM * i_ddi_irm_reduce() 7738561SScott.Carter@Sun.COM * 7748561SScott.Carter@Sun.COM * Use reduction algorithms to correct an imbalance in a pool. 7758561SScott.Carter@Sun.COM */ 7768561SScott.Carter@Sun.COM static void 7778561SScott.Carter@Sun.COM i_ddi_irm_reduce(ddi_irm_pool_t *pool_p) 7788561SScott.Carter@Sun.COM { 7798561SScott.Carter@Sun.COM int ret, imbalance; 7808561SScott.Carter@Sun.COM 7818561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 7828561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 7838561SScott.Carter@Sun.COM ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy)); 7848561SScott.Carter@Sun.COM 7858561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n", 7868561SScott.Carter@Sun.COM (void *)pool_p)); 7878561SScott.Carter@Sun.COM 7888561SScott.Carter@Sun.COM /* Compute the imbalance. Do nothing if already balanced. */ 7898561SScott.Carter@Sun.COM if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0) 7908561SScott.Carter@Sun.COM return; 7918561SScott.Carter@Sun.COM 7928561SScott.Carter@Sun.COM /* Reduce by policy */ 7938561SScott.Carter@Sun.COM switch (pool_p->ipool_policy) { 7948561SScott.Carter@Sun.COM case DDI_IRM_POLICY_LARGE: 7958561SScott.Carter@Sun.COM ret = i_ddi_irm_reduce_large(pool_p, imbalance); 7968561SScott.Carter@Sun.COM break; 7978561SScott.Carter@Sun.COM case DDI_IRM_POLICY_EVEN: 7988561SScott.Carter@Sun.COM ret = i_ddi_irm_reduce_even(pool_p, imbalance); 7998561SScott.Carter@Sun.COM break; 8008561SScott.Carter@Sun.COM } 8018561SScott.Carter@Sun.COM 8028561SScott.Carter@Sun.COM /* 8038561SScott.Carter@Sun.COM * If the policy based reductions failed, then 8048561SScott.Carter@Sun.COM * possibly reduce new requests as a last resort. 8058561SScott.Carter@Sun.COM */ 8068561SScott.Carter@Sun.COM if (ret != DDI_SUCCESS) { 8078561SScott.Carter@Sun.COM 8088561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 8098561SScott.Carter@Sun.COM "i_ddi_irm_reduce: policy reductions failed.\n")); 8108561SScott.Carter@Sun.COM 8118561SScott.Carter@Sun.COM /* Compute remaining imbalance */ 8128561SScott.Carter@Sun.COM imbalance = pool_p->ipool_resno - pool_p->ipool_totsz; 8138561SScott.Carter@Sun.COM 8148561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 8158561SScott.Carter@Sun.COM 8168561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(pool_p, imbalance); 8178561SScott.Carter@Sun.COM } 8188561SScott.Carter@Sun.COM } 8198561SScott.Carter@Sun.COM 8208561SScott.Carter@Sun.COM /* 8218561SScott.Carter@Sun.COM * i_ddi_irm_enqueue() 8228561SScott.Carter@Sun.COM * 8238561SScott.Carter@Sun.COM * Queue a pool to be balanced. Signals the balancing thread to wake 8248561SScott.Carter@Sun.COM * up and process the pool. If 'wait_flag' is true, then the current 8258561SScott.Carter@Sun.COM * thread becomes a waiter and blocks until the balance is completed. 8268561SScott.Carter@Sun.COM */ 8278561SScott.Carter@Sun.COM static void 8288561SScott.Carter@Sun.COM i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag) 8298561SScott.Carter@Sun.COM { 8308561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 8318561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 8328561SScott.Carter@Sun.COM 8338561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n", 8348561SScott.Carter@Sun.COM (void *)pool_p, (int)wait_flag)); 8358561SScott.Carter@Sun.COM 8368561SScott.Carter@Sun.COM /* Do nothing if pool is already balanced */ 8378561SScott.Carter@Sun.COM #ifndef DEBUG 8388561SScott.Carter@Sun.COM if ((pool_p->ipool_reqno == pool_p->ipool_resno)) { 8398561SScott.Carter@Sun.COM #else 8408561SScott.Carter@Sun.COM if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) { 8418561SScott.Carter@Sun.COM #endif /* DEBUG */ 8428561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 8438561SScott.Carter@Sun.COM "i_ddi_irm_enqueue: pool already balanced\n")); 8448561SScott.Carter@Sun.COM return; 8458561SScott.Carter@Sun.COM } 8468561SScott.Carter@Sun.COM 8478561SScott.Carter@Sun.COM /* Avoid deadlocks when IRM is not active */ 8488561SScott.Carter@Sun.COM if (!irm_active && wait_flag) { 8498561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 8508561SScott.Carter@Sun.COM "i_ddi_irm_enqueue: pool not active.\n")); 8518561SScott.Carter@Sun.COM return; 8528561SScott.Carter@Sun.COM } 8538561SScott.Carter@Sun.COM 8548561SScott.Carter@Sun.COM if (wait_flag) 8558561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS; 8568561SScott.Carter@Sun.COM 8578561SScott.Carter@Sun.COM if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) { 8588561SScott.Carter@Sun.COM pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED; 8598561SScott.Carter@Sun.COM cv_signal(&pool_p->ipool_cv); 8608561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n")); 8618561SScott.Carter@Sun.COM } 8628561SScott.Carter@Sun.COM 8638561SScott.Carter@Sun.COM if (wait_flag) { 8648561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n")); 8658561SScott.Carter@Sun.COM cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 8668561SScott.Carter@Sun.COM } 8678561SScott.Carter@Sun.COM } 8688561SScott.Carter@Sun.COM 8698561SScott.Carter@Sun.COM /* 8708561SScott.Carter@Sun.COM * Reduction Algorithms, Used For Balancing 8718561SScott.Carter@Sun.COM */ 8728561SScott.Carter@Sun.COM 8738561SScott.Carter@Sun.COM /* 8748561SScott.Carter@Sun.COM * i_ddi_irm_reduce_large() 8758561SScott.Carter@Sun.COM * 8768561SScott.Carter@Sun.COM * Algorithm for the DDI_IRM_POLICY_LARGE reduction policy. 8778561SScott.Carter@Sun.COM * 8788561SScott.Carter@Sun.COM * This algorithm generally reduces larger requests first, before 8798561SScott.Carter@Sun.COM * advancing to smaller requests. The scratch list is initially 8808561SScott.Carter@Sun.COM * sorted in descending order by current navail values, which are 8818561SScott.Carter@Sun.COM * maximized prior to reduction. This sorted order is preserved, 8828561SScott.Carter@Sun.COM * but within a range of equally sized requests they are secondarily 8838561SScott.Carter@Sun.COM * sorted in ascending order by initial nreq value. The head of the 8848561SScott.Carter@Sun.COM * list is always selected for reduction, since it is the current 8858561SScott.Carter@Sun.COM * largest request. After being reduced, it is sorted further into 8868561SScott.Carter@Sun.COM * the list before the next iteration. 8878561SScott.Carter@Sun.COM * 8888561SScott.Carter@Sun.COM * Optimizations in this algorithm include trying to reduce multiple 8898561SScott.Carter@Sun.COM * requests together if they are equally sized. And the algorithm 8908561SScott.Carter@Sun.COM * attempts to reduce in larger increments when possible to minimize 8918561SScott.Carter@Sun.COM * the total number of iterations. 8928561SScott.Carter@Sun.COM */ 8938561SScott.Carter@Sun.COM static int 8948561SScott.Carter@Sun.COM i_ddi_irm_reduce_large(ddi_irm_pool_t *pool_p, int imbalance) 8958561SScott.Carter@Sun.COM { 8968925SEvan.Yan@Sun.COM ddi_irm_req_t *head_p, *next_p; 8978925SEvan.Yan@Sun.COM int next_navail, nreqs, reduction; 8988561SScott.Carter@Sun.COM 8998561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 9008561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 9018561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 9028561SScott.Carter@Sun.COM 9038561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 9048561SScott.Carter@Sun.COM "i_ddi_irm_reduce_large: pool_p %p imbalance %d\n", (void *)pool_p, 9058561SScott.Carter@Sun.COM imbalance)); 9068561SScott.Carter@Sun.COM 9078561SScott.Carter@Sun.COM while (imbalance > 0) { 9088561SScott.Carter@Sun.COM 9098925SEvan.Yan@Sun.COM head_p = list_head(&pool_p->ipool_scratch_list); 9108561SScott.Carter@Sun.COM 9118561SScott.Carter@Sun.COM /* Fail if nothing is reducible */ 9128925SEvan.Yan@Sun.COM if (head_p->ireq_navail <= pool_p->ipool_defsz) { 9138561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 9148925SEvan.Yan@Sun.COM "i_ddi_irm_reduce_large: Failure. " 9158925SEvan.Yan@Sun.COM "All requests have downsized to low limit.\n")); 9168561SScott.Carter@Sun.COM return (DDI_FAILURE); 9178561SScott.Carter@Sun.COM } 9188561SScott.Carter@Sun.COM 9198561SScott.Carter@Sun.COM /* Count the number of equally sized requests */ 9208925SEvan.Yan@Sun.COM for (nreqs = 1, next_p = head_p; 9218925SEvan.Yan@Sun.COM (next_p = list_next(&pool_p->ipool_scratch_list, next_p)) != 9228925SEvan.Yan@Sun.COM NULL && (head_p->ireq_navail == next_p->ireq_navail); 9238925SEvan.Yan@Sun.COM nreqs++) 9248925SEvan.Yan@Sun.COM ; 9258561SScott.Carter@Sun.COM 9268925SEvan.Yan@Sun.COM next_navail = next_p ? next_p->ireq_navail : 0; 9278925SEvan.Yan@Sun.COM reduction = head_p->ireq_navail - 9288925SEvan.Yan@Sun.COM MAX(next_navail, pool_p->ipool_defsz); 9298561SScott.Carter@Sun.COM 9308925SEvan.Yan@Sun.COM if ((reduction * nreqs) > imbalance) { 9318925SEvan.Yan@Sun.COM reduction = imbalance / nreqs; 9328925SEvan.Yan@Sun.COM 9338925SEvan.Yan@Sun.COM if (reduction == 0) { 9348925SEvan.Yan@Sun.COM reduction = 1; 9358925SEvan.Yan@Sun.COM nreqs = imbalance; 9368561SScott.Carter@Sun.COM } 9378561SScott.Carter@Sun.COM } 9388561SScott.Carter@Sun.COM 9398925SEvan.Yan@Sun.COM next_p = head_p; 9408925SEvan.Yan@Sun.COM while (nreqs--) { 9418925SEvan.Yan@Sun.COM imbalance -= reduction; 9428925SEvan.Yan@Sun.COM next_p->ireq_navail -= reduction; 9438925SEvan.Yan@Sun.COM pool_p->ipool_resno -= reduction; 9448925SEvan.Yan@Sun.COM next_p = list_next(&pool_p->ipool_scratch_list, next_p); 9458561SScott.Carter@Sun.COM } 9468561SScott.Carter@Sun.COM 9478925SEvan.Yan@Sun.COM if (next_p && next_p->ireq_navail > head_p->ireq_navail) { 9488925SEvan.Yan@Sun.COM ASSERT(imbalance == 0); 9498561SScott.Carter@Sun.COM i_ddi_irm_reduce_large_resort(pool_p); 9508561SScott.Carter@Sun.COM } 9518561SScott.Carter@Sun.COM } 9528561SScott.Carter@Sun.COM 9538561SScott.Carter@Sun.COM return (DDI_SUCCESS); 9548561SScott.Carter@Sun.COM } 9558561SScott.Carter@Sun.COM 9568561SScott.Carter@Sun.COM /* 9578561SScott.Carter@Sun.COM * i_ddi_irm_reduce_large_resort() 9588561SScott.Carter@Sun.COM * 9598561SScott.Carter@Sun.COM * Helper function for i_ddi_irm_reduce_large(). Once a request 9608561SScott.Carter@Sun.COM * is reduced, this resorts it further down into the list as necessary. 9618561SScott.Carter@Sun.COM */ 9628561SScott.Carter@Sun.COM static void 9638561SScott.Carter@Sun.COM i_ddi_irm_reduce_large_resort(ddi_irm_pool_t *pool_p) 9648561SScott.Carter@Sun.COM { 9658925SEvan.Yan@Sun.COM ddi_irm_req_t *start_p, *end_p, *next_p; 9668561SScott.Carter@Sun.COM 9678561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 9688561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 9698561SScott.Carter@Sun.COM 9708925SEvan.Yan@Sun.COM start_p = list_head(&pool_p->ipool_scratch_list); 9718925SEvan.Yan@Sun.COM end_p = list_next(&pool_p->ipool_scratch_list, start_p); 9728925SEvan.Yan@Sun.COM while (end_p && start_p->ireq_navail == end_p->ireq_navail) 9738925SEvan.Yan@Sun.COM end_p = list_next(&pool_p->ipool_scratch_list, end_p); 9748561SScott.Carter@Sun.COM 9758925SEvan.Yan@Sun.COM next_p = end_p; 9768925SEvan.Yan@Sun.COM while (next_p && (next_p->ireq_navail > start_p->ireq_navail)) 9778561SScott.Carter@Sun.COM next_p = list_next(&pool_p->ipool_scratch_list, next_p); 9788561SScott.Carter@Sun.COM 9798925SEvan.Yan@Sun.COM while (start_p != end_p) { 9808925SEvan.Yan@Sun.COM list_remove(&pool_p->ipool_scratch_list, start_p); 9818925SEvan.Yan@Sun.COM list_insert_before(&pool_p->ipool_scratch_list, next_p, 9828925SEvan.Yan@Sun.COM start_p); 9838925SEvan.Yan@Sun.COM start_p = list_head(&pool_p->ipool_scratch_list); 9848925SEvan.Yan@Sun.COM } 9858561SScott.Carter@Sun.COM } 9868561SScott.Carter@Sun.COM 9878561SScott.Carter@Sun.COM /* 9888561SScott.Carter@Sun.COM * i_ddi_irm_reduce_even() 9898561SScott.Carter@Sun.COM * 9908561SScott.Carter@Sun.COM * Algorithm for the DDI_IRM_POLICY_EVEN reduction policy. 9918561SScott.Carter@Sun.COM * 9928561SScott.Carter@Sun.COM * This algorithm reduces requests evenly, without giving a 9938561SScott.Carter@Sun.COM * specific preference to smaller or larger requests. Each 9948561SScott.Carter@Sun.COM * iteration reduces all reducible requests by the same amount 9958561SScott.Carter@Sun.COM * until the imbalance is corrected. Although when possible, 9968561SScott.Carter@Sun.COM * it tries to avoid reducing requests below the threshold of 9978561SScott.Carter@Sun.COM * the interrupt pool's default allocation size. 9988561SScott.Carter@Sun.COM * 9998561SScott.Carter@Sun.COM * An optimization in this algorithm is to reduce the requests 10008561SScott.Carter@Sun.COM * in larger increments during each iteration, to minimize the 10018561SScott.Carter@Sun.COM * total number of iterations required. 10028561SScott.Carter@Sun.COM */ 10038561SScott.Carter@Sun.COM static int 10048561SScott.Carter@Sun.COM i_ddi_irm_reduce_even(ddi_irm_pool_t *pool_p, int imbalance) 10058561SScott.Carter@Sun.COM { 10068561SScott.Carter@Sun.COM ddi_irm_req_t *req_p, *last_p; 10078561SScott.Carter@Sun.COM uint_t nmin = pool_p->ipool_defsz; 10088561SScott.Carter@Sun.COM uint_t nreduce, reduction; 10098561SScott.Carter@Sun.COM 10108561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 10118561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 10128561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 10138561SScott.Carter@Sun.COM 10148561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 10158561SScott.Carter@Sun.COM "i_ddi_irm_reduce_even: pool_p %p imbalance %d\n", 10168561SScott.Carter@Sun.COM (void *)pool_p, imbalance)); 10178561SScott.Carter@Sun.COM 10188925SEvan.Yan@Sun.COM while (imbalance > 0) { 10198561SScott.Carter@Sun.COM 10208561SScott.Carter@Sun.COM /* Count reducible requests */ 10218561SScott.Carter@Sun.COM nreduce = 0; 10228561SScott.Carter@Sun.COM for (req_p = list_head(&pool_p->ipool_scratch_list); req_p; 10238561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p)) { 10248561SScott.Carter@Sun.COM if (req_p->ireq_navail <= nmin) 10258561SScott.Carter@Sun.COM break; 10268561SScott.Carter@Sun.COM last_p = req_p; 10278561SScott.Carter@Sun.COM nreduce++; 10288561SScott.Carter@Sun.COM } 10298561SScott.Carter@Sun.COM 10308925SEvan.Yan@Sun.COM /* Fail if none are reducible */ 10318561SScott.Carter@Sun.COM if (nreduce == 0) { 10328925SEvan.Yan@Sun.COM DDI_INTR_IRMDBG((CE_CONT, 10338925SEvan.Yan@Sun.COM "i_ddi_irm_reduce_even: Failure. " 10348925SEvan.Yan@Sun.COM "All requests have downsized to low limit.\n")); 10358925SEvan.Yan@Sun.COM return (DDI_FAILURE); 10368561SScott.Carter@Sun.COM } 10378561SScott.Carter@Sun.COM 10388561SScott.Carter@Sun.COM /* Compute reduction */ 10398561SScott.Carter@Sun.COM if (nreduce < imbalance) { 10408561SScott.Carter@Sun.COM reduction = last_p->ireq_navail - nmin; 10418561SScott.Carter@Sun.COM if ((reduction * nreduce) > imbalance) { 10428561SScott.Carter@Sun.COM reduction = imbalance / nreduce; 10438561SScott.Carter@Sun.COM } 10448561SScott.Carter@Sun.COM } else { 10458561SScott.Carter@Sun.COM reduction = 1; 10468561SScott.Carter@Sun.COM } 10478561SScott.Carter@Sun.COM 10488561SScott.Carter@Sun.COM /* Start at head of list, but skip excess */ 10498561SScott.Carter@Sun.COM req_p = list_head(&pool_p->ipool_scratch_list); 10508561SScott.Carter@Sun.COM while (nreduce > imbalance) { 10518561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p); 10528561SScott.Carter@Sun.COM nreduce--; 10538561SScott.Carter@Sun.COM } 10548561SScott.Carter@Sun.COM 10558561SScott.Carter@Sun.COM /* Do reductions */ 10568561SScott.Carter@Sun.COM while (req_p && (nreduce > 0)) { 10578561SScott.Carter@Sun.COM imbalance -= reduction; 10588561SScott.Carter@Sun.COM req_p->ireq_navail -= reduction; 10598561SScott.Carter@Sun.COM pool_p->ipool_resno -= reduction; 10608561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p); 10618561SScott.Carter@Sun.COM nreduce--; 10628561SScott.Carter@Sun.COM } 10638561SScott.Carter@Sun.COM } 10648561SScott.Carter@Sun.COM 10658561SScott.Carter@Sun.COM return (DDI_SUCCESS); 10668561SScott.Carter@Sun.COM } 10678561SScott.Carter@Sun.COM 10688561SScott.Carter@Sun.COM /* 10698561SScott.Carter@Sun.COM * i_ddi_irm_reduce_new() 10708561SScott.Carter@Sun.COM * 10718925SEvan.Yan@Sun.COM * Reduces new requests. This is only used as a last resort 10728925SEvan.Yan@Sun.COM * after another reduction algorithm failed. 10738561SScott.Carter@Sun.COM */ 10748561SScott.Carter@Sun.COM static void 10758561SScott.Carter@Sun.COM i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance) 10768561SScott.Carter@Sun.COM { 10778561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 10788925SEvan.Yan@Sun.COM uint_t nreduce; 10798561SScott.Carter@Sun.COM 10808561SScott.Carter@Sun.COM ASSERT(pool_p != NULL); 10818561SScott.Carter@Sun.COM ASSERT(imbalance > 0); 10828561SScott.Carter@Sun.COM ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 10838561SScott.Carter@Sun.COM 10848925SEvan.Yan@Sun.COM while (imbalance > 0) { 10858925SEvan.Yan@Sun.COM nreduce = 0; 10868925SEvan.Yan@Sun.COM for (req_p = list_head(&pool_p->ipool_scratch_list); 10878925SEvan.Yan@Sun.COM req_p && (imbalance > 0); 10888925SEvan.Yan@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p)) { 10898925SEvan.Yan@Sun.COM if (req_p->ireq_flags & DDI_IRM_FLAG_NEW && 10908925SEvan.Yan@Sun.COM req_p->ireq_navail > 1) { 10918925SEvan.Yan@Sun.COM req_p->ireq_navail--; 10928925SEvan.Yan@Sun.COM pool_p->ipool_resno--; 10938925SEvan.Yan@Sun.COM imbalance--; 10948925SEvan.Yan@Sun.COM nreduce++; 10958925SEvan.Yan@Sun.COM } 10968925SEvan.Yan@Sun.COM } 10978925SEvan.Yan@Sun.COM 10988925SEvan.Yan@Sun.COM if (nreduce == 0) 10998925SEvan.Yan@Sun.COM break; 11008925SEvan.Yan@Sun.COM } 11018925SEvan.Yan@Sun.COM 11028561SScott.Carter@Sun.COM for (req_p = list_head(&pool_p->ipool_scratch_list); 11038561SScott.Carter@Sun.COM req_p && (imbalance > 0); 11048561SScott.Carter@Sun.COM req_p = list_next(&pool_p->ipool_scratch_list, req_p)) { 11058561SScott.Carter@Sun.COM if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) { 11068925SEvan.Yan@Sun.COM ASSERT(req_p->ireq_navail == 1); 11078561SScott.Carter@Sun.COM req_p->ireq_navail--; 11088561SScott.Carter@Sun.COM pool_p->ipool_resno--; 11098561SScott.Carter@Sun.COM imbalance--; 11108561SScott.Carter@Sun.COM } 11118561SScott.Carter@Sun.COM } 11128561SScott.Carter@Sun.COM } 11138561SScott.Carter@Sun.COM 11148561SScott.Carter@Sun.COM /* 11158561SScott.Carter@Sun.COM * Miscellaneous Helper Functions 11168561SScott.Carter@Sun.COM */ 11178561SScott.Carter@Sun.COM 11188561SScott.Carter@Sun.COM /* 11198561SScott.Carter@Sun.COM * i_ddi_intr_get_pool() 11208561SScott.Carter@Sun.COM * 11218561SScott.Carter@Sun.COM * Get an IRM pool that supplies interrupts of a specified type. 11228561SScott.Carter@Sun.COM * Invokes a DDI_INTROP_GETPOOL to the bus nexus driver. Fails 11238561SScott.Carter@Sun.COM * if no pool exists. 11248561SScott.Carter@Sun.COM */ 11258561SScott.Carter@Sun.COM ddi_irm_pool_t * 11268561SScott.Carter@Sun.COM i_ddi_intr_get_pool(dev_info_t *dip, int type) 11278561SScott.Carter@Sun.COM { 11288561SScott.Carter@Sun.COM devinfo_intr_t *intr_p; 11298561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 11308561SScott.Carter@Sun.COM ddi_irm_req_t *req_p; 11318561SScott.Carter@Sun.COM ddi_intr_handle_impl_t hdl; 11328561SScott.Carter@Sun.COM 11338561SScott.Carter@Sun.COM ASSERT(dip != NULL); 11348561SScott.Carter@Sun.COM ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 11358561SScott.Carter@Sun.COM 11368561SScott.Carter@Sun.COM if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 11378561SScott.Carter@Sun.COM ((req_p = intr_p->devi_irm_req_p) != NULL) && 11388561SScott.Carter@Sun.COM ((pool_p = req_p->ireq_pool_p) != NULL) && 11398561SScott.Carter@Sun.COM (pool_p->ipool_types & type)) { 11408561SScott.Carter@Sun.COM return (pool_p); 11418561SScott.Carter@Sun.COM } 11428561SScott.Carter@Sun.COM 11438561SScott.Carter@Sun.COM bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 11448561SScott.Carter@Sun.COM hdl.ih_dip = dip; 11458561SScott.Carter@Sun.COM hdl.ih_type = type; 11468561SScott.Carter@Sun.COM 11478561SScott.Carter@Sun.COM if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL, 11488561SScott.Carter@Sun.COM &hdl, (void *)&pool_p) == DDI_SUCCESS) 11498561SScott.Carter@Sun.COM return (pool_p); 11508561SScott.Carter@Sun.COM 11518561SScott.Carter@Sun.COM return (NULL); 11528561SScott.Carter@Sun.COM } 11538561SScott.Carter@Sun.COM 11548561SScott.Carter@Sun.COM /* 11558561SScott.Carter@Sun.COM * i_ddi_irm_insertion_sort() 11568561SScott.Carter@Sun.COM * 11578561SScott.Carter@Sun.COM * Use the insertion sort method to insert a request into a list. 11588561SScott.Carter@Sun.COM * The list is sorted in descending order by request size. 11598561SScott.Carter@Sun.COM */ 11608561SScott.Carter@Sun.COM static void 11618561SScott.Carter@Sun.COM i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p) 11628561SScott.Carter@Sun.COM { 11638561SScott.Carter@Sun.COM ddi_irm_req_t *next_p; 11648561SScott.Carter@Sun.COM 11658561SScott.Carter@Sun.COM next_p = list_head(req_list); 11668561SScott.Carter@Sun.COM 11678561SScott.Carter@Sun.COM while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq)) 11688561SScott.Carter@Sun.COM next_p = list_next(req_list, next_p); 11698561SScott.Carter@Sun.COM 11708561SScott.Carter@Sun.COM list_insert_before(req_list, next_p, req_p); 11718561SScott.Carter@Sun.COM } 11728561SScott.Carter@Sun.COM 11738561SScott.Carter@Sun.COM /* 11748561SScott.Carter@Sun.COM * i_ddi_irm_notify() 11758561SScott.Carter@Sun.COM * 11768561SScott.Carter@Sun.COM * Notify a driver of changes to its interrupt request using the 11778561SScott.Carter@Sun.COM * generic callback mechanism. Checks for errors in processing. 11788561SScott.Carter@Sun.COM */ 11798561SScott.Carter@Sun.COM static int 11808561SScott.Carter@Sun.COM i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p) 11818561SScott.Carter@Sun.COM { 11828561SScott.Carter@Sun.COM ddi_cb_action_t action; 11838561SScott.Carter@Sun.COM ddi_cb_t *cb_p; 11848561SScott.Carter@Sun.COM uint_t nintrs; 11858561SScott.Carter@Sun.COM int ret, count; 11868561SScott.Carter@Sun.COM 11878561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n", 11888561SScott.Carter@Sun.COM (void *)pool_p, (void *)req_p)); 11898561SScott.Carter@Sun.COM 11908561SScott.Carter@Sun.COM /* Do not notify new or unchanged requests */ 11918561SScott.Carter@Sun.COM if ((req_p->ireq_navail == req_p->ireq_scratch) || 11928561SScott.Carter@Sun.COM (req_p->ireq_flags & DDI_IRM_FLAG_NEW)) 11938561SScott.Carter@Sun.COM return (DDI_SUCCESS); 11948561SScott.Carter@Sun.COM 11958561SScott.Carter@Sun.COM /* Determine action and count */ 11968561SScott.Carter@Sun.COM if (req_p->ireq_navail > req_p->ireq_scratch) { 11978561SScott.Carter@Sun.COM action = DDI_CB_INTR_ADD; 11988561SScott.Carter@Sun.COM count = req_p->ireq_navail - req_p->ireq_scratch; 11998561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n", 12008561SScott.Carter@Sun.COM count)); 12018561SScott.Carter@Sun.COM } else { 12028561SScott.Carter@Sun.COM action = DDI_CB_INTR_REMOVE; 12038561SScott.Carter@Sun.COM count = req_p->ireq_scratch - req_p->ireq_navail; 12048561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n", 12058561SScott.Carter@Sun.COM count)); 12068561SScott.Carter@Sun.COM } 12078561SScott.Carter@Sun.COM 12088561SScott.Carter@Sun.COM /* Lookup driver callback */ 12098561SScott.Carter@Sun.COM if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) { 12108561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n")); 12118561SScott.Carter@Sun.COM return (DDI_FAILURE); 12128561SScott.Carter@Sun.COM } 12138561SScott.Carter@Sun.COM 12148561SScott.Carter@Sun.COM /* Do callback */ 12158561SScott.Carter@Sun.COM ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count, 12168561SScott.Carter@Sun.COM cb_p->cb_arg1, cb_p->cb_arg2); 12178561SScott.Carter@Sun.COM 12188561SScott.Carter@Sun.COM /* Log callback errors */ 12198561SScott.Carter@Sun.COM if (ret != DDI_SUCCESS) { 12208561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n", 12218561SScott.Carter@Sun.COM ddi_driver_name(req_p->ireq_dip), 12228561SScott.Carter@Sun.COM ddi_get_instance(req_p->ireq_dip), (int)action, ret); 12238561SScott.Carter@Sun.COM } 12248561SScott.Carter@Sun.COM 12258561SScott.Carter@Sun.COM /* Check if the driver exceeds its availability */ 12268561SScott.Carter@Sun.COM nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip); 12278561SScott.Carter@Sun.COM if (nintrs > req_p->ireq_navail) { 12288561SScott.Carter@Sun.COM cmn_err(CE_WARN, "%s%d: failed to release interrupts " 12298561SScott.Carter@Sun.COM "(nintrs=%d, navail=%d).\n", 12308561SScott.Carter@Sun.COM ddi_driver_name(req_p->ireq_dip), 12318561SScott.Carter@Sun.COM ddi_get_instance(req_p->ireq_dip), nintrs, 12328561SScott.Carter@Sun.COM req_p->ireq_navail); 12338561SScott.Carter@Sun.COM pool_p->ipool_resno += (nintrs - req_p->ireq_navail); 12348561SScott.Carter@Sun.COM req_p->ireq_navail = nintrs; 12358561SScott.Carter@Sun.COM return (DDI_FAILURE); 12368561SScott.Carter@Sun.COM } 12378561SScott.Carter@Sun.COM 12388561SScott.Carter@Sun.COM /* Update request */ 12398561SScott.Carter@Sun.COM req_p->ireq_scratch = req_p->ireq_navail; 12408561SScott.Carter@Sun.COM 12418561SScott.Carter@Sun.COM return (DDI_SUCCESS); 12428561SScott.Carter@Sun.COM } 12438561SScott.Carter@Sun.COM 12448561SScott.Carter@Sun.COM /* 12458561SScott.Carter@Sun.COM * i_ddi_irm_debug_balance() 12468561SScott.Carter@Sun.COM * 12478561SScott.Carter@Sun.COM * A debug/test only routine to force the immediate, 12488561SScott.Carter@Sun.COM * synchronous rebalancing of an interrupt pool. 12498561SScott.Carter@Sun.COM */ 12508561SScott.Carter@Sun.COM #ifdef DEBUG 12518561SScott.Carter@Sun.COM void 12528561SScott.Carter@Sun.COM i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag) 12538561SScott.Carter@Sun.COM { 12548561SScott.Carter@Sun.COM ddi_irm_pool_t *pool_p; 12558561SScott.Carter@Sun.COM int type; 12568561SScott.Carter@Sun.COM 12578561SScott.Carter@Sun.COM DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n", 12588561SScott.Carter@Sun.COM (void *)dip, (int)wait_flag)); 12598561SScott.Carter@Sun.COM 12608561SScott.Carter@Sun.COM if (((type = i_ddi_intr_get_current_type(dip)) != 0) && 12618561SScott.Carter@Sun.COM ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) { 12628561SScott.Carter@Sun.COM mutex_enter(&pool_p->ipool_lock); 12638561SScott.Carter@Sun.COM i_ddi_irm_enqueue(pool_p, wait_flag); 12648561SScott.Carter@Sun.COM mutex_exit(&pool_p->ipool_lock); 12658561SScott.Carter@Sun.COM } 12668561SScott.Carter@Sun.COM } 12678561SScott.Carter@Sun.COM #endif 1268