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