1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/note.h> 26 #include <sys/sysmacros.h> 27 #include <sys/types.h> 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/kmem.h> 31 #include <sys/cmn_err.h> 32 #include <sys/debug.h> 33 #include <sys/ddi.h> 34 #include <sys/sunndi.h> 35 #include <sys/ndi_impldefs.h> /* include prototypes */ 36 37 #if defined(__i386) || defined(__amd64) 38 /* 39 * MSI-X allocation limit. 40 */ 41 extern uint_t ddi_msix_alloc_limit; 42 #endif 43 44 /* 45 * Interrupt Resource Management (IRM). 46 */ 47 48 #define DDI_IRM_BALANCE_DELAY (60) /* In seconds */ 49 50 #define DDI_IRM_HAS_CB(c) ((c) && (c->cb_flags & DDI_CB_FLAG_INTR)) 51 52 #define DDI_IRM_IS_REDUCIBLE(r) (((r->ireq_flags & DDI_IRM_FLAG_CALLBACK) && \ 53 (r->ireq_type == DDI_INTR_TYPE_MSIX)) || \ 54 (r->ireq_flags & DDI_IRM_FLAG_NEW)) 55 56 extern pri_t minclsyspri; 57 58 /* Global policies */ 59 int irm_enable = 1; 60 boolean_t irm_active = B_FALSE; 61 int irm_default_policy = DDI_IRM_POLICY_LARGE; 62 uint_t irm_balance_delay = DDI_IRM_BALANCE_DELAY; 63 64 /* Global list of interrupt pools */ 65 kmutex_t irm_pools_lock; 66 list_t irm_pools_list; 67 68 /* Global debug tunables */ 69 #ifdef DEBUG 70 int irm_debug_policy = 0; 71 uint_t irm_debug_size = 0; 72 #endif /* DEBUG */ 73 74 static void irm_balance_thread(ddi_irm_pool_t *); 75 static void i_ddi_irm_balance(ddi_irm_pool_t *); 76 static void i_ddi_irm_enqueue(ddi_irm_pool_t *, boolean_t); 77 static void i_ddi_irm_reduce(ddi_irm_pool_t *pool); 78 static int i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *, int, int); 79 static void i_ddi_irm_reduce_new(ddi_irm_pool_t *, int); 80 static void i_ddi_irm_insertion_sort(list_t *, ddi_irm_req_t *); 81 static int i_ddi_irm_notify(ddi_irm_pool_t *, ddi_irm_req_t *); 82 static int i_ddi_irm_modify_increase(ddi_irm_req_t *, int); 83 84 /* 85 * OS Initialization Routines 86 */ 87 88 /* 89 * irm_init() 90 * 91 * Initialize IRM subsystem before any drivers are attached. 92 */ 93 void 94 irm_init(void) 95 { 96 /* Do nothing if IRM is disabled */ 97 if (!irm_enable) 98 return; 99 100 /* Verify that the default balancing policy is valid */ 101 if (!DDI_IRM_POLICY_VALID(irm_default_policy)) 102 irm_default_policy = DDI_IRM_POLICY_LARGE; 103 104 /* Initialize the global list of interrupt pools */ 105 mutex_init(&irm_pools_lock, NULL, MUTEX_DRIVER, NULL); 106 list_create(&irm_pools_list, sizeof (ddi_irm_pool_t), 107 offsetof(ddi_irm_pool_t, ipool_link)); 108 } 109 110 /* 111 * i_ddi_irm_poststartup() 112 * 113 * IRM is not activated until after the IO subsystem is initialized. 114 * When activated, per-pool balancing threads are spawned and a flag 115 * is set so that all future pools will be activated when created. 116 * 117 * NOTE: the global variable 'irm_enable' disables IRM if zero. 118 */ 119 void 120 i_ddi_irm_poststartup(void) 121 { 122 ddi_irm_pool_t *pool_p; 123 124 /* Do nothing if IRM is disabled */ 125 if (!irm_enable) 126 return; 127 128 /* Lock the global list */ 129 mutex_enter(&irm_pools_lock); 130 131 /* Activate all defined pools */ 132 for (pool_p = list_head(&irm_pools_list); pool_p; 133 pool_p = list_next(&irm_pools_list, pool_p)) 134 pool_p->ipool_thread = thread_create(NULL, 0, 135 irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 136 137 /* Set future pools to be active */ 138 irm_active = B_TRUE; 139 140 /* Unlock the global list */ 141 mutex_exit(&irm_pools_lock); 142 } 143 144 /* 145 * NDI interfaces for creating/destroying IRM pools. 146 */ 147 148 /* 149 * ndi_irm_create() 150 * 151 * Nexus interface to create an IRM pool. Create the new 152 * pool and add it to the global list of interrupt pools. 153 */ 154 int 155 ndi_irm_create(dev_info_t *dip, ddi_irm_params_t *paramsp, 156 ddi_irm_pool_t **pool_retp) 157 { 158 ddi_irm_pool_t *pool_p; 159 160 ASSERT(dip != NULL); 161 ASSERT(paramsp != NULL); 162 ASSERT(pool_retp != NULL); 163 ASSERT(paramsp->iparams_total >= 1); 164 ASSERT(paramsp->iparams_types != 0); 165 166 DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_create: dip %p\n", (void *)dip)); 167 168 /* Check if IRM is enabled */ 169 if (!irm_enable) 170 return (NDI_FAILURE); 171 172 /* Validate parameters */ 173 if ((dip == NULL) || (paramsp == NULL) || (pool_retp == NULL) || 174 (paramsp->iparams_total < 1) || (paramsp->iparams_types == 0)) 175 return (NDI_FAILURE); 176 177 /* Allocate and initialize the pool */ 178 pool_p = kmem_zalloc(sizeof (ddi_irm_pool_t), KM_SLEEP); 179 pool_p->ipool_owner = dip; 180 pool_p->ipool_policy = irm_default_policy; 181 pool_p->ipool_types = paramsp->iparams_types; 182 pool_p->ipool_totsz = paramsp->iparams_total; 183 pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, MAX(DDI_MIN_MSIX_ALLOC, 184 paramsp->iparams_total / DDI_MSIX_ALLOC_DIVIDER)); 185 list_create(&pool_p->ipool_req_list, sizeof (ddi_irm_req_t), 186 offsetof(ddi_irm_req_t, ireq_link)); 187 list_create(&pool_p->ipool_scratch_list, sizeof (ddi_irm_req_t), 188 offsetof(ddi_irm_req_t, ireq_scratch_link)); 189 cv_init(&pool_p->ipool_cv, NULL, CV_DRIVER, NULL); 190 mutex_init(&pool_p->ipool_lock, NULL, MUTEX_DRIVER, NULL); 191 mutex_init(&pool_p->ipool_navail_lock, NULL, MUTEX_DRIVER, NULL); 192 193 /* Add to global list of pools */ 194 mutex_enter(&irm_pools_lock); 195 list_insert_tail(&irm_pools_list, pool_p); 196 mutex_exit(&irm_pools_lock); 197 198 /* If IRM is active, then activate the pool */ 199 if (irm_active) 200 pool_p->ipool_thread = thread_create(NULL, 0, 201 irm_balance_thread, pool_p, 0, &p0, TS_RUN, minclsyspri); 202 203 *pool_retp = pool_p; 204 return (NDI_SUCCESS); 205 } 206 207 /* 208 * ndi_irm_resize_pool() 209 * 210 * Nexus interface to resize IRM pool. If the pool size drops 211 * below the allocated number of vectors then initiate rebalance 212 * operation before resizing the pool. If rebalance operation fails 213 * then return NDI_FAILURE. 214 */ 215 int 216 ndi_irm_resize_pool(ddi_irm_pool_t *pool_p, uint_t new_size) 217 { 218 uint_t prev_size; 219 220 ASSERT(pool_p != NULL); 221 222 DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 223 " current-size 0x%x new-size 0x%x\n", 224 (void *)pool_p, pool_p->ipool_totsz, new_size)); 225 226 if (pool_p == NULL) 227 return (NDI_EINVAL); 228 229 /* Check if IRM is enabled */ 230 if (!irm_enable) 231 return (NDI_FAILURE); 232 233 mutex_enter(&pool_p->ipool_lock); 234 235 /* 236 * If we are increasing the pool size or if the reserved 237 * number of vectors is <= the new pool size then simply 238 * update the pool size and enqueue a reblance operation 239 * if necessary to use the new vectors. 240 */ 241 if ((pool_p->ipool_totsz < new_size) || 242 (pool_p->ipool_resno <= new_size)) { 243 /* set new pool size */ 244 pool_p->ipool_totsz = new_size; 245 /* adjust the default allocation limit */ 246 pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, 247 MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER)); 248 /* queue a rebalance operation to use the new vectors */ 249 if (pool_p->ipool_reqno > pool_p->ipool_resno) 250 i_ddi_irm_enqueue(pool_p, B_FALSE); 251 mutex_exit(&pool_p->ipool_lock); 252 return (NDI_SUCCESS); 253 } 254 255 DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 256 " needs a rebalance operation\n", (void *)pool_p)); 257 258 /* 259 * requires a rebalance operation 260 */ 261 /* save the current pool size */ 262 prev_size = pool_p->ipool_totsz; 263 /* set the pool size to the desired new value */ 264 pool_p->ipool_totsz = new_size; 265 /* perform the rebalance operation */ 266 i_ddi_irm_enqueue(pool_p, B_TRUE); 267 268 /* 269 * If rebalance operation couldn't free up enough 270 * vectors then fail the resize operation. 271 */ 272 if (pool_p->ipool_resno > new_size) { /* rebalance failed */ 273 /* restore the pool size to the previous value */ 274 pool_p->ipool_totsz = prev_size; 275 /* enqueue a rebalance operation for the original pool size */ 276 i_ddi_irm_enqueue(pool_p, B_FALSE); 277 mutex_exit(&pool_p->ipool_lock); 278 return (NDI_FAILURE); 279 } else { /* rebalance worked */ 280 /* adjust the default allocation limit */ 281 pool_p->ipool_defsz = MIN(DDI_MAX_MSIX_ALLOC, 282 MAX(DDI_MIN_MSIX_ALLOC, new_size / DDI_MSIX_ALLOC_DIVIDER)); 283 mutex_exit(&pool_p->ipool_lock); 284 DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_resize_pool: pool_p %p" 285 " resized from %x to %x\n", 286 (void *)pool_p, prev_size, pool_p->ipool_totsz)); 287 return (NDI_SUCCESS); 288 } 289 } 290 291 /* 292 * ndi_irm_destroy() 293 * 294 * Nexus interface to destroy an IRM pool. Destroy the pool 295 * and remove it from the global list of interrupt pools. 296 */ 297 int 298 ndi_irm_destroy(ddi_irm_pool_t *pool_p) 299 { 300 ASSERT(pool_p != NULL); 301 ASSERT(pool_p->ipool_resno == 0); 302 303 DDI_INTR_IRMDBG((CE_CONT, "ndi_irm_destroy: pool_p %p\n", 304 (void *)pool_p)); 305 306 /* Validate parameters */ 307 if (pool_p == NULL) 308 return (NDI_FAILURE); 309 310 /* Validate that pool is empty */ 311 if (pool_p->ipool_resno != 0) 312 return (NDI_BUSY); 313 314 /* Remove the pool from the global list */ 315 mutex_enter(&irm_pools_lock); 316 list_remove(&irm_pools_list, pool_p); 317 mutex_exit(&irm_pools_lock); 318 319 /* Terminate the balancing thread */ 320 mutex_enter(&pool_p->ipool_lock); 321 if (pool_p->ipool_thread && 322 (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) { 323 pool_p->ipool_flags |= DDI_IRM_FLAG_EXIT; 324 cv_signal(&pool_p->ipool_cv); 325 mutex_exit(&pool_p->ipool_lock); 326 thread_join(pool_p->ipool_thread->t_did); 327 } else 328 mutex_exit(&pool_p->ipool_lock); 329 330 /* Destroy the pool */ 331 cv_destroy(&pool_p->ipool_cv); 332 mutex_destroy(&pool_p->ipool_lock); 333 mutex_destroy(&pool_p->ipool_navail_lock); 334 list_destroy(&pool_p->ipool_req_list); 335 list_destroy(&pool_p->ipool_scratch_list); 336 kmem_free(pool_p, sizeof (ddi_irm_pool_t)); 337 338 return (NDI_SUCCESS); 339 } 340 341 /* 342 * Insert/Modify/Remove Interrupt Requests 343 */ 344 345 /* 346 * i_ddi_irm_insert() 347 * 348 * Insert a new request into an interrupt pool, and balance the pool. 349 */ 350 int 351 i_ddi_irm_insert(dev_info_t *dip, int type, int count) 352 { 353 ddi_irm_req_t *req_p; 354 devinfo_intr_t *intr_p; 355 ddi_irm_pool_t *pool_p; 356 uint_t nreq, nmin, npartial; 357 boolean_t irm_flag = B_FALSE; 358 359 ASSERT(dip != NULL); 360 ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 361 ASSERT(count > 0); 362 363 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: dip %p type %d count %d\n", 364 (void *)dip, type, count)); 365 366 /* Validate parameters */ 367 if ((dip == NULL) || (count < 1) || !DDI_INTR_TYPE_FLAG_VALID(type)) { 368 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: invalid args\n")); 369 return (DDI_EINVAL); 370 } 371 372 /* Check for an existing request */ 373 if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 374 (intr_p->devi_irm_req_p != NULL)) 375 return (DDI_SUCCESS); 376 377 /* Check for IRM support from the system */ 378 if ((pool_p = i_ddi_intr_get_pool(dip, type)) == NULL) { 379 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: not supported\n")); 380 return (DDI_ENOTSUP); 381 } 382 383 /* Check for IRM support from the driver */ 384 if (i_ddi_irm_supported(dip, type) == DDI_SUCCESS) 385 irm_flag = B_TRUE; 386 387 /* Determine request size */ 388 nreq = (irm_flag) ? count : 389 MIN(count, i_ddi_intr_get_limit(dip, type, pool_p)); 390 nmin = (irm_flag) ? 1 : nreq; 391 npartial = MIN(nreq, pool_p->ipool_defsz); 392 393 /* Allocate and initialize the request */ 394 req_p = kmem_zalloc(sizeof (ddi_irm_req_t), KM_SLEEP); 395 req_p->ireq_type = type; 396 req_p->ireq_dip = dip; 397 req_p->ireq_pool_p = pool_p; 398 req_p->ireq_nreq = nreq; 399 req_p->ireq_flags = DDI_IRM_FLAG_NEW; 400 if (irm_flag) 401 req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 402 403 /* Lock the pool */ 404 mutex_enter(&pool_p->ipool_lock); 405 406 /* Check for minimal fit before inserting */ 407 if ((pool_p->ipool_minno + nmin) > pool_p->ipool_totsz) { 408 cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 409 ddi_driver_name(dip), ddi_get_instance(dip)); 410 mutex_exit(&pool_p->ipool_lock); 411 kmem_free(req_p, sizeof (ddi_irm_req_t)); 412 return (DDI_EAGAIN); 413 } 414 415 /* Insert the request into the pool */ 416 pool_p->ipool_reqno += nreq; 417 pool_p->ipool_minno += nmin; 418 i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 419 420 /* 421 * Try to fulfill the request. 422 * 423 * If all the interrupts are available, and either the request 424 * is static or the pool is active, then just take them directly. 425 * 426 * If only some of the interrupts are available, and the request 427 * can receive future callbacks, then take some now but queue the 428 * pool to be rebalanced later. 429 * 430 * Otherwise, immediately rebalance the pool and wait. 431 */ 432 if ((!irm_flag || (pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE)) && 433 ((pool_p->ipool_resno + nreq) <= pool_p->ipool_totsz)) { 434 435 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 436 "request completely fulfilled.\n")); 437 pool_p->ipool_resno += nreq; 438 req_p->ireq_navail = nreq; 439 req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 440 441 } else if (irm_flag && 442 ((pool_p->ipool_resno + npartial) <= pool_p->ipool_totsz)) { 443 444 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 445 "request partially fulfilled.\n")); 446 pool_p->ipool_resno += npartial; 447 req_p->ireq_navail = npartial; 448 req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 449 i_ddi_irm_enqueue(pool_p, B_FALSE); 450 451 } else { 452 453 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_insert: " 454 "request needs immediate rebalance.\n")); 455 i_ddi_irm_enqueue(pool_p, B_TRUE); 456 req_p->ireq_flags &= ~(DDI_IRM_FLAG_NEW); 457 } 458 459 /* Fail if the request cannot be fulfilled at all */ 460 if (req_p->ireq_navail == 0) { 461 cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 462 ddi_driver_name(dip), ddi_get_instance(dip)); 463 pool_p->ipool_reqno -= nreq; 464 pool_p->ipool_minno -= nmin; 465 list_remove(&pool_p->ipool_req_list, req_p); 466 mutex_exit(&pool_p->ipool_lock); 467 kmem_free(req_p, sizeof (ddi_irm_req_t)); 468 return (DDI_EAGAIN); 469 } 470 471 /* Unlock the pool */ 472 mutex_exit(&pool_p->ipool_lock); 473 474 intr_p->devi_irm_req_p = req_p; 475 return (DDI_SUCCESS); 476 } 477 478 /* 479 * i_ddi_irm_modify() 480 * 481 * Modify an existing request in an interrupt pool, and balance the pool. 482 */ 483 int 484 i_ddi_irm_modify(dev_info_t *dip, int nreq) 485 { 486 devinfo_intr_t *intr_p; 487 ddi_irm_req_t *req_p; 488 ddi_irm_pool_t *pool_p; 489 int type; 490 int retval = DDI_SUCCESS; 491 492 ASSERT(dip != NULL); 493 ASSERT(nreq > 0); 494 495 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: dip %p nreq %d\n", 496 (void *)dip, nreq)); 497 498 /* Validate parameters */ 499 if ((dip == NULL) || (nreq < 1)) { 500 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 501 return (DDI_EINVAL); 502 } 503 504 /* Do nothing if not mapped to an IRM pool */ 505 if (((intr_p = DEVI(dip)->devi_intr_p) == NULL) || 506 ((req_p = intr_p->devi_irm_req_p) == NULL)) 507 return (DDI_SUCCESS); 508 509 /* Do nothing if new size is the same */ 510 if (nreq == req_p->ireq_nreq) 511 return (DDI_SUCCESS); 512 513 /* Do not allow MSI requests to be resized */ 514 if ((type = req_p->ireq_type) == DDI_INTR_TYPE_MSI) { 515 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid type\n")); 516 return (DDI_ENOTSUP); 517 } 518 519 /* Select the pool */ 520 if ((pool_p = req_p->ireq_pool_p) == NULL) { 521 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: missing pool\n")); 522 return (DDI_FAILURE); 523 } 524 525 /* Validate request size is not too large */ 526 if (nreq > i_ddi_intr_get_limit(dip, type, pool_p)) { 527 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: invalid args\n")); 528 return (DDI_EINVAL); 529 } 530 531 /* Lock the pool */ 532 mutex_enter(&pool_p->ipool_lock); 533 534 /* 535 * Process the modification. 536 * 537 * - To increase a non-IRM request, call the implementation in 538 * i_ddi_irm_modify_increase(). 539 * 540 * - To decrease a non-IRM request, directly update the pool and 541 * request, then queue the pool for later rebalancing. 542 * 543 * - To modify an IRM request, always queue the pool for later 544 * rebalancing. IRM consumers rely upon callbacks for changes. 545 */ 546 if ((nreq > req_p->ireq_nreq) && 547 (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)) { 548 549 retval = i_ddi_irm_modify_increase(req_p, nreq); 550 551 } else { 552 553 /* Update pool and request */ 554 pool_p->ipool_reqno -= req_p->ireq_nreq; 555 pool_p->ipool_reqno += nreq; 556 if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS) { 557 pool_p->ipool_minno -= req_p->ireq_navail; 558 pool_p->ipool_resno -= req_p->ireq_navail; 559 pool_p->ipool_minno += nreq; 560 pool_p->ipool_resno += nreq; 561 req_p->ireq_navail = nreq; 562 } 563 req_p->ireq_nreq = nreq; 564 565 /* Re-sort request into the pool */ 566 list_remove(&pool_p->ipool_req_list, req_p); 567 i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 568 569 /* Queue pool for asynchronous rebalance */ 570 i_ddi_irm_enqueue(pool_p, B_FALSE); 571 } 572 573 /* Unlock the pool */ 574 mutex_exit(&pool_p->ipool_lock); 575 576 return (retval); 577 } 578 579 /* 580 * i_ddi_irm_modify_increase() 581 * 582 * Increase a non-IRM request. The additional interrupts are 583 * directly taken from the pool when possible. Otherwise, an 584 * immediate, synchronous rebalance is performed. A temporary 585 * proxy request is used for any rebalance operation to ensure 586 * the request is not reduced below its current allocation. 587 * 588 * NOTE: pool must already be locked. 589 */ 590 static int 591 i_ddi_irm_modify_increase(ddi_irm_req_t *req_p, int nreq) 592 { 593 dev_info_t *dip = req_p->ireq_dip; 594 ddi_irm_pool_t *pool_p = req_p->ireq_pool_p; 595 ddi_irm_req_t new_req; 596 int count, delta; 597 598 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 599 600 /* Compute number of additional vectors */ 601 count = nreq - req_p->ireq_nreq; 602 603 /* Check for minimal fit */ 604 if ((pool_p->ipool_minno + count) > pool_p->ipool_totsz) { 605 cmn_err(CE_WARN, "%s%d: interrupt pool too full.\n", 606 ddi_driver_name(dip), ddi_get_instance(dip)); 607 return (DDI_EAGAIN); 608 } 609 610 /* Update the pool */ 611 pool_p->ipool_reqno += count; 612 pool_p->ipool_minno += count; 613 614 /* Attempt direct implementation */ 615 if ((pool_p->ipool_resno + count) <= pool_p->ipool_totsz) { 616 req_p->ireq_nreq += count; 617 req_p->ireq_navail += count; 618 pool_p->ipool_resno += count; 619 return (DDI_SUCCESS); 620 } 621 622 /* Rebalance required: fail if pool is not active */ 623 if ((pool_p->ipool_flags & DDI_IRM_FLAG_ACTIVE) == 0) { 624 pool_p->ipool_reqno -= count; 625 pool_p->ipool_minno -= count; 626 return (DDI_EAGAIN); 627 } 628 629 /* Insert temporary proxy request */ 630 bzero(&new_req, sizeof (ddi_irm_req_t)); 631 new_req.ireq_dip = dip; 632 new_req.ireq_nreq = count; 633 new_req.ireq_pool_p = pool_p; 634 new_req.ireq_type = req_p->ireq_type; 635 new_req.ireq_flags = DDI_IRM_FLAG_NEW; 636 i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, &new_req); 637 638 /* Synchronously rebalance */ 639 i_ddi_irm_enqueue(pool_p, B_TRUE); 640 641 /* Remove proxy request, and merge into original request */ 642 req_p->ireq_nreq += count; 643 if ((delta = (count - new_req.ireq_navail)) > 0) { 644 req_p->ireq_nreq -= delta; 645 pool_p->ipool_reqno -= delta; 646 pool_p->ipool_minno -= delta; 647 } 648 req_p->ireq_navail += new_req.ireq_navail; 649 list_remove(&pool_p->ipool_req_list, req_p); 650 list_remove(&pool_p->ipool_req_list, &new_req); 651 i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 652 653 return (DDI_SUCCESS); 654 } 655 656 /* 657 * i_ddi_irm_remove() 658 * 659 * Remove a request from an interrupt pool, and balance the pool. 660 */ 661 int 662 i_ddi_irm_remove(dev_info_t *dip) 663 { 664 devinfo_intr_t *intr_p; 665 ddi_irm_pool_t *pool_p; 666 ddi_irm_req_t *req_p; 667 uint_t nmin; 668 669 ASSERT(dip != NULL); 670 671 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: dip %p\n", (void *)dip)); 672 673 /* Validate parameters */ 674 if (dip == NULL) { 675 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_remove: invalid args\n")); 676 return (DDI_EINVAL); 677 } 678 679 /* Check if the device has a request */ 680 if (!(intr_p = DEVI(dip)->devi_intr_p) || 681 !(req_p = intr_p->devi_irm_req_p)) { 682 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_modify: not found\n")); 683 return (DDI_EINVAL); 684 } 685 686 /* Lock the pool */ 687 pool_p = req_p->ireq_pool_p; 688 mutex_enter(&pool_p->ipool_lock); 689 690 /* Remove request */ 691 nmin = DDI_IRM_IS_REDUCIBLE(req_p) ? 1 : req_p->ireq_nreq; 692 pool_p->ipool_minno -= nmin; 693 pool_p->ipool_reqno -= req_p->ireq_nreq; 694 pool_p->ipool_resno -= req_p->ireq_navail; 695 list_remove(&pool_p->ipool_req_list, req_p); 696 697 /* Queue pool to be rebalanced */ 698 i_ddi_irm_enqueue(pool_p, B_FALSE); 699 700 /* Unlock the pool */ 701 mutex_exit(&pool_p->ipool_lock); 702 703 /* Destroy the request */ 704 intr_p->devi_irm_req_p = NULL; 705 kmem_free(req_p, sizeof (ddi_irm_req_t)); 706 707 return (DDI_SUCCESS); 708 } 709 710 /* 711 * i_ddi_irm_set_cb() 712 * 713 * Change the callback flag for a request, in response to 714 * a change in its callback registration. Then rebalance 715 * the interrupt pool. 716 * 717 * NOTE: the request is not locked because the navail value 718 * is not directly affected. The balancing thread may 719 * modify the navail value in the background after it 720 * locks the request itself. 721 */ 722 void 723 i_ddi_irm_set_cb(dev_info_t *dip, boolean_t has_cb_flag) 724 { 725 devinfo_intr_t *intr_p; 726 ddi_irm_pool_t *pool_p; 727 ddi_irm_req_t *req_p; 728 uint_t nreq; 729 730 ASSERT(dip != NULL); 731 732 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: dip %p has_cb_flag %d\n", 733 (void *)dip, (int)has_cb_flag)); 734 735 /* Validate parameters */ 736 if (dip == NULL) 737 return; 738 739 /* Check for association with interrupt pool */ 740 if (!(intr_p = DEVI(dip)->devi_intr_p) || 741 !(req_p = intr_p->devi_irm_req_p)) { 742 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_set_cb: not in pool\n")); 743 return; 744 } 745 746 /* Lock the pool */ 747 pool_p = req_p->ireq_pool_p; 748 mutex_enter(&pool_p->ipool_lock); 749 750 /* 751 * Update the request and the pool 752 */ 753 if (has_cb_flag) { 754 755 /* Update pool statistics */ 756 if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) 757 pool_p->ipool_minno -= (req_p->ireq_nreq - 1); 758 759 /* Update request */ 760 req_p->ireq_flags |= DDI_IRM_FLAG_CALLBACK; 761 762 /* Rebalance in background */ 763 i_ddi_irm_enqueue(pool_p, B_FALSE); 764 765 } else { 766 767 /* Determine new request size */ 768 nreq = MIN(req_p->ireq_nreq, pool_p->ipool_defsz); 769 770 #if defined(__i386) || defined(__amd64) 771 /* Use the default static limit for non-IRM drivers */ 772 if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) 773 nreq = MIN(nreq, ddi_msix_alloc_limit); 774 #endif 775 776 /* Update pool statistics */ 777 pool_p->ipool_reqno -= req_p->ireq_nreq; 778 pool_p->ipool_reqno += nreq; 779 if (req_p->ireq_type == DDI_INTR_TYPE_MSIX) { 780 pool_p->ipool_minno -= 1; 781 pool_p->ipool_minno += nreq; 782 } else { 783 pool_p->ipool_minno -= req_p->ireq_nreq; 784 pool_p->ipool_minno += nreq; 785 } 786 787 /* Update request size, and re-sort in pool */ 788 req_p->ireq_nreq = nreq; 789 list_remove(&pool_p->ipool_req_list, req_p); 790 i_ddi_irm_insertion_sort(&pool_p->ipool_req_list, req_p); 791 792 /* Rebalance synchronously, before losing callback */ 793 i_ddi_irm_enqueue(pool_p, B_TRUE); 794 795 /* Remove callback flag */ 796 req_p->ireq_flags &= ~(DDI_IRM_FLAG_CALLBACK); 797 } 798 799 /* Unlock the pool */ 800 mutex_exit(&pool_p->ipool_lock); 801 } 802 803 /* 804 * i_ddi_irm_supported() 805 * 806 * Query if IRM is supported by a driver using a specific interrupt type. 807 * Notice that IRM is limited to MSI-X users with registered callbacks. 808 */ 809 int 810 i_ddi_irm_supported(dev_info_t *dip, int type) 811 { 812 ddi_cb_t *cb_p = DEVI(dip)->devi_cb_p; 813 814 return ((DDI_IRM_HAS_CB(cb_p) && (type == DDI_INTR_TYPE_MSIX)) ? 815 DDI_SUCCESS : DDI_ENOTSUP); 816 } 817 818 /* 819 * Interrupt Pool Balancing 820 */ 821 822 /* 823 * irm_balance_thread() 824 * 825 * One instance of this thread operates per each defined IRM pool. 826 * It does the initial activation of the pool, as well as balancing 827 * any requests that were queued up before the pool was active. 828 * Once active, it waits forever to service balance operations. 829 */ 830 static void 831 irm_balance_thread(ddi_irm_pool_t *pool_p) 832 { 833 clock_t interval; 834 835 DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: pool_p %p\n", 836 (void *)pool_p)); 837 838 /* Lock the pool */ 839 mutex_enter(&pool_p->ipool_lock); 840 841 /* Perform initial balance if required */ 842 if (pool_p->ipool_reqno > pool_p->ipool_resno) 843 i_ddi_irm_balance(pool_p); 844 845 /* Activate the pool */ 846 pool_p->ipool_flags |= DDI_IRM_FLAG_ACTIVE; 847 848 /* Main loop */ 849 for (;;) { 850 851 /* Compute the delay interval */ 852 interval = drv_usectohz(irm_balance_delay * 1000000); 853 854 /* Sleep until queued */ 855 cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 856 857 DDI_INTR_IRMDBG((CE_CONT, "irm_balance_thread: signaled.\n")); 858 859 /* Wait one interval, or until there are waiters */ 860 if ((interval > 0) && 861 !(pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) && 862 !(pool_p->ipool_flags & DDI_IRM_FLAG_EXIT)) { 863 (void) cv_reltimedwait(&pool_p->ipool_cv, 864 &pool_p->ipool_lock, interval, TR_CLOCK_TICK); 865 } 866 867 /* Check if awakened to exit */ 868 if (pool_p->ipool_flags & DDI_IRM_FLAG_EXIT) { 869 DDI_INTR_IRMDBG((CE_CONT, 870 "irm_balance_thread: exiting...\n")); 871 mutex_exit(&pool_p->ipool_lock); 872 thread_exit(); 873 } 874 875 /* Balance the pool */ 876 i_ddi_irm_balance(pool_p); 877 878 /* Notify waiters */ 879 if (pool_p->ipool_flags & DDI_IRM_FLAG_WAITERS) { 880 cv_broadcast(&pool_p->ipool_cv); 881 pool_p->ipool_flags &= ~(DDI_IRM_FLAG_WAITERS); 882 } 883 884 /* Clear QUEUED condition */ 885 pool_p->ipool_flags &= ~(DDI_IRM_FLAG_QUEUED); 886 } 887 } 888 889 /* 890 * i_ddi_irm_balance() 891 * 892 * Balance a pool. The general algorithm is to first reset all 893 * requests to their maximum size, use reduction algorithms to 894 * solve any imbalance, and then notify affected drivers. 895 */ 896 static void 897 i_ddi_irm_balance(ddi_irm_pool_t *pool_p) 898 { 899 ddi_irm_req_t *req_p; 900 901 #ifdef DEBUG 902 uint_t debug_totsz = 0; 903 int debug_policy = 0; 904 #endif /* DEBUG */ 905 906 ASSERT(pool_p != NULL); 907 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 908 909 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: pool_p %p\n", 910 (void *)pool_p)); 911 912 #ifdef DEBUG /* Adjust size and policy settings */ 913 if (irm_debug_size > pool_p->ipool_minno) { 914 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_balance: debug size %d\n", 915 irm_debug_size)); 916 debug_totsz = pool_p->ipool_totsz; 917 pool_p->ipool_totsz = irm_debug_size; 918 } 919 if (DDI_IRM_POLICY_VALID(irm_debug_policy)) { 920 DDI_INTR_IRMDBG((CE_CONT, 921 "i_ddi_irm_balance: debug policy %d\n", irm_debug_policy)); 922 debug_policy = pool_p->ipool_policy; 923 pool_p->ipool_policy = irm_debug_policy; 924 } 925 #endif /* DEBUG */ 926 927 /* Lock the availability lock */ 928 mutex_enter(&pool_p->ipool_navail_lock); 929 930 /* 931 * Put all of the reducible requests into a scratch list. 932 * Reset each one of them to their maximum availability. 933 */ 934 for (req_p = list_head(&pool_p->ipool_req_list); req_p; 935 req_p = list_next(&pool_p->ipool_req_list, req_p)) { 936 if (DDI_IRM_IS_REDUCIBLE(req_p)) { 937 pool_p->ipool_resno -= req_p->ireq_navail; 938 req_p->ireq_scratch = req_p->ireq_navail; 939 req_p->ireq_navail = req_p->ireq_nreq; 940 pool_p->ipool_resno += req_p->ireq_navail; 941 list_insert_tail(&pool_p->ipool_scratch_list, req_p); 942 } 943 } 944 945 /* Balance the requests */ 946 i_ddi_irm_reduce(pool_p); 947 948 /* Unlock the availability lock */ 949 mutex_exit(&pool_p->ipool_navail_lock); 950 951 /* 952 * Process REMOVE notifications. 953 * 954 * If a driver fails to release interrupts: exclude it from 955 * further processing, correct the resulting imbalance, and 956 * start over again at the head of the scratch list. 957 */ 958 req_p = list_head(&pool_p->ipool_scratch_list); 959 while (req_p) { 960 if ((req_p->ireq_navail < req_p->ireq_scratch) && 961 (i_ddi_irm_notify(pool_p, req_p) != DDI_SUCCESS)) { 962 list_remove(&pool_p->ipool_scratch_list, req_p); 963 mutex_enter(&pool_p->ipool_navail_lock); 964 i_ddi_irm_reduce(pool_p); 965 mutex_exit(&pool_p->ipool_navail_lock); 966 req_p = list_head(&pool_p->ipool_scratch_list); 967 } else { 968 req_p = list_next(&pool_p->ipool_scratch_list, req_p); 969 } 970 } 971 972 /* 973 * Process ADD notifications. 974 * 975 * This is the last use of the scratch list, so empty it. 976 */ 977 while (req_p = list_remove_head(&pool_p->ipool_scratch_list)) { 978 if (req_p->ireq_navail > req_p->ireq_scratch) { 979 (void) i_ddi_irm_notify(pool_p, req_p); 980 } 981 } 982 983 #ifdef DEBUG /* Restore size and policy settings */ 984 if (debug_totsz != 0) 985 pool_p->ipool_totsz = debug_totsz; 986 if (debug_policy != 0) 987 pool_p->ipool_policy = debug_policy; 988 #endif /* DEBUG */ 989 } 990 991 /* 992 * i_ddi_irm_reduce() 993 * 994 * Use reduction algorithms to correct an imbalance in a pool. 995 */ 996 static void 997 i_ddi_irm_reduce(ddi_irm_pool_t *pool_p) 998 { 999 int imbalance; 1000 1001 ASSERT(pool_p != NULL); 1002 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 1003 ASSERT(DDI_IRM_POLICY_VALID(pool_p->ipool_policy)); 1004 1005 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_reduce: pool_p %p\n", 1006 (void *)pool_p)); 1007 1008 /* Compute the imbalance. Do nothing if already balanced. */ 1009 if ((imbalance = pool_p->ipool_resno - pool_p->ipool_totsz) <= 0) 1010 return; 1011 1012 /* 1013 * Try policy based reduction first. If it failed, then 1014 * possibly reduce new requests as a last resort. 1015 */ 1016 if (i_ddi_irm_reduce_by_policy(pool_p, imbalance, pool_p->ipool_policy) 1017 != DDI_SUCCESS) { 1018 1019 DDI_INTR_IRMDBG((CE_CONT, 1020 "i_ddi_irm_reduce: policy reductions failed.\n")); 1021 1022 /* Compute remaining imbalance */ 1023 imbalance = pool_p->ipool_resno - pool_p->ipool_totsz; 1024 1025 ASSERT(imbalance > 0); 1026 1027 i_ddi_irm_reduce_new(pool_p, imbalance); 1028 } 1029 } 1030 1031 /* 1032 * i_ddi_irm_enqueue() 1033 * 1034 * Queue a pool to be balanced. Signals the balancing thread to wake 1035 * up and process the pool. If 'wait_flag' is true, then the current 1036 * thread becomes a waiter and blocks until the balance is completed. 1037 */ 1038 static void 1039 i_ddi_irm_enqueue(ddi_irm_pool_t *pool_p, boolean_t wait_flag) 1040 { 1041 ASSERT(pool_p != NULL); 1042 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 1043 1044 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool_p %p wait_flag %d\n", 1045 (void *)pool_p, (int)wait_flag)); 1046 1047 /* Do nothing if pool is already balanced */ 1048 #ifndef DEBUG 1049 if ((pool_p->ipool_reqno == pool_p->ipool_resno)) { 1050 #else 1051 if ((pool_p->ipool_reqno == pool_p->ipool_resno) && !irm_debug_size) { 1052 #endif /* DEBUG */ 1053 DDI_INTR_IRMDBG((CE_CONT, 1054 "i_ddi_irm_enqueue: pool already balanced\n")); 1055 return; 1056 } 1057 1058 /* Avoid deadlocks when IRM is not active */ 1059 if (!irm_active && wait_flag) { 1060 DDI_INTR_IRMDBG((CE_CONT, 1061 "i_ddi_irm_enqueue: pool not active.\n")); 1062 return; 1063 } 1064 1065 if (wait_flag) 1066 pool_p->ipool_flags |= DDI_IRM_FLAG_WAITERS; 1067 1068 if (wait_flag || !(pool_p->ipool_flags & DDI_IRM_FLAG_QUEUED)) { 1069 pool_p->ipool_flags |= DDI_IRM_FLAG_QUEUED; 1070 cv_signal(&pool_p->ipool_cv); 1071 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: pool queued.\n")); 1072 } 1073 1074 if (wait_flag) { 1075 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_enqueue: waiting...\n")); 1076 cv_wait(&pool_p->ipool_cv, &pool_p->ipool_lock); 1077 } 1078 } 1079 1080 /* 1081 * i_ddi_irm_reduce_by_policy() 1082 * 1083 * Reduces requests based on reduction policies. 1084 * 1085 * For the DDI_IRM_POLICY_LARGE reduction policy, the algorithm 1086 * generally reduces larger requests first, before advancing 1087 * to smaller requests. 1088 * For the DDI_IRM_POLICY_EVEN reduction policy, the algorithm 1089 * reduces requests evenly, without giving a specific preference 1090 * to smaller or larger requests. Each iteration reduces all 1091 * reducible requests by the same amount until the imbalance is 1092 * corrected. 1093 * 1094 * The scratch list is initially sorted in descending order by current 1095 * navail values, which are maximized prior to reduction. This sorted 1096 * order is preserved. It avoids reducing requests below the threshold 1097 * of the interrupt pool's default allocation size. 1098 * 1099 * Optimizations in this algorithm include trying to reduce multiple 1100 * requests together. And the algorithm attempts to reduce in larger 1101 * increments when possible to minimize the total number of iterations. 1102 */ 1103 static int 1104 i_ddi_irm_reduce_by_policy(ddi_irm_pool_t *pool_p, int imbalance, int policy) 1105 { 1106 ASSERT(pool_p != NULL); 1107 ASSERT(imbalance > 0); 1108 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 1109 1110 while (imbalance > 0) { 1111 list_t *slist_p = &pool_p->ipool_scratch_list; 1112 ddi_irm_req_t *req_p = list_head(slist_p), *last_p; 1113 uint_t nreduce = 0, nremain = 0, stop_navail; 1114 uint_t pool_defsz = pool_p->ipool_defsz; 1115 uint_t reduction, max_redu; 1116 1117 /* Fail if none are reducible */ 1118 if (!req_p || req_p->ireq_navail <= pool_defsz) { 1119 DDI_INTR_IRMDBG((CE_CONT, 1120 "i_ddi_irm_reduce_by_policy: Failure. " 1121 "All requests have downsized to low limit.\n")); 1122 return (DDI_FAILURE); 1123 } 1124 1125 /* Count reducible requests */ 1126 stop_navail = (policy == DDI_IRM_POLICY_LARGE) ? 1127 req_p->ireq_navail - 1 : pool_defsz; 1128 for (; req_p; req_p = list_next(slist_p, req_p)) { 1129 if (req_p->ireq_navail <= stop_navail) 1130 break; 1131 nreduce++; 1132 } 1133 1134 /* Compute reduction */ 1135 last_p = req_p ? list_prev(slist_p, req_p) : list_tail(slist_p); 1136 if ((policy == DDI_IRM_POLICY_LARGE) && req_p && 1137 req_p->ireq_navail > pool_defsz) 1138 reduction = last_p->ireq_navail - req_p->ireq_navail; 1139 else 1140 reduction = last_p->ireq_navail - pool_defsz; 1141 1142 if ((max_redu = reduction * nreduce) > imbalance) { 1143 reduction = imbalance / nreduce; 1144 nremain = imbalance % nreduce; 1145 pool_p->ipool_resno -= imbalance; 1146 imbalance = 0; 1147 } else { 1148 pool_p->ipool_resno -= max_redu; 1149 imbalance -= max_redu; 1150 } 1151 1152 /* Reduce */ 1153 for (req_p = list_head(slist_p); (reduction != 0) && nreduce--; 1154 req_p = list_next(slist_p, req_p)) { 1155 req_p->ireq_navail -= reduction; 1156 } 1157 1158 for (req_p = last_p; nremain--; 1159 req_p = list_prev(slist_p, req_p)) { 1160 req_p->ireq_navail--; 1161 } 1162 } 1163 1164 return (DDI_SUCCESS); 1165 } 1166 1167 /* 1168 * i_ddi_irm_reduce_new() 1169 * 1170 * Reduces new requests. This is only used as a last resort 1171 * after another reduction algorithm failed. 1172 * 1173 * NOTE: The pool locking in i_ddi_irm_insert() ensures 1174 * there can be only one new request at a time in a pool. 1175 */ 1176 static void 1177 i_ddi_irm_reduce_new(ddi_irm_pool_t *pool_p, int imbalance) 1178 { 1179 ddi_irm_req_t *req_p; 1180 1181 ASSERT(pool_p != NULL); 1182 ASSERT(imbalance > 0); 1183 ASSERT(MUTEX_HELD(&pool_p->ipool_lock)); 1184 1185 DDI_INTR_IRMDBG((CE_CONT, 1186 "i_ddi_irm_reduce_new: pool_p %p imbalance %d\n", 1187 (void *)pool_p, imbalance)); 1188 1189 for (req_p = list_head(&pool_p->ipool_scratch_list); req_p; 1190 req_p = list_next(&pool_p->ipool_scratch_list, req_p)) { 1191 if (req_p->ireq_flags & DDI_IRM_FLAG_NEW) { 1192 ASSERT(req_p->ireq_navail >= imbalance); 1193 req_p->ireq_navail -= imbalance; 1194 pool_p->ipool_resno -= imbalance; 1195 return; 1196 } 1197 } 1198 1199 /* should never go here */ 1200 ASSERT(B_FALSE); 1201 } 1202 1203 /* 1204 * Miscellaneous Helper Functions 1205 */ 1206 1207 /* 1208 * i_ddi_intr_get_pool() 1209 * 1210 * Get an IRM pool that supplies interrupts of a specified type. 1211 * Invokes a DDI_INTROP_GETPOOL to the bus nexus driver. Fails 1212 * if no pool exists. 1213 */ 1214 ddi_irm_pool_t * 1215 i_ddi_intr_get_pool(dev_info_t *dip, int type) 1216 { 1217 devinfo_intr_t *intr_p; 1218 ddi_irm_pool_t *pool_p; 1219 ddi_irm_req_t *req_p; 1220 ddi_intr_handle_impl_t hdl; 1221 1222 ASSERT(dip != NULL); 1223 ASSERT(DDI_INTR_TYPE_FLAG_VALID(type)); 1224 1225 if (((intr_p = DEVI(dip)->devi_intr_p) != NULL) && 1226 ((req_p = intr_p->devi_irm_req_p) != NULL) && 1227 ((pool_p = req_p->ireq_pool_p) != NULL) && 1228 (pool_p->ipool_types & type)) { 1229 return (pool_p); 1230 } 1231 1232 bzero(&hdl, sizeof (ddi_intr_handle_impl_t)); 1233 hdl.ih_dip = dip; 1234 hdl.ih_type = type; 1235 1236 if (i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPOOL, 1237 &hdl, (void *)&pool_p) == DDI_SUCCESS) 1238 return (pool_p); 1239 1240 return (NULL); 1241 } 1242 1243 /* 1244 * i_ddi_irm_insertion_sort() 1245 * 1246 * Use the insertion sort method to insert a request into a list. 1247 * The list is sorted in descending order by request size. 1248 */ 1249 static void 1250 i_ddi_irm_insertion_sort(list_t *req_list, ddi_irm_req_t *req_p) 1251 { 1252 ddi_irm_req_t *next_p; 1253 1254 next_p = list_head(req_list); 1255 1256 while (next_p && (next_p->ireq_nreq > req_p->ireq_nreq)) 1257 next_p = list_next(req_list, next_p); 1258 1259 list_insert_before(req_list, next_p, req_p); 1260 } 1261 1262 /* 1263 * i_ddi_irm_notify() 1264 * 1265 * Notify a driver of changes to its interrupt request using the 1266 * generic callback mechanism. Checks for errors in processing. 1267 */ 1268 static int 1269 i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p) 1270 { 1271 ddi_cb_action_t action; 1272 ddi_cb_t *cb_p; 1273 uint_t nintrs; 1274 int ret, count; 1275 1276 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: pool_p %p req_p %p\n", 1277 (void *)pool_p, (void *)req_p)); 1278 1279 /* Do not notify new or unchanged requests */ 1280 if ((req_p->ireq_navail == req_p->ireq_scratch) || 1281 (req_p->ireq_flags & DDI_IRM_FLAG_NEW)) 1282 return (DDI_SUCCESS); 1283 1284 /* Determine action and count */ 1285 if (req_p->ireq_navail > req_p->ireq_scratch) { 1286 action = DDI_CB_INTR_ADD; 1287 count = req_p->ireq_navail - req_p->ireq_scratch; 1288 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: adding %d\n", 1289 count)); 1290 } else { 1291 action = DDI_CB_INTR_REMOVE; 1292 count = req_p->ireq_scratch - req_p->ireq_navail; 1293 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_notify: removing %d\n", 1294 count)); 1295 } 1296 1297 /* Lookup driver callback */ 1298 if ((cb_p = DEVI(req_p->ireq_dip)->devi_cb_p) == NULL) { 1299 DDI_INTR_IRMDBG((CE_WARN, "i_ddi_irm_notify: no callback!\n")); 1300 return (DDI_FAILURE); 1301 } 1302 1303 /* Do callback */ 1304 ret = cb_p->cb_func(req_p->ireq_dip, action, (void *)(uintptr_t)count, 1305 cb_p->cb_arg1, cb_p->cb_arg2); 1306 1307 /* Log callback errors */ 1308 if (ret != DDI_SUCCESS) { 1309 cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n", 1310 ddi_driver_name(req_p->ireq_dip), 1311 ddi_get_instance(req_p->ireq_dip), (int)action, ret); 1312 } 1313 1314 /* Check if the driver exceeds its availability */ 1315 nintrs = i_ddi_intr_get_current_nintrs(req_p->ireq_dip); 1316 if (nintrs > req_p->ireq_navail) { 1317 cmn_err(CE_WARN, "%s%d: failed to release interrupts " 1318 "(nintrs=%d, navail=%d).\n", 1319 ddi_driver_name(req_p->ireq_dip), 1320 ddi_get_instance(req_p->ireq_dip), nintrs, 1321 req_p->ireq_navail); 1322 pool_p->ipool_resno += (nintrs - req_p->ireq_navail); 1323 req_p->ireq_navail = nintrs; 1324 return (DDI_FAILURE); 1325 } 1326 1327 /* Update request */ 1328 req_p->ireq_scratch = req_p->ireq_navail; 1329 1330 return (DDI_SUCCESS); 1331 } 1332 1333 /* 1334 * i_ddi_irm_debug_balance() 1335 * 1336 * A debug/test only routine to force the immediate, 1337 * synchronous rebalancing of an interrupt pool. 1338 */ 1339 #ifdef DEBUG 1340 void 1341 i_ddi_irm_debug_balance(dev_info_t *dip, boolean_t wait_flag) 1342 { 1343 ddi_irm_pool_t *pool_p; 1344 int type; 1345 1346 DDI_INTR_IRMDBG((CE_CONT, "i_ddi_irm_debug_balance: dip %p wait %d\n", 1347 (void *)dip, (int)wait_flag)); 1348 1349 if (((type = i_ddi_intr_get_current_type(dip)) != 0) && 1350 ((pool_p = i_ddi_intr_get_pool(dip, type)) != NULL)) { 1351 mutex_enter(&pool_p->ipool_lock); 1352 i_ddi_irm_enqueue(pool_p, wait_flag); 1353 mutex_exit(&pool_p->ipool_lock); 1354 } 1355 } 1356 #endif 1357