1 /* 2 * SPDX-FileCopyrightText: Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 * SPDX-License-Identifier: MIT 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 /*! 25 * @file 26 * 27 * @brief Implementation for the NUMA interfaces, used by parent module PMA only. 28 * This file interfaces with the RM Linux layer which interfaces with the 29 * Linux kernel. 30 */ 31 32 #include "gpu/mem_mgr/phys_mem_allocator/numa.h" 33 #include "gpu/mem_mgr/phys_mem_allocator/phys_mem_allocator_util.h" 34 #include "gpu/mem_mgr/mem_scrub.h" 35 #include "utils/nvprintf.h" 36 #include "utils/nvassert.h" 37 #include "os/os.h" 38 39 // 40 // Local helper functions and declarations 41 // 42 43 //TODO merge or nuke these functions 44 static NV_STATUS _pmaNumaAvailableEvictablePage(PMA *pPma, NvS32 *validRegionList); 45 static NV_STATUS _pmaNumaAvailableEvictableRange(PMA *pPma, NvS32 *validRegionList, 46 NvLength actualSize, NvU64 pageSize, NvU64 *evictStart, NvU64 *evictEnd); 47 static NV_STATUS _pmaNumaAllocateRange(PMA *pPma, NvU32 numaNodeId, NvLength actualSize, 48 NvU64 pageSize, NvU64 *pPages, NvBool bScrubOnAlloc, NvBool allowEvict, NvS32 *validRegionList, 49 NvU64 *allocatedCount); 50 static NV_STATUS _pmaNumaAllocatePages (PMA *pPma, NvU32 numaNodeId, NvU64 pageSize, 51 NvLength allocationCount, NvU64 *pPages, NvBool bScrubOnAlloc, NvBool allowEvict, NvS32 *validRegionList, 52 NvU64 *allocatedPages); 53 54 /*! 55 * @brief Check if there is at least one evictable page from UVM. 56 */ 57 static NV_STATUS _pmaNumaAvailableEvictablePage 58 ( 59 PMA *pPma, 60 NvS32 *validRegionList 61 ) 62 { 63 NvU32 regionIdx; 64 PMA_PAGESTATUS frameState; 65 void *pMap = NULL; 66 NV_STATUS status = NV_ERR_NO_MEMORY; 67 68 for (regionIdx = 0; regionIdx < pPma->regSize; regionIdx++) 69 { 70 NvU32 regId, frameNum; 71 NvU64 totalFrames; 72 73 regId = (NvU32)validRegionList[regionIdx]; 74 75 if (validRegionList[regionIdx] == -1) 76 continue; 77 78 pMap = pPma->pRegions[regId]; 79 pPma->pMapInfo->pmaMapGetSize(pMap, &totalFrames); 80 totalFrames >>= PMA_PAGE_SHIFT; 81 82 for (frameNum = 0; frameNum < totalFrames; frameNum++) 83 { 84 frameState = pPma->pMapInfo->pmaMapRead(pMap, frameNum, NV_TRUE); 85 if ((frameState & STATE_MASK) == STATE_UNPIN) 86 { 87 status = NV_OK; 88 break; 89 } 90 } 91 92 if (status == NV_OK) 93 break; 94 } 95 96 if (status == NV_OK) 97 NV_PRINTF(LEVEL_INFO, "Evictable frame: FOUND\n"); 98 else 99 NV_PRINTF(LEVEL_INFO, "Evictable frame: NOT FOUND\n"); 100 101 return status; 102 } 103 104 /*! 105 * @brief Check if there is a contiguous range of 106 * evictable frame with UVM and get the start 107 * and end address if there is 108 * In NUMA, OS manages memory and PMA will only track allocated memory in ALLOC_PIN 109 * and ALLOC_UNPIN state. FREE memory is managed by OS and cannot be tracked by PMA 110 * and hence PMA cannot consider FREE memory for eviction and can only consider frames 111 * in known state to PMA or eviction. ALLOC_PIN cannot be evicted and hence only ALLOC_UNPIN 112 * can be evictable. 113 */ 114 NV_STATUS _pmaNumaAvailableEvictableRange 115 ( 116 PMA *pPma, 117 NvS32 *validRegionList, 118 NvLength actualSize, 119 NvU64 pageSize, 120 NvU64 *evictStart, 121 NvU64 *evictEnd 122 ) 123 { 124 void *pMap = NULL; 125 NvU32 regionIdx; 126 NV_STATUS status = NV_ERR_NO_MEMORY; 127 128 if ((evictStart == NULL) || (evictEnd == NULL)) 129 { 130 return NV_ERR_INVALID_ARGUMENT; 131 } 132 133 *evictStart = 0; 134 *evictEnd = 0; 135 136 for (regionIdx = 0; regionIdx < pPma->regSize; regionIdx++) 137 { 138 NvU64 addrBase; 139 NvU32 regId; 140 141 if (validRegionList[regionIdx] == -1) 142 continue; 143 144 regId = (NvU32)validRegionList[regionIdx]; 145 pMap = pPma->pRegions[regId]; 146 addrBase = pPma->pRegDescriptors[regId]->base; 147 148 149 if ((status = pPma->pMapInfo->pmaMapScanContiguousNumaEviction(pMap, addrBase, actualSize, 150 pageSize, evictStart, evictEnd)) == NV_OK) 151 { 152 break; 153 } 154 } 155 156 return status; 157 } 158 159 /*! 160 * Check if the number of free frames is below the skip threshold percentage of total. 161 * @return NV_TRUE free frame count is below threshold. 162 * NV_FALSE otherwise. 163 */ 164 static NvBool _pmaCheckFreeFramesToSkipReclaim(PMA *pPma) 165 { 166 return (100 * pPma->pmaStats.numFreeFrames < 167 (pPma->pmaStats.num2mbPages * (_PMA_2MB >> PMA_PAGE_SHIFT) * pPma->numaReclaimSkipThreshold)); 168 } 169 170 /*! 171 * @brief Allocate contiguous memory for Numa 172 * 173 */ 174 NV_STATUS _pmaNumaAllocateRange 175 ( 176 PMA *pPma, 177 NvU32 numaNodeId, 178 NvLength actualSize, 179 NvU64 pageSize, 180 NvU64 *pPages, 181 NvBool bScrubOnAlloc, 182 NvBool allowEvict, 183 NvS32 *validRegionList, 184 NvU64 *allocatedCount 185 ) 186 { 187 NV_STATUS status = NV_ERR_NO_MEMORY; 188 NvU64 sysPhysAddr = 0, gpaPhysAddr = 0, evictStart = 0, evictEnd = 0; 189 NvU32 flags = OS_ALLOC_PAGES_NODE_NONE; 190 *allocatedCount = 0; 191 192 // check if numFreeFrames(64KB) are below a certain % of PMA managed memory(indicated by num2mbPages). 193 if (_pmaCheckFreeFramesToSkipReclaim(pPma)) 194 { 195 flags = OS_ALLOC_PAGES_NODE_SKIP_RECLAIM; 196 } 197 198 portSyncSpinlockRelease(pPma->pPmaLock); 199 200 // Try to allocate contiguous allocation of actualSize from OS. Do not force RECLAIM 201 status = osAllocPagesNode((int)numaNodeId, (NvLength)actualSize, flags, &sysPhysAddr); 202 203 if (status == NV_OK) 204 { 205 NvU32 j; 206 // j=0 head page is already refcounted at allocation 207 for (j = 1; j < (actualSize >> PMA_PAGE_SHIFT); j++) 208 { 209 osAllocAcquirePage(sysPhysAddr + (j << PMA_PAGE_SHIFT)); 210 } 211 212 gpaPhysAddr = sysPhysAddr - pPma->coherentCpuFbBase; 213 NV_ASSERT(gpaPhysAddr < pPma->coherentCpuFbBase); 214 *allocatedCount = 1; 215 216 if (bScrubOnAlloc) 217 { 218 PSCRUB_NODE pPmaScrubList = NULL; 219 NvU64 count; 220 221 if ((status = scrubSubmitPages(pPma->pScrubObj, (NvU32)actualSize, &gpaPhysAddr, 222 1, &pPmaScrubList, &count)) != NV_OK) 223 { 224 status = NV_ERR_INSUFFICIENT_RESOURCES; 225 goto scrub_exit; 226 } 227 228 if (count > 0) 229 _pmaClearScrubBit(pPma, pPmaScrubList, count); 230 231 if ((status = _pmaCheckScrubbedPages(pPma, actualSize, &gpaPhysAddr, 1)) != NV_OK) 232 { 233 status = NV_ERR_INSUFFICIENT_RESOURCES; 234 } 235 236 scrub_exit: 237 portMemFree(pPmaScrubList); 238 239 if (status == NV_ERR_INSUFFICIENT_RESOURCES) 240 { 241 NV_PRINTF(LEVEL_ERROR, "ERROR: scrubber OOM!\n"); 242 } 243 } 244 245 portSyncSpinlockAcquire(pPma->pPmaLock); 246 goto allocated; 247 } 248 249 portSyncSpinlockAcquire(pPma->pPmaLock); 250 251 NV_PRINTF(LEVEL_INFO, "Allocate from OS failed for allocation size = %lld!\n", 252 (NvU64) actualSize); 253 254 255 if (allowEvict) 256 { 257 // Check if UVM has evictable contiguous allocations of actualSize 258 status = _pmaNumaAvailableEvictableRange(pPma, validRegionList, 259 actualSize, pageSize, 260 &evictStart, &evictEnd); 261 } 262 263 if ((status == NV_OK) && (evictEnd - evictStart + 1) >= actualSize) 264 { 265 void *pMap = NULL; 266 NvU32 regId; 267 MEMORY_PROTECTION prot; 268 269 NV_ASSERT((evictEnd - evictStart + 1) == actualSize); 270 status = NV_ERR_NO_MEMORY; 271 regId = findRegionID(pPma, evictStart); 272 pMap = pPma->pRegions[regId]; 273 prot = pPma->pRegDescriptors[regId]->bProtected ? MEMORY_PROTECTION_PROTECTED : 274 MEMORY_PROTECTION_UNPROTECTED; 275 276 if (pMap != NULL) 277 { 278 // 279 // Call UVM to evict the contiguous allocation and evict the rest to OS 280 // UVM will call into PMA to free this contiguous range along with any excesses. 281 // PMA will release only the excess allocation to OS in the free routine. 282 // i.e., region evictStart to evictEnd is marked as 'ATTRIB_EVICTING' and will not 283 // be returned to OS. 284 // 285 status = _pmaEvictContiguous(pPma, pMap, evictStart, evictEnd, prot); 286 287 if (status == NV_ERR_NO_MEMORY) 288 { 289 NV_PRINTF(LEVEL_INFO, "Eviction Failed = %llx to %llx!\n", evictStart, evictEnd); 290 } 291 else 292 { 293 NV_PRINTF(LEVEL_INFO, "Eviction succeeded = %llx to %llx Scrub status 0x%x!\n", 294 evictStart, evictEnd, status); 295 gpaPhysAddr = evictStart; 296 *allocatedCount = 1; 297 } 298 } 299 else 300 { 301 NV_PRINTF(LEVEL_INFO, "pMap NULL cannot perform eviction\n"); 302 } 303 } 304 305 306 allocated: 307 308 // GPA needs to be acquired by shifting by the ATS aperture base address 309 pPages[0] = gpaPhysAddr; 310 311 return status; 312 } 313 314 /*! 315 * @brief Allocate discontiguous pages for Numa 316 * 317 */ 318 static NV_STATUS _pmaNumaAllocatePages 319 ( 320 PMA *pPma, 321 NvU32 numaNodeId, 322 NvU64 pageSize, 323 NvLength allocationCount, 324 NvU64 *pPages, 325 NvBool bScrubOnAlloc, 326 NvBool allowEvict, 327 NvS32 *validRegionList, 328 NvU64 *allocatedPages 329 ) 330 { 331 NV_STATUS status = NV_ERR_NO_MEMORY; 332 NvU64 sysPhysAddr; 333 NvU64 i = 0, j = 0; 334 NvU32 flags = OS_ALLOC_PAGES_NODE_NONE; 335 336 NV_ASSERT(allocationCount); 337 338 // check if numFreeFrames are below certain % of PMA managed memory. 339 if (_pmaCheckFreeFramesToSkipReclaim(pPma)) 340 { 341 flags = OS_ALLOC_PAGES_NODE_SKIP_RECLAIM; 342 } 343 344 portSyncSpinlockRelease(pPma->pPmaLock); 345 346 for (; i < allocationCount; i++) 347 { 348 status = osAllocPagesNode((int)numaNodeId, (NvLength) pageSize, flags, &sysPhysAddr); 349 if (status != NV_OK) 350 { 351 NV_PRINTF(LEVEL_INFO, "Alloc from OS failed for i= %lld allocationCount = %lld pageSize = %lld!\n", 352 i, (NvU64) allocationCount, (NvU64) pageSize); 353 break; 354 } 355 356 // GPA needs to be acquired by shifting by the ATS aperture base address 357 NV_ASSERT(sysPhysAddr >= pPma->coherentCpuFbBase); 358 pPages[i] = sysPhysAddr - pPma->coherentCpuFbBase; 359 360 // Skip the head page at offset 0 (j=0) as it is refcounted at allocation 361 for (j = 1; j < (pageSize >> PMA_PAGE_SHIFT); j++) 362 { 363 osAllocAcquirePage(sysPhysAddr + (j << PMA_PAGE_SHIFT)); 364 } 365 } 366 367 if (bScrubOnAlloc) 368 { 369 PSCRUB_NODE pPmaScrubList = NULL; 370 NvU64 count; 371 372 if ((status = scrubSubmitPages(pPma->pScrubObj, pageSize, pPages, 373 i, &pPmaScrubList, &count)) != NV_OK) 374 { 375 status = NV_ERR_INSUFFICIENT_RESOURCES; 376 goto scrub_exit; 377 } 378 379 if (count > 0) 380 _pmaClearScrubBit(pPma, pPmaScrubList, count); 381 382 if ((status = _pmaCheckScrubbedPages(pPma, pageSize, pPages, (NvU32)i)) != NV_OK) 383 { 384 status = NV_ERR_INSUFFICIENT_RESOURCES; 385 } 386 387 scrub_exit: 388 portMemFree(pPmaScrubList); 389 390 if (status == NV_ERR_INSUFFICIENT_RESOURCES) 391 { 392 NV_PRINTF(LEVEL_ERROR, "ERROR: scrubber OOM!\n"); 393 portSyncSpinlockAcquire(pPma->pPmaLock); 394 goto exit; 395 } 396 } 397 398 portSyncSpinlockAcquire(pPma->pPmaLock); 399 400 if (( i < allocationCount) && allowEvict) 401 { 402 NvU32 regionIdx; 403 404 // Check if there is atleast one evictable page 405 status = _pmaNumaAvailableEvictablePage(pPma, validRegionList); 406 407 if (status != NV_OK) 408 { 409 goto exit; 410 } 411 412 status = NV_ERR_NO_MEMORY; 413 414 for (regionIdx = 0; regionIdx < pPma->regSize; regionIdx++) 415 { 416 NvU32 regId; 417 NvU64 addrBase, addrLimit; 418 void *pMap = NULL; 419 MEMORY_PROTECTION prot; 420 421 if (validRegionList[regionIdx] == -1) 422 { 423 continue; 424 } 425 426 regId = (NvU32)validRegionList[regionIdx]; 427 pMap = pPma->pRegions[regId]; 428 429 addrBase = pPma->pRegDescriptors[regId]->base; 430 addrLimit = pPma->pRegDescriptors[regId]->limit; 431 prot = pPma->pRegDescriptors[regId]->bProtected ? MEMORY_PROTECTION_PROTECTED : 432 MEMORY_PROTECTION_UNPROTECTED; 433 434 status = _pmaEvictPages(pPma, pMap, 435 &pPages[i], (NvU32)(allocationCount - i), 436 &pPages[0], i, 437 pageSize, addrBase, addrLimit, prot); 438 439 if (status != NV_ERR_NO_MEMORY) 440 { 441 NV_PRINTF(LEVEL_INFO, "Frames %lld evicted in region %d of total allocationCount %lld Scrub status 0x%x!\n", 442 i, regionIdx, (NvU64) allocationCount, status); 443 // 444 // UVM can over evict, but will call into PMA only to evict the excess. 445 // free startAddr + actualSize, (uvmAllocatedSize - actualSize) to OS. 446 // Assume no under eviction. Overeviction is taken care of by the free routine. 447 // 448 i = allocationCount; 449 break; 450 } 451 452 NV_PRINTF(LEVEL_INFO, "Eviction Failed %d pages !\n", (NvU32) (allocationCount - i)); 453 } 454 455 } 456 457 exit: 458 *allocatedPages = i; 459 460 return status; 461 } 462 463 464 NV_STATUS pmaNumaAllocate 465 ( 466 PMA *pPma, 467 NvLength allocationCount, 468 NvU64 pageSize, 469 PMA_ALLOCATION_OPTIONS *allocationOptions, 470 NvU64 *pPages 471 ) 472 { 473 NvU32 i; 474 NV_STATUS status = NV_OK; 475 NvU32 numaNodeId = pPma->numaNodeId; 476 NvS32 regionList[PMA_REGION_SIZE]; 477 NvU32 flags = allocationOptions->flags; 478 NvLength allocSize = 0; 479 NvLength allocCount = 0; 480 NvU32 contigFlag = !!(flags & PMA_ALLOCATE_CONTIGUOUS); 481 // As per bug #2444368, kernel scrubbing is too slow. Use the GPU scrubber instead 482 NvBool bScrubOnAlloc = !(flags & PMA_ALLOCATE_NO_ZERO); 483 NvBool allowEvict = !(flags & PMA_ALLOCATE_DONT_EVICT); 484 NvBool partialFlag = !!(flags & PMA_ALLOCATE_ALLOW_PARTIAL); 485 NvBool bSkipScrubFlag = !!(flags & PMA_ALLOCATE_NO_ZERO); 486 487 NvU64 finalAllocatedCount = 0; 488 489 if (!pPma->bNuma) 490 { 491 NV_PRINTF(LEVEL_FATAL, "Cannot allocate from NUMA node %d on a non-NUMA system.\n", 492 numaNodeId); 493 return NV_ERR_INVALID_ARGUMENT; 494 } 495 496 if (pageSize > _PMA_512MB) 497 { 498 NV_PRINTF(LEVEL_FATAL, "Cannot allocate with more than 512MB contiguity.\n"); 499 return NV_ERR_INVALID_ARGUMENT; 500 } 501 502 if (pPma->nodeOnlined != NV_TRUE) 503 { 504 NV_PRINTF(LEVEL_INFO, "Cannot allocate from NUMA node %d before it is onlined.\n", 505 numaNodeId); 506 return NV_ERR_INVALID_STATE; 507 } 508 509 if (contigFlag) 510 { 511 if (((NvU64)allocationCount) * ((NvU64) pageSize) > NV_U32_MAX) 512 { 513 NV_PRINTF(LEVEL_FATAL, "Cannot allocate more than 4GB contiguous memory in one call.\n"); 514 return NV_ERR_INVALID_ARGUMENT; 515 } 516 } 517 518 // We are not changing the state. Can be outside the lock perhaps 519 NV_CHECK_OK_OR_RETURN(LEVEL_FATAL, pmaSelector(pPma, allocationOptions, regionList)); 520 521 // 522 // Scrub on free is enabled for this allocation request if the feature is enabled and the 523 // caller does not want to skip scrubber. 524 // Caller may want to skip scrubber when it knows the memory is zero'ed or when we are 525 // initializing RM structures needed by the scrubber itself. 526 // 527 if (pPma->bScrubOnFree && !bSkipScrubFlag) 528 { 529 portSyncMutexAcquire(pPma->pAllocLock); 530 portSyncRwLockAcquireRead(pPma->pScrubberValidLock); 531 532 if (pmaPortAtomicGet(&pPma->scrubberValid) != PMA_SCRUBBER_VALID) 533 { 534 NV_PRINTF(LEVEL_WARNING, "PMA object is not valid\n"); 535 portSyncRwLockReleaseRead(pPma->pScrubberValidLock); 536 portSyncMutexRelease(pPma->pAllocLock); 537 return NV_ERR_INVALID_STATE; 538 } 539 } 540 else 541 { 542 // 543 // Scrub-on-free feature is OFF, therefore we cannot do scrub-on-alloc 544 // either because it uses the same HW 545 // 546 bScrubOnAlloc = NV_FALSE; 547 } 548 549 // 550 // In the NUMA path, scrub on free does not provide enough safety guarantees 551 // because pages are released to the kernel and they can be reused by other 552 // processes. Therefore, we can only guarantee that the returned pages are 553 // zero if scrub on alloc is used. 554 // 555 allocationOptions->resultFlags = (bScrubOnAlloc)? PMA_ALLOCATE_RESULT_IS_ZERO : 0; 556 557 portSyncSpinlockAcquire(pPma->pPmaLock); 558 559 if (contigFlag) 560 { 561 allocCount = 1; 562 allocSize = allocationCount * pageSize; 563 status = _pmaNumaAllocateRange(pPma, numaNodeId, allocSize, pageSize, pPages, bScrubOnAlloc, allowEvict, regionList, &finalAllocatedCount); 564 } 565 else 566 { 567 allocCount = allocationCount; 568 allocSize = pageSize; 569 status = _pmaNumaAllocatePages(pPma, numaNodeId, (NvU32) allocSize, allocCount, pPages, bScrubOnAlloc, allowEvict, regionList, &finalAllocatedCount); 570 } 571 572 if ((status == NV_ERR_NO_MEMORY) && partialFlag && (finalAllocatedCount > 0)) 573 { 574 status = NV_OK; 575 } 576 577 if (status == NV_OK) 578 { 579 NvU32 regId; 580 void *pMap = NULL; 581 NvU64 regAddrBase; 582 NvU64 frameOffset; 583 NvU64 frameCount = 0; 584 PMA_PAGESTATUS curStatus = STATE_FREE; 585 PMA_PAGESTATUS allocOption = !!(flags & PMA_ALLOCATE_PINNED) ? 586 STATE_PIN : STATE_UNPIN; 587 588 NV_PRINTF(LEVEL_INFO, "SUCCESS allocCount %lld, allocsize %lld eviction? %s pinned ? %s contig? %s\n", 589 (NvU64) allocCount,(NvU64) allocSize, (flags & PMA_ALLOCATE_DONT_EVICT) ? "NOTALLOWED" : "ALLOWED", 590 !!(flags & PMA_ALLOCATE_PINNED) ? "PINNED" : "UNPINNED", contigFlag ? "CONTIG":"DISCONTIG"); 591 592 for (i = 0; i < finalAllocatedCount; i++) 593 { 594 NvU32 j; 595 596 regId = findRegionID(pPma, pPages[i]); 597 pMap = pPma->pRegions[regId]; 598 regAddrBase = pPma->pRegDescriptors[regId]->base; 599 frameCount = allocSize >> PMA_PAGE_SHIFT; 600 601 for (j = 0; j < frameCount; j++) 602 { 603 frameOffset = PMA_ADDR2FRAME(pPages[i], regAddrBase) + j; 604 605 curStatus = pPma->pMapInfo->pmaMapRead(pMap, frameOffset, NV_TRUE); 606 607 if (curStatus & ATTRIB_EVICTING) 608 { 609 status = NV_ERR_NO_MEMORY; 610 break; 611 } 612 pPma->pMapInfo->pmaMapChangeStateAttrib(pMap, frameOffset, allocOption, NV_TRUE); 613 } 614 if (status != NV_OK) 615 break; 616 } 617 618 if (status == NV_OK) 619 { 620 allocationOptions->numPagesAllocated = (NvLength)finalAllocatedCount; 621 } 622 } 623 624 625 if (status != NV_OK) 626 { 627 NV_PRINTF(LEVEL_INFO, "FAILED allocCount %lld, allocsize %lld eviction? %s pinned ? %s contig? %s\n", 628 (NvU64) allocCount, (NvU64) allocSize, (flags & PMA_ALLOCATE_DONT_EVICT) ? "NOTALLOWED" : "ALLOWED", 629 !!(flags & PMA_ALLOCATE_PINNED) ? "PINNED" : "UNPINNED", contigFlag ? "CONTIG":"DISCONTIG"); 630 // 631 // Free the entire allocation if scrubbing failed or if we had allocated evicting allocations. 632 // Evicting allocation will be handled in the pmaEvictContiguous 633 // 634 if (finalAllocatedCount > 0) 635 pmaNumaFreeInternal(pPma, pPages, finalAllocatedCount, pageSize, 0); 636 637 status = NV_ERR_NO_MEMORY; 638 } 639 640 portSyncSpinlockRelease(pPma->pPmaLock); 641 642 if (pPma->bScrubOnFree && !bSkipScrubFlag) 643 { 644 portSyncRwLockReleaseRead(pPma->pScrubberValidLock); 645 portSyncMutexRelease(pPma->pAllocLock); 646 } 647 648 return status; 649 } 650 651 void pmaNumaFreeInternal 652 ( 653 PMA *pPma, 654 NvU64 *pPages, 655 NvU64 pageCount, 656 NvU64 size, 657 NvU32 flag 658 ) 659 { 660 NvU64 i, j; 661 662 NV_PRINTF(LEVEL_INFO, "Freeing pPage[0] = %llx pageCount %lld\n", pPages[0], pageCount); 663 664 for (i = 0; i < pageCount; i++) 665 { 666 NvU32 regId; 667 NvU64 addrBase; 668 NvU64 sysPhysAddr = 0; 669 NvU64 frameNum; 670 NvU64 framesPerPage; 671 672 // Shift the GPA to acquire the bus address (SPA) 673 NV_ASSERT(pPages[i] < pPma->coherentCpuFbSize); 674 675 regId = findRegionID(pPma, pPages[i]); 676 addrBase = pPma->pRegDescriptors[regId]->base; 677 frameNum = PMA_ADDR2FRAME(pPages[i], addrBase); 678 framesPerPage = size >> PMA_PAGE_SHIFT; 679 sysPhysAddr = pPages[i] + pPma->coherentCpuFbBase; 680 681 for (j = 0; j < framesPerPage; j++) 682 { 683 PMA_PAGESTATUS newStatus = STATE_FREE; 684 PMA_PAGESTATUS currentStatus; 685 NvU64 sysPagePhysAddr = 0; 686 currentStatus = pPma->pMapInfo->pmaMapRead(pPma->pRegions[regId], (frameNum + j), NV_TRUE); 687 688 // 689 // When the pages are marked for evicting, we will skip free the page to OS 690 // in order to reuse the page. 691 // 692 if (currentStatus & ATTRIB_EVICTING) 693 { 694 // 695 // Evicting allocations are returned to new client and will be freed later. 696 // We set the ATTRIB_NUMA_REUSE bit here just in case eviction fails later and we 697 // need to release the page to OS in the allocation path. 698 // 699 if (currentStatus & STATE_UNPIN) 700 { 701 pPma->pMapInfo->pmaMapChangeStateAttribEx(pPma->pRegions[regId], (frameNum + j), 702 ATTRIB_NUMA_REUSE, ATTRIB_NUMA_REUSE); 703 } 704 continue; 705 } 706 sysPagePhysAddr = sysPhysAddr + (j << PMA_PAGE_SHIFT); 707 osAllocReleasePage(sysPagePhysAddr); 708 pPma->pMapInfo->pmaMapChangeStateAttribEx(pPma->pRegions[regId], (frameNum + j), newStatus, ~ATTRIB_EVICTING); 709 } 710 } 711 } 712 713 void pmaNumaSetReclaimSkipThreshold(PMA *pPma, NvU32 skipReclaimPercent) 714 { 715 portSyncSpinlockAcquire(pPma->pPmaLock); 716 pPma->numaReclaimSkipThreshold = skipReclaimPercent; 717 portSyncSpinlockRelease(pPma->pPmaLock); 718 } 719