1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2021-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 // FIXME XXX
25 #define NVOC_KERNEL_GRAPHICS_MANAGER_H_PRIVATE_ACCESS_ALLOWED
26 
27 #include "kernel/gpu/fifo/kernel_fifo.h"
28 #include "kernel/gpu/fifo/kernel_channel.h"
29 #include "kernel/gpu/fifo/kernel_channel_group.h"
30 #include "kernel/gpu/fifo/kernel_channel_group_api.h"
31 #include "kernel/gpu/fifo/kernel_sched_mgr.h"
32 #include "virtualization/kernel_vgpu_mgr.h"
33 #include "rmapi/rs_utils.h"
34 #include "rmapi/client.h"
35 #include "gpu/subdevice/subdevice.h"
36 
37 #include "kernel/core/locks.h"
38 #include "lib/base_utils.h"
39 
40 #include "gpu/mmu/kern_gmmu.h"
41 #include "vgpu/rpc.h"
42 #include "vgpu/vgpu_events.h"
43 #include "nvRmReg.h"
44 
45 #include "class/cl0080.h"
46 #include "class/cl2080.h"
47 #include "class/cl208f.h"
48 #include "class/clc572.h"
49 
50 #include "ctrl/ctrl0080/ctrl0080fifo.h"
51 
52 #define KFIFO_EHEAP_OWNER NvU32_BUILD('f','i','f','o')
53 
54 static EHeapOwnershipComparator _kfifoUserdOwnerComparator;
55 
56 static NV_STATUS _kfifoChidMgrAllocChidHeaps(OBJGPU     *pGpu,
57                                              KernelFifo *pKernelFifo,
58                                              CHID_MGR   *pChidMgr);
59 
60 static NV_STATUS _kfifoChidMgrAllocVChidHeapPointers(OBJGPU *pGpu, CHID_MGR *pChidMgr);
61 
62 static NV_STATUS _kfifoChidMgrInitChannelGroupMgr(OBJGPU *pGpu, CHID_MGR *pChidMgr);
63 
64 static void _kfifoChidMgrDestroyChidHeaps(CHID_MGR *pChidMgr);
65 
66 static void _kfifoChidMgrDestroyChannelGroupMgr(CHID_MGR *pChidMgr);
67 
68 static NV_STATUS _kfifoChidMgrFreeIsolationId(CHID_MGR *pChidMgr, NvU32 ChID);
69 
70 
71 NvU32 kfifoGetNumEschedDrivenEngines_IMPL
72 (
73     KernelFifo *pKernelFifo
74 )
75 {
76     const ENGINE_INFO *pEngineInfo;
77     NV_ASSERT(kfifoGetNumEngines_HAL(ENG_GET_GPU(pKernelFifo), pKernelFifo) >
78               0);
79     pEngineInfo = kfifoGetEngineInfo(pKernelFifo);
80     return pEngineInfo->numRunlists;
81 }
82 
83 
84 NV_STATUS
85 kfifoChidMgrConstruct_IMPL
86 (
87     OBJGPU      *pGpu,
88     KernelFifo  *pKernelFifo
89 )
90 {
91     NV_STATUS status = NV_OK;
92     NvU32     i;
93     NvU32     numEngines;
94 
95     //
96     // Allocate memory for the array of CHID_MGR pointers. Since this is an
97     // array, we allocate memory for pointers unto maxNumRunlists. We will only
98     // allocate objects for the valid ones.
99     //
100     if (kfifoIsPerRunlistChramEnabled(pKernelFifo))
101     {
102         //
103         // Construct the engine list if it isn't already constructed (internally
104         // checks if it was already constructed)
105         //
106         NV_ASSERT_OK_OR_RETURN(kfifoConstructEngineList_HAL(pGpu, pKernelFifo));
107         pKernelFifo->numChidMgrs = kfifoGetMaxNumRunlists_HAL(pGpu, pKernelFifo);
108     }
109     else
110         pKernelFifo->numChidMgrs = 1;
111 
112     if (pKernelFifo->numChidMgrs > MAX_NUM_RUNLISTS)
113     {
114         //
115         // This only currently defines the size of our bitvector
116         // pKernelFifo->chidMgrValid. Catch this case if HW expands beyond this so we
117         // can increase the size allocated to the bitvector
118         //
119         NV_PRINTF(LEVEL_ERROR, "numChidMgrs 0x%x exceeds MAX_NUM_RUNLISTS\n",
120             pKernelFifo->numChidMgrs);
121         DBG_BREAKPOINT();
122         return NV_ERR_BUFFER_TOO_SMALL;
123     }
124 
125     pKernelFifo->ppChidMgr = portMemAllocNonPaged(sizeof(CHID_MGR *) * pKernelFifo->numChidMgrs);
126     if (pKernelFifo->ppChidMgr == NULL)
127     {
128         status = NV_ERR_NO_MEMORY;
129         pKernelFifo->ppChidMgr = NULL;
130         NV_PRINTF(LEVEL_ERROR, "Failed to allocate pFifo->pChidMgr\n");
131         DBG_BREAKPOINT();
132         return status;
133     }
134     portMemSet(pKernelFifo->ppChidMgr, 0, sizeof(CHID_MGR *) * pKernelFifo->numChidMgrs);
135 
136     // Initialize the valid mask
137     if (kfifoIsPerRunlistChramEnabled(pKernelFifo))
138     {
139         numEngines = kfifoGetNumEngines_HAL(pGpu, pKernelFifo);
140         for (i = 0; i < numEngines; i++)
141         {
142             NvU32 runlistId;
143             status = kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo,
144                                               ENGINE_INFO_TYPE_INVALID, i,
145                                               ENGINE_INFO_TYPE_RUNLIST, &runlistId);
146             if (status == NV_OK)
147                 bitVectorSet(&pKernelFifo->chidMgrValid, runlistId);
148             else
149             {
150                 NV_PRINTF(LEVEL_ERROR, "Translation to runlistId failed for engine %d\n", i);
151                 DBG_BREAKPOINT();
152                 goto fail;
153             }
154         }
155     }
156     else
157     {
158         bitVectorSet(&pKernelFifo->chidMgrValid, 0); // We only have 1 chidmgr
159     }
160 
161     // Allocate memory for each CHID_MGR and its members (only the valid ones)
162     for (i = 0; i < pKernelFifo->numChidMgrs; i++)
163     {
164         if (!bitVectorTest(&pKernelFifo->chidMgrValid, i))
165             continue;
166 
167         pKernelFifo->ppChidMgr[i] = portMemAllocNonPaged(sizeof(CHID_MGR));
168         if (pKernelFifo->ppChidMgr[i] == NULL)
169         {
170             status = NV_ERR_NO_MEMORY;
171             NV_PRINTF(LEVEL_ERROR, "Failed to allocate pFifo->pChidMgr[%d]\n", i);
172             DBG_BREAKPOINT();
173             goto fail;
174         }
175         portMemSet(pKernelFifo->ppChidMgr[i], 0, sizeof(CHID_MGR));
176 
177         pKernelFifo->ppChidMgr[i]->runlistId = i;
178 
179         pKernelFifo->ppChidMgr[i]->pChanGrpTree = portMemAllocNonPaged(sizeof(KernelChannelGroupMap));
180         mapInitIntrusive(pKernelFifo->ppChidMgr[i]->pChanGrpTree);
181 
182         status = _kfifoChidMgrAllocChidHeaps(pGpu, pKernelFifo, pKernelFifo->ppChidMgr[i]);
183         if (status != NV_OK)
184         {
185             NV_PRINTF(LEVEL_ERROR, "Error allocating FifoDataHeap in "
186                 "pChidMgr. Status = %s (0x%x)\n",
187                 nvstatusToString(status), status);
188             DBG_BREAKPOINT();
189             goto fail;
190         }
191 
192         status = _kfifoChidMgrInitChannelGroupMgr(pGpu, pKernelFifo->ppChidMgr[i]);
193         if (status != NV_OK)
194             goto fail;
195     }
196 
197 
198     return status;
199 
200 fail:
201     kfifoChidMgrDestruct(pKernelFifo);
202     return status;
203 }
204 
205 void
206 kfifoChidMgrDestruct_IMPL
207 (
208     KernelFifo *pKernelFifo
209 )
210 {
211     NvU32 i;
212 
213     for (i = 0; i < pKernelFifo->numChidMgrs; i++)
214     {
215         if (pKernelFifo->ppChidMgr[i] != NULL)
216         {
217             mapDestroy(pKernelFifo->ppChidMgr[i]->pChanGrpTree);
218             portMemFree(pKernelFifo->ppChidMgr[i]->pChanGrpTree);
219             _kfifoChidMgrDestroyChidHeaps(pKernelFifo->ppChidMgr[i]);
220             _kfifoChidMgrDestroyChannelGroupMgr(pKernelFifo->ppChidMgr[i]);
221             portMemFree(pKernelFifo->ppChidMgr[i]);
222             pKernelFifo->ppChidMgr[i] = NULL;
223         }
224     }
225 
226     portMemFree(pKernelFifo->ppChidMgr);
227     pKernelFifo->ppChidMgr = NULL;
228     bitVectorClrAll(&pKernelFifo->chidMgrValid);
229     pKernelFifo->numChidMgrs = 0;
230 }
231 
232 /*
233  * @brief Allocate and initialize the virtual ChId heap pointers
234  */
235 static NV_STATUS
236 _kfifoChidMgrAllocVChidHeapPointers
237 (
238     OBJGPU     *pGpu,
239     CHID_MGR   *pChidMgr
240 )
241 {
242     NV_STATUS status = NV_OK;
243     NvU32 i;
244 
245     if (IS_VIRTUAL(pGpu))
246     {
247         return NV_OK;
248     }
249 
250     if (gpuIsSriovEnabled(pGpu))
251     {
252         //
253         // For Virtual Channel Heap
254         // Allocate Memory for Heap Object pointers
255         //
256         pChidMgr->ppVirtualChIDHeap = portMemAllocNonPaged(sizeof(OBJEHEAP *) * (VMMU_MAX_GFID));
257         if (pChidMgr->ppVirtualChIDHeap == NULL)
258         {
259             NV_PRINTF(LEVEL_ERROR,
260                       "Error allocating memory for virtual channel heap pointers\n");
261             return NV_ERR_NO_MEMORY;
262         }
263 
264         // initialize
265         for (i = 0; i < VMMU_MAX_GFID; i++)
266         {
267             pChidMgr->ppVirtualChIDHeap[i] = NULL;
268         }
269     }
270     return status;
271 }
272 
273 
274 /*
275  * @brief Allocates & initializes ChID heaps
276  */
277 static NV_STATUS
278 _kfifoChidMgrAllocChidHeaps
279 (
280     OBJGPU      *pGpu,
281     KernelFifo  *pKernelFifo,
282     CHID_MGR    *pChidMgr
283 )
284 {
285     NV_STATUS status = NV_OK;
286 
287     if (pChidMgr->numChannels == 0)
288     {
289         if (kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr) == 0)
290         {
291             NV_PRINTF(LEVEL_ERROR, "pChidMgr->numChannels is 0\n");
292             DBG_BREAKPOINT();
293             return NV_ERR_INVALID_STATE;
294         }
295     }
296 
297     pChidMgr->pFifoDataHeap = portMemAllocNonPaged(sizeof(*pChidMgr->pFifoDataHeap));
298     if (pChidMgr->pFifoDataHeap == NULL)
299     {
300         status = NV_ERR_NO_MEMORY;
301         NV_PRINTF(LEVEL_ERROR,
302                   "Error in Allocating memory for pFifoDataHeap! Status = %s (0x%x)\n",
303                   nvstatusToString(status), status);
304         return status;
305     }
306     constructObjEHeap(pChidMgr->pFifoDataHeap, 0, pChidMgr->numChannels,
307                       sizeof(KernelChannel *), 0);
308 
309     if (kfifoIsChidHeapEnabled(pKernelFifo))
310     {
311         NvU32 userdBar1Size;
312         NvU32 numChannels         = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
313         NvU32 subProcessIsolation = 1;
314 
315         pChidMgr->pGlobalChIDHeap = portMemAllocNonPaged(sizeof(OBJEHEAP));
316         if (pChidMgr->pGlobalChIDHeap == NULL)
317         {
318             NV_PRINTF(LEVEL_ERROR,
319                       "Error in Allocating memory for global ChID heap!\n");
320             return NV_ERR_NO_MEMORY;
321         }
322         constructObjEHeap(pChidMgr->pGlobalChIDHeap, 0, numChannels,
323                           sizeof(PFIFO_ISOLATIONID), 0);
324 
325         //
326         // Enable USERD allocation isolation. USERD allocated by different clients
327         // should not be in the same page
328         //
329         kfifoGetUserdSizeAlign_HAL(pKernelFifo, &userdBar1Size, NULL);
330         NvBool bIsolationEnabled = (pKernelFifo->bUsePerRunlistChram && pKernelFifo->bDisableChidIsolation) ? NV_FALSE : NV_TRUE;
331         pChidMgr->pGlobalChIDHeap->eheapSetOwnerIsolation(pChidMgr->pGlobalChIDHeap,
332                                                           bIsolationEnabled,
333                                                           RM_PAGE_SIZE / userdBar1Size);
334 
335         // Disable USERD allocation isolation for guest if disabled from vmioplugin
336         {
337             // In this case subProcessIsolation is always 0
338             if (IS_GSP_CLIENT(pGpu))
339             {
340                 subProcessIsolation = 0;
341             }
342         }
343         if (!subProcessIsolation)
344         {
345             pChidMgr->pGlobalChIDHeap->eheapSetOwnerIsolation(
346                                             pChidMgr->pGlobalChIDHeap,
347                                             NV_FALSE,
348                                             RM_PAGE_SIZE / userdBar1Size);
349     #if (defined(_WIN32) || defined(_WIN64) || defined(NV_UNIX)) && !RMCFG_FEATURE_MODS_FEATURES
350             NV_PRINTF(LEVEL_INFO,
351                       "Sub Process channel isolation disabled by vGPU plugin\n");
352     #endif
353         }
354 
355         status = _kfifoChidMgrAllocVChidHeapPointers(pGpu, pChidMgr);
356     }
357 
358     return status;
359 }
360 
361 static void
362 _kfifoChidMgrDestroyChidHeaps
363 (
364     CHID_MGR     *pChidMgr
365 )
366 {
367     if (pChidMgr->pFifoDataHeap != NULL)
368     {
369         pChidMgr->pFifoDataHeap->eheapDestruct(pChidMgr->pFifoDataHeap);
370         portMemFree(pChidMgr->pFifoDataHeap);
371         pChidMgr->pFifoDataHeap = NULL;
372     }
373     if (pChidMgr->pGlobalChIDHeap != NULL)
374     {
375         pChidMgr->pGlobalChIDHeap->eheapDestruct(pChidMgr->pGlobalChIDHeap);
376         portMemFree(pChidMgr->pGlobalChIDHeap);
377         pChidMgr->pGlobalChIDHeap = NULL;
378     }
379 
380     portMemFree(pChidMgr->ppVirtualChIDHeap);
381     pChidMgr->ppVirtualChIDHeap = NULL;
382 }
383 
384 
385 static NV_STATUS
386 _kfifoChidMgrInitChannelGroupMgr
387 (
388     OBJGPU     *pGpu,
389     CHID_MGR   *pChidMgr
390 )
391 {
392     KernelFifo *pKernelFifo      = GPU_GET_KERNEL_FIFO(pGpu);
393     FIFO_HW_ID *pFifoHwID        = &pChidMgr->channelGrpMgr;
394     NvU32       allocSize;
395     NvU32       numChannelGroups = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
396 
397     if (numChannelGroups == 0)
398     {
399         return NV_OK;
400     }
401 
402     // Rounds up to dword alignemnt, then converts bits to bytes.
403     allocSize = RM_ALIGN_UP(numChannelGroups, 32)/8;
404 
405     pFifoHwID->pHwIdInUse = portMemAllocNonPaged(allocSize);
406     if (pFifoHwID->pHwIdInUse == NULL)
407         return NV_ERR_NO_MEMORY;
408 
409     // bytes to NvU32[] elements
410     pFifoHwID->hwIdInUseSz = allocSize/4;
411 
412     portMemSet(pFifoHwID->pHwIdInUse, 0, allocSize);
413 
414     //
415     // If numChannelGroups isn't a multiple of 32 we need to set the bits > numChannelGroups to
416     // 1.  Otherwise when we allocate IDs starting at the top we'll allocate
417     // ids >numChannelGroups.
418     //
419     if (numChannelGroups % 32 != 0)
420     {
421         pFifoHwID->pHwIdInUse[numChannelGroups/32] |= ~ ((1<<(numChannelGroups%32))-1);
422     }
423 
424     return NV_OK;
425 }
426 
427 static void
428 _kfifoChidMgrDestroyChannelGroupMgr
429 (
430     CHID_MGR *pChidMgr
431 )
432 {
433     if (pChidMgr->channelGrpMgr.pHwIdInUse)
434     {
435         portMemFree(pChidMgr->channelGrpMgr.pHwIdInUse);
436         pChidMgr->channelGrpMgr.pHwIdInUse = NULL;
437         pChidMgr->channelGrpMgr.hwIdInUseSz = 0;
438     }
439 }
440 
441 static NV_STATUS
442 _kfifoChidMgrFreeIsolationId
443 (
444     CHID_MGR   *pChidMgr,
445     NvU32       ChID
446 )
447 {
448     EMEMBLOCK  *pIsolationIdBlock = pChidMgr->pGlobalChIDHeap->eheapGetBlock(
449         pChidMgr->pGlobalChIDHeap,
450         ChID,
451         NV_FALSE);
452 
453     NV_ASSERT_OR_RETURN(pIsolationIdBlock, NV_ERR_OBJECT_NOT_FOUND);
454     NV_ASSERT(pIsolationIdBlock->refCount > 0);
455     NV_ASSERT(pIsolationIdBlock->pData != NULL);
456     portMemFree(pIsolationIdBlock->pData);
457 
458     pIsolationIdBlock->pData = NULL;
459 
460     return NV_OK;
461 }
462 
463 /*!
464  * @breif Fifo defined call back comparator to compare eheap block ownership ID
465  *
466  * @param[in]  pRequesterID  Ownership ID constructed by caller
467  * @param[in]  pIsolationID
468  *
469  * @return NV_TRUE if two ownership IDs belong to the same owner
470  */
471 static NvBool
472 _kfifoUserdOwnerComparator
473 (
474     void *pRequesterID,
475     void *pIsolationID
476 )
477 {
478     PFIFO_ISOLATIONID pAllocID = (PFIFO_ISOLATIONID)pRequesterID;
479     PFIFO_ISOLATIONID pBlockID = (PFIFO_ISOLATIONID)pIsolationID;
480 
481     //
482     // The block's data will be NULL if the channel has been destroyed but there
483     // is still a refcount on the channel ID. In that case no work can be issued
484     // to that channel ID now or in the future, so we can act as though the
485     // channel does not exist.
486     //
487     if (!pBlockID)
488         return NV_TRUE;
489 
490     if ((pAllocID->domain       != pBlockID->domain)    ||
491         (pAllocID->processID    != pBlockID->processID) ||
492         (pAllocID->subProcessID != pBlockID->subProcessID))
493     {
494         return NV_FALSE;
495     }
496     else
497     {
498         return NV_TRUE;
499     }
500 }
501 
502 /*
503  * @brief Returns the number of vGPU plugin channels.
504  *
505  * Depending on whether this code is executed on the CPU RM or the Physical RM,
506  * different structures are used to retrieve the number.
507  * On the CPU RM or the monolithic RM, KERNEL_HOST_VGPU_DEVICE::numPluginChannels is used,
508  * whereas on the physical RM it's HOST_VGPU_DEVICE::numPluginChannels.
509  */
510 static NV_STATUS
511 _kfifoGetVgpuPluginChannelsCount
512 (
513     OBJGPU *pGpu,
514     NvU32 *pNumPluginChannels
515 )
516 {
517     NV_ASSERT_OR_RETURN(pNumPluginChannels != NULL, NV_ERR_INVALID_ARGUMENT);
518 
519     if (!RMCFG_FEATURE_PLATFORM_GSP)
520     {
521         KERNEL_HOST_VGPU_DEVICE *pKernelHostVgpuDevice = NULL;
522 
523         NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextKernelHostVgpuDevice(pGpu, &pKernelHostVgpuDevice));
524         NV_ASSERT_OR_RETURN(pKernelHostVgpuDevice != NULL, NV_ERR_OBJECT_NOT_FOUND);
525 
526         *pNumPluginChannels = pKernelHostVgpuDevice->numPluginChannels;
527     }
528 
529     return NV_OK;
530 }
531 
532 /*!
533  * @brief Allocates one Channel ID on heap
534  *
535  * @param[in] OBJGPU     GPU Object
536  * @param[in] KernelFifo KernelFifo Object
537  * @param[in] CHID_MGR   Channel ID manager
538  * @param[in] chFlag NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_DOWN
539  *                   NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE
540  *                   NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_UP
541  * @param[in] bForceInternalIdx true if requesting specific index within USERD
542  *                              page
543  * @param[in] internalIdx requested index within USERD page when
544  *                        bForceInternalIdx true
545  * @param[in] ChID ChID to assign in case of ADDRESS_ALLOCATE
546  * @param[in,out] pKernelChannel The previously allocated KernelChannel structure
547  *
548  * @return NV_OK if allocation is successful
549  *         NV_ERR_NO_FREE_FIFOS: allocated channel ID exceeds MAX channels.
550  */
551 NV_STATUS
552 kfifoChidMgrAllocChid_IMPL
553 (
554     OBJGPU                  *pGpu,
555     KernelFifo              *pKernelFifo,
556     CHID_MGR                *pChidMgr,
557     NvHandle                 hClient,
558     CHANNEL_HW_ID_ALLOC_MODE chIdFlag,
559     NvBool                   bForceInternalIdx,
560     NvU32                    internalIdx,
561     NvBool                   bForceUserdPage,
562     NvU32                    userdPageIdx,
563     NvU32                    ChID,
564     KernelChannel           *pKernelChannel
565 )
566 {
567     NvU64             chSize;
568     NvU32             chFlag                = chIdFlag;
569     NvU64             ChID64                = 0;
570     NvU64             subProcessID          = 0;
571     NvU64             processID             = 0;
572     NvBool            bIsSubProcessDisabled = NV_FALSE;
573     RmClient         *pClient;
574     NvU32             offsetAlign = 1;
575     NvU32             gfid;
576     PFIFO_ISOLATIONID pIsolationID = NULL;
577     NV_STATUS         status;
578     NvU32             numChannels;
579 
580     NV_ASSERT_OR_RETURN(pKernelChannel != NULL, NV_ERR_INVALID_ARGUMENT);
581 
582     numChannels = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
583 
584     switch (chIdFlag)
585     {
586         case CHANNEL_HW_ID_ALLOC_MODE_GROW_DOWN:
587             chFlag = NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_DOWN;
588             break;
589         case CHANNEL_HW_ID_ALLOC_MODE_GROW_UP:
590             chFlag = NVOS32_ALLOC_FLAGS_FORCE_MEM_GROWS_UP;
591             break;
592         case CHANNEL_HW_ID_ALLOC_MODE_PROVIDED:
593             ChID64 = ChID;
594             chFlag = NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE;
595             break;
596         default:
597             NV_PRINTF(LEVEL_ERROR, "Invalid channel ID alloc mode %d\n", chFlag);
598             DBG_BREAKPOINT();
599             return NV_ERR_INVALID_ARGUMENT;
600     }
601 
602     // we are allocating only one Channel at a time
603     chSize = 1;
604 
605     // Create unique isolation ID for each process
606     pClient = serverutilGetClientUnderLock(hClient);
607     if (pClient == NULL)
608     {
609         NV_PRINTF(LEVEL_ERROR, "Invalid client handle %ux\n", hClient);
610         DBG_BREAKPOINT();
611         return NV_ERR_INVALID_CLIENT;
612     }
613 
614     NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextGfid(pGpu, &gfid));
615 
616     // SRIOV: In guest plugin context allocate the chid.
617     //        In guest RM context allocate the same chid as guest
618     if (IS_GFID_VF(gfid))
619     {
620         NvU32             numPluginChannels;
621         NvU64             rangeLo, rangeHi, base, size;
622 
623         NV_ASSERT_OR_RETURN(pChidMgr->ppVirtualChIDHeap[gfid],
624                             NV_ERR_INVALID_STATE);
625 
626         NV_ASSERT_OK_OR_RETURN(_kfifoGetVgpuPluginChannelsCount(pGpu, &numPluginChannels));
627 
628         pChidMgr->ppVirtualChIDHeap[gfid]->eheapGetBase(
629             pChidMgr->ppVirtualChIDHeap[gfid],
630             &base);
631         pChidMgr->ppVirtualChIDHeap[gfid]->eheapGetSize(
632             pChidMgr->ppVirtualChIDHeap[gfid],
633             &size);
634 
635         rangeLo = base;
636         rangeHi = base + size - 1;
637 
638         // Route plugin channels to be allocated at the top
639         NV_ASSERT_OR_RETURN(numPluginChannels < size,
640                             NV_ERR_INVALID_PARAMETER);
641         if (pKernelChannel->pKernelChannelGroupApi->pKernelChannelGroup->bIsCallingContextVgpuPlugin &&
642             numPluginChannels > 0)
643         {
644             rangeLo = rangeHi - numPluginChannels + 1;
645         }
646         else
647         {
648             rangeHi = rangeHi - numPluginChannels;
649         }
650 
651         status = pChidMgr->ppVirtualChIDHeap[gfid]->eheapSetAllocRange(
652                      pChidMgr->ppVirtualChIDHeap[gfid],
653                      rangeLo,
654                      rangeHi);
655 
656         NV_ASSERT_OK_OR_RETURN(status);
657 
658         if (bForceUserdPage)
659         {
660             NV_ASSERT_OR_RETURN(!bForceInternalIdx, NV_ERR_INVALID_STATE);
661             ChID64 = ((NvU64)userdPageIdx) *
662                          pChidMgr->ppVirtualChIDHeap[gfid]->ownerGranularity +
663                      internalIdx;
664             chFlag |= NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE;
665         }
666         else if (bForceInternalIdx)
667         {
668             chFlag |= NVOS32_ALLOC_FLAGS_FORCE_INTERNAL_INDEX;
669             offsetAlign = internalIdx;
670         }
671 
672         if (chFlag & NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE)
673             NV_ASSERT_OR_RETURN((ChID64 <= rangeHi) && (ChID64 >= rangeLo),
674                                 NV_ERR_INVALID_PARAMETER);
675 
676         // We'll allocate from the VirtualChIdHeap for the guest
677         status = pChidMgr->ppVirtualChIDHeap[gfid]->eheapAlloc(
678             pChidMgr->ppVirtualChIDHeap[gfid], // This Heap
679             KFIFO_EHEAP_OWNER,                  // owner
680             &chFlag,                           // Alloc Flags
681             &ChID64,                           // Alloc Offset
682             &chSize,                           // Size
683             offsetAlign,                       // offsetAlign
684             1,                                 // sizeAlign
685             NULL,                              // Allocated mem block
686             NULL,                              // Isolation ID
687             NULL // Fifo defined ownership comparator
688         );
689         if (status != NV_OK)
690         {
691             NV_PRINTF(LEVEL_ERROR,
692                       "Failed to allocate Channel ID 0x%llx %d on heap \n",
693                       ChID64,
694                       chIdFlag);
695             DBG_BREAKPOINT();
696             goto fail;
697         }
698     }
699     else
700     {
701         //
702         // Legacy / SRIOV vGPU Host, SRIOV guest, baremetal CPU RM, GSP FW, GSP
703         // client allocate from global heap
704         //
705         pIsolationID = portMemAllocNonPaged(sizeof(FIFO_ISOLATIONID));
706         NV_ASSERT_OR_RETURN((pIsolationID != NULL), NV_ERR_NO_MEMORY);
707         portMemSet(pIsolationID, 0, sizeof(FIFO_ISOLATIONID));
708 
709         //
710         // Check if the allocation request is from the guest RM or host RM
711         //
712         processID             = pClient->ProcID;
713         subProcessID          = pClient->SubProcessID;
714         bIsSubProcessDisabled = pClient->bIsSubProcessDisabled;
715 
716         if (RMCFG_FEATURE_PLATFORM_GSP || kchannelCheckIsKernel(pKernelChannel))
717         {
718             //
719             // If not GSPFW: Allocation request is from host RM kernel
720             // If GSPFW: ChID has already been chosen by CPU-RM, but pClient
721             //   doesn't have the true processID, so just allow the whole pool.
722             //
723             pIsolationID->domain = HOST_KERNEL;
724             processID            = KERNEL_PID;
725         }
726         else
727         {
728             if (0x0 != subProcessID)
729             {
730                 //
731                 // Allocation request is from the guest RM
732                 //
733                 if (KERNEL_PID == subProcessID)
734                 {
735                     pIsolationID->domain = GUEST_KERNEL;
736                 }
737                 else
738                 {
739                     pIsolationID->domain = GUEST_USER;
740                 }
741             }
742             else
743             {
744                 pIsolationID->domain = HOST_USER;
745             }
746         }
747 
748         pIsolationID->processID    = processID;
749         pIsolationID->subProcessID = subProcessID;
750 
751         //
752         // Overwrite isolation ID if guest USERD isolation is disabled
753         //
754         if ((subProcessID != 0x0) && (bIsSubProcessDisabled))
755         {
756             pIsolationID->domain       = GUEST_INSECURE;
757             pIsolationID->subProcessID = KERNEL_PID;
758         }
759 
760         /* Channel USERD manipuliation only supported without GFID */
761         if (bForceUserdPage)
762         {
763             NV_ASSERT_OR_RETURN(!bForceInternalIdx, NV_ERR_INVALID_STATE);
764             ChID64 = ((NvU64)userdPageIdx) *
765                          pChidMgr->pGlobalChIDHeap->ownerGranularity +
766                      internalIdx;
767             chFlag |= NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE;
768         }
769         else if (bForceInternalIdx)
770         {
771             chFlag |= NVOS32_ALLOC_FLAGS_FORCE_INTERNAL_INDEX;
772             offsetAlign = internalIdx;
773         }
774 
775         status = pChidMgr->pGlobalChIDHeap->eheapAlloc(
776             pChidMgr->pGlobalChIDHeap, // This Heap
777             KFIFO_EHEAP_OWNER,          // owner
778             &chFlag,                   // Alloc Flags
779             &ChID64,                   // Alloc Offset
780             &chSize,                   // Size
781             offsetAlign,               // offsetAlign
782             1,                         // sizeAlign
783             NULL,                      // Allocated mem block
784             pIsolationID,              // Isolation ID
785             _kfifoUserdOwnerComparator  // Fifo defined ownership comparator
786         );
787 
788         if (status != NV_OK)
789         {
790             NV_PRINTF(LEVEL_ERROR, "Failed to allocate Channel ID on heap\n");
791             DBG_BREAKPOINT();
792             goto fail;
793         }
794     }
795 
796     //
797     // Now allocate at a fixed offset from the pFifoDataHeap once the previous
798     // ID allocation told us which ID to use.
799     //
800     chFlag = NVOS32_ALLOC_FLAGS_FIXED_ADDRESS_ALLOCATE;
801     status = pChidMgr->pFifoDataHeap->eheapAlloc(
802         pChidMgr->pFifoDataHeap, // This Heap
803         KFIFO_EHEAP_OWNER,        // owner
804         &chFlag,                 // Alloc Flags
805         &ChID64,                 // Alloc Offset
806         &chSize,                 // Size
807         1,                       // offsetAlign
808         1,                       // sizeAlign
809         NULL,                    // Allocated mem block
810         NULL,                    // Isolation ID
811         NULL                     // ownership comparator
812     );
813 
814     if (status != NV_OK)
815     {
816         //
817         // Should never happen since we're mirroring the global chid heap, or
818         // pre-reserving space on the global chid heap for SR-IOV capable
819         // systems.
820         //
821         NV_PRINTF(LEVEL_ERROR, "Failed to allocate Channel on fifo data heap\n");
822         goto fail;
823     }
824 
825     ChID = NvU64_LO32(ChID64);
826 
827     if (ChID < numChannels)
828     {
829         PEMEMBLOCK pFifoDataBlock = pChidMgr->pFifoDataHeap->eheapGetBlock(
830             pChidMgr->pFifoDataHeap,
831             ChID,
832             NV_FALSE);
833         PEMEMBLOCK pIsolationIdBlock = pChidMgr->pGlobalChIDHeap->eheapGetBlock(
834             pChidMgr->pGlobalChIDHeap,
835             ChID,
836             NV_FALSE);
837 
838         if (IS_GFID_PF(gfid))
839             pIsolationIdBlock->pData = pIsolationID;
840 
841         pFifoDataBlock->pData = pKernelChannel;
842         pKernelChannel->ChID  = ChID;
843     }
844     else
845     {
846         NV_PRINTF(LEVEL_WARNING, "No allocatable FIFO available.\n");
847         status = NV_ERR_NO_FREE_FIFOS;
848         goto fail;
849     }
850     return NV_OK;
851 
852 fail:
853     // We already know that pIsolationID is non-NULL here.
854     portMemFree(pIsolationID);
855     return status;
856 }
857 
858 /*
859  * Retain a channel ID which has already been allocated by
860  * kfifoChidMgrAllocChid. Until released, the HW channel ID will not be
861  * allocated by any new channels even after kfifoChidMgrFreeChid has been
862  * called.
863  */
864 NV_STATUS
865 kfifoChidMgrRetainChid_IMPL
866 (
867     OBJGPU     *pGpu,
868     KernelFifo *pKernelFifo,
869     CHID_MGR   *pChidMgr,
870     NvU32       ChID
871 )
872 {
873     NvU32       gfid;
874     PEMEMBLOCK  pFifoDataBlock = NULL;
875 
876     NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextGfid(pGpu, &gfid));
877 
878     if (IS_GFID_VF(gfid))
879     {
880         NV_ASSERT_OR_RETURN(pChidMgr->ppVirtualChIDHeap[gfid] != NULL,
881                             NV_ERR_INVALID_STATE);
882         PEMEMBLOCK  pVirtChIdBlock = pChidMgr->ppVirtualChIDHeap[gfid]->eheapGetBlock(
883             pChidMgr->ppVirtualChIDHeap[gfid],
884             ChID,
885             NV_FALSE);
886         NV_ASSERT_OR_RETURN(pVirtChIdBlock != NULL, NV_ERR_OBJECT_NOT_FOUND);
887         NV_ASSERT(pVirtChIdBlock->refCount > 0);
888         ++pVirtChIdBlock->refCount;
889     }
890     else
891     {
892         NV_ASSERT_OR_RETURN(pChidMgr->pGlobalChIDHeap != NULL, NV_ERR_INVALID_STATE);
893         PEMEMBLOCK  pChIdBlock = pChidMgr->pGlobalChIDHeap->eheapGetBlock(
894             pChidMgr->pGlobalChIDHeap,
895             ChID,
896             NV_FALSE);
897         NV_ASSERT_OR_RETURN(pChIdBlock != NULL, NV_ERR_OBJECT_NOT_FOUND);
898         NV_ASSERT(pChIdBlock->refCount > 0);
899         ++pChIdBlock->refCount;
900     }
901 
902     NV_ASSERT_OR_RETURN(pChidMgr->pFifoDataHeap != NULL, NV_ERR_INVALID_STATE);
903     pFifoDataBlock = pChidMgr->pFifoDataHeap->eheapGetBlock(
904         pChidMgr->pFifoDataHeap,
905         ChID,
906         NV_FALSE);
907     NV_ASSERT_OR_RETURN(pFifoDataBlock != NULL, NV_ERR_OBJECT_NOT_FOUND);
908     NV_ASSERT(pFifoDataBlock->refCount > 0);
909     ++pFifoDataBlock->refCount;
910 
911     return NV_OK;
912 }
913 
914 /*
915  * Drop the refcount on the given channel (ID), removing it from pFifo's heap if
916  * its refcount reaches 0.
917  */
918 NV_STATUS
919 kfifoChidMgrReleaseChid_IMPL
920 (
921     OBJGPU     *pGpu,
922     KernelFifo *pKernelFifo,
923     CHID_MGR   *pChidMgr,
924     NvU32       ChID
925 )
926 {
927     NvU32 gfid;
928 
929     NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextGfid(pGpu, &gfid));
930 
931     if (IS_GFID_VF(gfid))
932     {
933         NV_ASSERT_OR_RETURN(pChidMgr->ppVirtualChIDHeap[gfid] != NULL, NV_ERR_INVALID_STATE);
934         NV_ASSERT_OK(pChidMgr->ppVirtualChIDHeap[gfid]->eheapFree(pChidMgr->ppVirtualChIDHeap[gfid], ChID));
935     }
936     else
937     {
938         NV_ASSERT_OR_RETURN(pChidMgr->pGlobalChIDHeap != NULL, NV_ERR_INVALID_STATE);
939         NV_ASSERT_OK(pChidMgr->pGlobalChIDHeap->eheapFree(pChidMgr->pGlobalChIDHeap, ChID));
940     }
941 
942     NV_ASSERT_OR_RETURN(pChidMgr->pFifoDataHeap != NULL, NV_ERR_INVALID_STATE);
943     NV_ASSERT_OK_OR_RETURN(pChidMgr->pFifoDataHeap->eheapFree(pChidMgr->pFifoDataHeap, ChID));
944 
945     return NV_OK;
946 }
947 
948 /*
949  * Removes the association between pKernelChannel and its channel ID. Note that this
950  * will not remove the channel ID itself from pFifo's heap if
951  * fifoHeapRetainChannelId has been called.
952  */
953 NV_STATUS
954 kfifoChidMgrFreeChid_IMPL
955 (
956     OBJGPU       *pGpu,
957     KernelFifo   *pKernelFifo,
958     CHID_MGR     *pChidMgr,
959     NvU32         ChID
960 )
961 {
962     EMEMBLOCK *pFifoDataBlock;
963     NV_STATUS  status;
964     NvU32 gfid;
965 
966     //
967     // This channel is going away, so clear its pointer from the channel ID's heap
968     // block.
969     //
970     pFifoDataBlock = pChidMgr->pFifoDataHeap->eheapGetBlock(
971         pChidMgr->pFifoDataHeap,
972         ChID,
973         NV_FALSE);
974     NV_ASSERT_OR_RETURN(pFifoDataBlock != NULL, NV_ERR_OBJECT_NOT_FOUND);
975     NV_ASSERT(pFifoDataBlock->refCount > 0);
976     pFifoDataBlock->pData = NULL;
977 
978     NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextGfid(pGpu, &gfid));
979 
980     if (IS_GFID_PF(gfid))
981     {
982         //
983         // This marks the channel ID as orphaned and causes it to be ignored for
984         // isolation purposes. This only matters if there will still be a reference
985         // on the ID after we release ours below.
986         //
987         status = _kfifoChidMgrFreeIsolationId(pChidMgr, ChID);
988         if(status != NV_OK)
989         {
990             NV_PRINTF(LEVEL_ERROR,
991                 "Failed to free IsolationId. Status = 0x%x\n", status);
992             DBG_BREAKPOINT();
993             return status;
994         }
995     }
996 
997     return kfifoChidMgrReleaseChid(pGpu, pKernelFifo, pChidMgr, ChID);
998 }
999 
1000 /**
1001  * @brief Reserve a contiguous set of SCHIDs from the end of our CHID heap for
1002  * the given GFID
1003  *
1004 
1005  * @param[in] pChidMgr         CHID_MGR pointer
1006  * @param[in] numChannels      Number of SCHIDs to reserve
1007  * @param[in] pHostVgpuDevice  HOST_VGPU_DEVICE
1008  *
1009  * @return NV_OK if success
1010  */
1011 NV_STATUS
1012 kfifoChidMgrReserveSystemChids_IMPL
1013 (
1014     OBJGPU           *pGpu,
1015     KernelFifo       *pKernelFifo,
1016     CHID_MGR         *pChidMgr,
1017     NvU32             numChannels,
1018     NvU32             flags,
1019     NvU32             gfid,
1020     NvU32            *pChidOffset,
1021     NvU32            *pChannelCount,
1022     NvHandle          hMigClient,
1023     NvU32             engineFifoListNumEntries,
1024     FIFO_ENGINE_LIST *pEngineFifoList
1025 )
1026 {
1027     NV_STATUS         status              = NV_OK;
1028     NvU64             chSize;
1029     NvU64             offset              = 0;
1030     PFIFO_ISOLATIONID pIsolationID        = NULL;
1031     PEMEMBLOCK        pIsolationIdBlock;
1032     NvU32             userdBar1Size;
1033 
1034     if (IS_VIRTUAL(pGpu))
1035     {
1036         // Not supported on guest or when SRIOV is disabled
1037         return NV_ERR_NOT_SUPPORTED;
1038     }
1039 
1040     pIsolationID = portMemAllocNonPaged(sizeof(FIFO_ISOLATIONID));
1041     NV_ASSERT_OR_RETURN((pIsolationID != NULL), NV_ERR_NO_MEMORY);
1042     portMemSet(pIsolationID, 0, sizeof(FIFO_ISOLATIONID));
1043 
1044     chSize = numChannels;
1045 
1046     status = pChidMgr->pGlobalChIDHeap->eheapAlloc(
1047         pChidMgr->pGlobalChIDHeap,       // This Heap
1048         KFIFO_EHEAP_OWNER,               // owner
1049         &flags,                          // Alloc Flags
1050         &offset,                         // Alloc Offset
1051         &chSize,                         // Size
1052         1,                               // offsetAlign
1053         1,                               // sizeAlign
1054         NULL,                            // Allocated mem block
1055         pIsolationID,                    // IsolationID
1056         _kfifoUserdOwnerComparator       // Fifo defined ownership comparator
1057     );
1058 
1059     if(status != NV_OK)
1060     {
1061         NV_PRINTF(LEVEL_ERROR, "Failed to reserve channel IDs. Status = 0x%x\n", status);
1062         DBG_BREAKPOINT();
1063 
1064         //
1065         // Free the allocated memory and return early. After this, all failure
1066         // points can goto the common cleanup label
1067         //
1068         portMemFree(pIsolationID);
1069         return status;
1070     }
1071 
1072     pIsolationIdBlock = pChidMgr->pGlobalChIDHeap->eheapGetBlock(
1073         pChidMgr->pGlobalChIDHeap,
1074         offset,
1075         NV_FALSE);
1076     if (pIsolationIdBlock == NULL)
1077     {
1078         // Something bad happened. This should not fail if allocation succeeded
1079         NV_PRINTF(LEVEL_ERROR, "Could not fetch block from eheap\n");
1080         DBG_BREAKPOINT();
1081         goto cleanup;
1082     }
1083     pIsolationIdBlock->pData = pIsolationID;
1084 
1085     status = kfifoSetChidOffset(pGpu, pKernelFifo, pChidMgr, (NvU32)offset,
1086                                 numChannels, gfid, pChidOffset, pChannelCount,
1087                                 hMigClient, engineFifoListNumEntries, pEngineFifoList);
1088 
1089     if (status != NV_OK)
1090     {
1091         NV_PRINTF(LEVEL_ERROR,
1092             "Failed to program the CHID table\n");
1093         goto cleanup;
1094     }
1095 
1096     pChidMgr->ppVirtualChIDHeap[gfid] = portMemAllocNonPaged(sizeof(OBJEHEAP));
1097     if (pChidMgr->ppVirtualChIDHeap[gfid] == NULL)
1098     {
1099         status = NV_ERR_NO_MEMORY;
1100         NV_PRINTF(LEVEL_ERROR, "Error allocating memory for virtual "
1101             "channel ID heap\n");
1102         goto cleanup;
1103     }
1104     portMemSet(pChidMgr->ppVirtualChIDHeap[gfid], 0, sizeof(OBJEHEAP));
1105 
1106     //
1107     // Construct heap using low as offset and size of numChannels. This heap
1108     // will be used for guest channel ID allocations, but will be in the
1109     // system channel ID space, hence it only manages IDs from offset to
1110     // (offset + numChannels).
1111     //
1112     constructObjEHeap(pChidMgr->ppVirtualChIDHeap[gfid], offset,
1113                       (offset + numChannels), 0, 0);
1114 
1115     kfifoGetUserdSizeAlign_HAL(pKernelFifo, &userdBar1Size, NULL);
1116     pChidMgr->ppVirtualChIDHeap[gfid]->eheapSetOwnerIsolation(
1117         pChidMgr->ppVirtualChIDHeap[gfid],
1118         NV_FALSE,
1119         RM_PAGE_SIZE/userdBar1Size);
1120 
1121     return status;
1122 
1123 cleanup:
1124     portMemFree(pChidMgr->ppVirtualChIDHeap[gfid]);
1125     NV_ASSERT(kfifoSetChidOffset(pGpu, pKernelFifo, pChidMgr, 0, 0,
1126                                  gfid, pChidOffset, pChannelCount, hMigClient,
1127                                  engineFifoListNumEntries, pEngineFifoList) == NV_OK);
1128     NV_ASSERT(pChidMgr->pGlobalChIDHeap->eheapFree(pChidMgr->pGlobalChIDHeap, offset) == NV_OK);
1129     portMemFree(pIsolationID);
1130     return status;
1131 }
1132 
1133 /*! Frees a block of contiguous SCHIDs previously reserved for the given GFID */
1134 NV_STATUS
1135 kfifoChidMgrFreeSystemChids_IMPL
1136 (
1137     OBJGPU           *pGpu,
1138     KernelFifo       *pKernelFifo,
1139     CHID_MGR         *pChidMgr,
1140     NvU32             gfid,
1141     NvU32            *pChidOffset,
1142     NvU32            *pChannelCount,
1143     NvHandle          hMigClient,
1144     NvU32             engineFifoListNumEntries,
1145     FIFO_ENGINE_LIST *pEngineFifoList
1146 )
1147 {
1148     NV_STATUS status, tmpStatus;
1149     NvU64     chId;
1150 
1151     if (IS_VIRTUAL(pGpu))
1152     {
1153         // Not supported on guest or when SRIOV is disabled
1154         return NV_ERR_NOT_SUPPORTED;
1155     }
1156 
1157     // Get the schid base
1158     pChidMgr->ppVirtualChIDHeap[gfid]->eheapGetBase(
1159         pChidMgr->ppVirtualChIDHeap[gfid],
1160         &chId);
1161 
1162     status = _kfifoChidMgrFreeIsolationId(pChidMgr, (NvU32)chId);
1163     if(status != NV_OK)
1164     {
1165         NV_PRINTF(LEVEL_ERROR,
1166             "Failed to free IsolationId. Status = 0x%x\n",
1167             status);
1168         DBG_BREAKPOINT();
1169         return status;
1170     }
1171 
1172     status = pChidMgr->pGlobalChIDHeap->eheapFree(pChidMgr->pGlobalChIDHeap, chId);
1173     if(status != NV_OK)
1174     {
1175         NV_PRINTF(LEVEL_ERROR,
1176             "Failed to free channel IDs. Status = 0x%x\n",
1177             status);
1178         DBG_BREAKPOINT();
1179 
1180         //
1181         // March on anyway to program the ChId table. We'll return an error
1182         // if we get here though.
1183         //
1184     }
1185 
1186     tmpStatus = kfifoSetChidOffset(pGpu, pKernelFifo, pChidMgr, 0, 0,
1187                                    gfid, pChidOffset, pChannelCount, hMigClient,
1188                                    engineFifoListNumEntries, pEngineFifoList);
1189     if (tmpStatus != NV_OK)
1190     {
1191         NV_PRINTF(LEVEL_ERROR,
1192             "Failed to program the CHID table\n");
1193         DBG_BREAKPOINT();
1194         return tmpStatus;
1195     }
1196 
1197     pChidMgr->ppVirtualChIDHeap[gfid]->eheapDestruct(pChidMgr->ppVirtualChIDHeap[gfid]);
1198     portMemFree(pChidMgr->ppVirtualChIDHeap[gfid]);
1199     pChidMgr->ppVirtualChIDHeap[gfid] = NULL;
1200 
1201     return status;
1202 }
1203 
1204 NvU32
1205 kfifoChidMgrGetNumChannels_IMPL
1206 (
1207     OBJGPU *pGpu,
1208     KernelFifo *pKernelFifo,
1209     CHID_MGR *pChidMgr
1210 )
1211 {
1212     // Cache ChidMgr's numChannels if not set
1213     if (pChidMgr->numChannels == 0)
1214     {
1215         NvU32 numChannels = kfifoRunlistQueryNumChannels_HAL(pGpu, pKernelFifo,
1216                                                              pChidMgr->runlistId);
1217 
1218         if (pKernelFifo->bNumChannelsOverride)
1219         {
1220             pChidMgr->numChannels = NV_MIN(pKernelFifo->numChannelsOverride, numChannels);
1221         }
1222         else
1223         {
1224             pChidMgr->numChannels = numChannels;
1225         }
1226 
1227         // Once we have set calculated value disable any overrides.
1228         pKernelFifo->bNumChannelsOverride = 0;
1229     }
1230 
1231     return pChidMgr->numChannels;
1232 }
1233 
1234 NvU32
1235 kfifoRunlistQueryNumChannels_KERNEL
1236 (
1237     OBJGPU *pGpu,
1238     KernelFifo *pKernelFifo,
1239     NvU32 runlistId
1240 )
1241 {
1242     NvU32 numChannels = 0;
1243     NvU32 status;
1244 
1245     // Do internal control call and set numChannels
1246     if (IS_GSP_CLIENT(pGpu))
1247     {
1248         RM_API  *pRmApi   = GPU_GET_PHYSICAL_RMAPI(pGpu);
1249         NV2080_CTRL_INTERNAL_FIFO_GET_NUM_CHANNELS_PARAMS numChannelsParams = {0};
1250 
1251         numChannelsParams.runlistId = runlistId;
1252 
1253         status = pRmApi->Control(pRmApi,
1254                                  pGpu->hInternalClient,
1255                                  pGpu->hInternalSubdevice,
1256                                  NV2080_CTRL_CMD_INTERNAL_FIFO_GET_NUM_CHANNELS,
1257                                  &numChannelsParams,
1258                                  sizeof(NV2080_CTRL_INTERNAL_FIFO_GET_NUM_CHANNELS_PARAMS));
1259         if (status != NV_OK)
1260         {
1261             DBG_BREAKPOINT();
1262             return 0;
1263         }
1264 
1265         numChannels = numChannelsParams.numChannels;
1266     }
1267 
1268     NV_ASSERT(numChannels > 0);
1269 
1270     return numChannels;
1271 }
1272 
1273 /**
1274  * @brief reserves a hardware channel slot for a channel group
1275  *
1276  * Only responsible for indicating a hardware channel is in use, does not set
1277  * any other software state.
1278  *
1279  * This function is not called in broadcast mode
1280  *
1281  * @param pGpu
1282  * @param pKernelFifo
1283  * @param pChidMgr
1284  * @param[out] grpID
1285  */
1286 NV_STATUS
1287 kfifoChidMgrAllocChannelGroupHwID_IMPL
1288 (
1289     OBJGPU     *pGpu,
1290     KernelFifo *pKernelFifo,
1291     CHID_MGR   *pChidMgr,
1292     NvU32      *pChGrpID
1293 )
1294 {
1295     NvU32 maxChannelGroups;
1296 
1297     if (pChGrpID == NULL)
1298         return NV_ERR_INVALID_ARGUMENT;
1299 
1300     maxChannelGroups = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
1301     if (maxChannelGroups == 0)
1302     {
1303         NV_PRINTF(LEVEL_ERROR, "Zero max channel groups!!!\n");
1304         return NV_ERR_INVALID_ARGUMENT;
1305     }
1306 
1307     // Find the least unused grpID
1308     *pChGrpID = nvBitFieldLSZero(pChidMgr->channelGrpMgr.pHwIdInUse,
1309                                  pChidMgr->channelGrpMgr.hwIdInUseSz);
1310 
1311     if (*pChGrpID < maxChannelGroups)
1312     {
1313         nvBitFieldSet(pChidMgr->channelGrpMgr.pHwIdInUse,
1314                       pChidMgr->channelGrpMgr.hwIdInUseSz, *pChGrpID, NV_TRUE);
1315     }
1316     else
1317     {
1318         *pChGrpID = maxChannelGroups;
1319         NV_PRINTF(LEVEL_ERROR, "No allocatable FIFO available.\n");
1320         return NV_ERR_NO_FREE_FIFOS;
1321     }
1322     return NV_OK;
1323 }
1324 
1325 
1326 /**
1327  * @brief Releases a hardware channel group ID.
1328  *
1329  * Not responsible for freeing any software state beyond that which indicates a
1330  * hardware channel is in use.
1331  *
1332  * This function is not called in broadcast mode
1333  *
1334  * @param pGpu
1335  * @param pFifo
1336  * @param pChidMgr
1337  * @param chGrpID
1338  */
1339 NV_STATUS
1340 kfifoChidMgrFreeChannelGroupHwID_IMPL
1341 (
1342     OBJGPU     *pGpu,
1343     KernelFifo *pKernelFifo,
1344     CHID_MGR   *pChidMgr,
1345     NvU32       chGrpID
1346 )
1347 {
1348     NvU32 maxChannelGroups;
1349 
1350     maxChannelGroups = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
1351     if (maxChannelGroups == 0)
1352     {
1353         NV_PRINTF(LEVEL_ERROR, "Zero max channel groups!!!\n");
1354         return NV_ERR_INVALID_ARGUMENT;
1355     }
1356     NV_ASSERT_OR_RETURN(chGrpID < maxChannelGroups, NV_ERR_INVALID_ARGUMENT);
1357 
1358     //
1359     // Look for the channel group, check to make sure it's InUse bit is set
1360     // and then clear it to indicate the grpID is no longer in use
1361     //
1362     NV_ASSERT(nvBitFieldTest(pChidMgr->channelGrpMgr.pHwIdInUse,
1363                              pChidMgr->channelGrpMgr.hwIdInUseSz, chGrpID));
1364     nvBitFieldSet(pChidMgr->channelGrpMgr.pHwIdInUse,
1365                   pChidMgr->channelGrpMgr.hwIdInUseSz, chGrpID, NV_FALSE);
1366 
1367     return NV_OK;
1368 }
1369 
1370 CHID_MGR *
1371 kfifoGetChidMgr_IMPL
1372 (
1373     OBJGPU      *pGpu,
1374     KernelFifo  *pKernelFifo,
1375     NvU32        runlistId
1376 )
1377 {
1378     if (!kfifoIsPerRunlistChramEnabled(pKernelFifo))
1379     {
1380         // We only have 1 chidmgr when we don't have a per-runlist channel RAM
1381         if ((pKernelFifo->numChidMgrs != 1) ||
1382             (pKernelFifo->ppChidMgr == NULL) ||
1383             !bitVectorTest(&pKernelFifo->chidMgrValid, 0))
1384         {
1385             return NULL;
1386         }
1387         return pKernelFifo->ppChidMgr[0];
1388     }
1389     else
1390     {
1391         if (runlistId >= pKernelFifo->numChidMgrs)
1392         {
1393             return NULL;
1394         }
1395         //
1396         // It is valid to return a NULL value as long as runlistId is less than
1397         // maxNumRunlists since it is possible that not everything in the range
1398         // [0, numChidMgrs) represents a valid runlistId. The onus is on the
1399         // caller to check for NULL and only then use the CHIDMGR pointer
1400         //
1401         return pKernelFifo->ppChidMgr[runlistId];
1402     }
1403 }
1404 
1405 /*! Gets associated CHIDMGR object for given FIFO_ENGINE_INFO_TYPE and value */
1406 NV_STATUS
1407 kfifoGetChidMgrFromType_IMPL
1408 (
1409     OBJGPU     *pGpu,
1410     KernelFifo *pKernelFifo,
1411     NvU32       engineInfoType,
1412     NvU32       val,
1413     CHID_MGR  **ppChidMgr
1414 )
1415 {
1416     NV_STATUS status = NV_OK;
1417     NvU32     runlistId;
1418 
1419     NV_CHECK_OR_RETURN(LEVEL_INFO, ppChidMgr != NULL, NV_ERR_INVALID_ARGUMENT);
1420 
1421     // Initialize the pointer to NULL, in case we fail and return early
1422     *ppChidMgr = NULL;
1423 
1424     status = kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo,
1425                                       engineInfoType, val,
1426                                       ENGINE_INFO_TYPE_RUNLIST, &runlistId);
1427     NV_CHECK_OR_RETURN(LEVEL_INFO, NV_OK == status, status);
1428 
1429     *ppChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, runlistId);
1430 
1431     return NV_OK;
1432 }
1433 
1434 /*!
1435  * @brief Fetch pKernelChannel based on chidmgr and chid.
1436  *
1437  * This look-up uses the chid heap. It should find the first allocation of the channel,
1438  * which is useful if the handle is duped to another client.
1439  *
1440  * @param[in] pGpu
1441  * @param[in] pKernelFifo
1442  * @param[in] pChidMgr      the ChIDMgr (per-runlist)
1443  * @param[in] ChID          the ChID
1444  *
1445  * @return the KernelChannel * or NULL
1446  */
1447 KernelChannel *
1448 kfifoChidMgrGetKernelChannel_IMPL
1449 (
1450     OBJGPU     *pGpu,
1451     KernelFifo *pKernelFifo,
1452     CHID_MGR   *pChidMgr,
1453     NvU32       ChID
1454 )
1455 {
1456     EMEMBLOCK  *pFifoDataBlock;
1457     NvU32       numChannels;
1458 
1459     NV_ASSERT_OR_RETURN(pChidMgr != NULL, NULL);
1460     // Lite mode channels don't have KernelChannel yet
1461     NV_ASSERT_OR_RETURN(!kfifoIsLiteModeEnabled_HAL(pGpu, pKernelFifo), NULL);
1462 
1463     numChannels = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
1464     if (ChID >= numChannels)
1465     {
1466         return NULL;
1467     }
1468 
1469     pFifoDataBlock = pChidMgr->pFifoDataHeap->eheapGetBlock(
1470         pChidMgr->pFifoDataHeap,
1471         ChID,
1472         NV_FALSE);
1473     if (pFifoDataBlock != NULL)
1474     {
1475         return (KernelChannel *)pFifoDataBlock->pData;
1476     }
1477 
1478     return NULL;
1479 }
1480 
1481 /*! Gets channel group data corresponding to grpID */
1482 KernelChannelGroup *
1483 kfifoChidMgrGetKernelChannelGroup_IMPL
1484 (
1485     OBJGPU     *pGpu,
1486     KernelFifo *pKernelFifo,
1487     CHID_MGR   *pChidMgr,
1488     NvU32       grpID
1489 )
1490 {
1491     KernelChannelGroup *pKernelChannelGroup = NULL;
1492 
1493     pKernelChannelGroup = mapFind(pChidMgr->pChanGrpTree, grpID);
1494     if (pKernelChannelGroup == NULL)
1495     {
1496         NV_PRINTF(LEVEL_INFO, "Can't find channel group %d\n", grpID);
1497     }
1498 
1499     return pKernelChannelGroup;
1500 }
1501 
1502 /*!
1503  * @brief Gets channel group data corresponding to grpID
1504  *
1505  * This function is not called in broadcast mode
1506  *
1507  * @param     pGpu
1508  * @param     pFifo
1509  * @param[in] grpID
1510  * @param[in] runlistID pass CHIDMGR_RUNLIST_ID_LEGACY if not known
1511  *
1512  * @returns KernelChannelGroup * on success
1513  *        NULL      if channel group is not found
1514  */
1515 KernelChannelGroup *
1516 kfifoGetChannelGroup_IMPL
1517 (
1518     OBJGPU     *pGpu,
1519     KernelFifo *pKernelFifo,
1520     NvU32       grpID,
1521     NvU32       runlistID
1522 )
1523 {
1524     CHID_MGR *pChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, runlistID);
1525 
1526     return kfifoChidMgrGetKernelChannelGroup(pGpu, pKernelFifo, pChidMgr, grpID);
1527 }
1528 
1529 /*! Gets total number of channel groups in use */
1530 NvU32
1531 kfifoGetChannelGroupsInUse_IMPL
1532 (
1533     OBJGPU     *pGpu,
1534     KernelFifo *pKernelFifo
1535 )
1536 {
1537     NvU32    numChannelGroups      = 0;
1538     NvU32    numChannelGroupsInUse = 0;
1539     NvU32    chGrpID, i;
1540 
1541     for (i = 0; i < pKernelFifo->numChidMgrs; i++)
1542     {
1543         if (pKernelFifo->ppChidMgr[i] != NULL)
1544         {
1545             numChannelGroups = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo,
1546                                                       pKernelFifo->ppChidMgr[i]);
1547 
1548             for (chGrpID = 0; chGrpID < numChannelGroups; chGrpID++)
1549             {
1550                 if (nvBitFieldTest(pKernelFifo->ppChidMgr[i]->channelGrpMgr.pHwIdInUse,
1551                                    pKernelFifo->ppChidMgr[i]->channelGrpMgr.hwIdInUseSz,
1552                                    chGrpID))
1553                 {
1554                     numChannelGroupsInUse++;
1555                 }
1556             }
1557         }
1558     }
1559     return numChannelGroupsInUse;
1560 }
1561 
1562 /*! Gets total number of channel groups in use per engine */
1563 NvU32
1564 kfifoGetRunlistChannelGroupsInUse_IMPL
1565 (
1566     OBJGPU     *pGpu,
1567     KernelFifo *pKernelFifo,
1568     NvU32       runlistId
1569 )
1570 {
1571     NvU32      numChannelGroups      = 0;
1572     NvU32      numChannelGroupsInUse = 0;
1573     NvU32      chGrpID;
1574     CHID_MGR  *pChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, runlistId);
1575 
1576     numChannelGroups = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
1577     for (chGrpID = 0; chGrpID < numChannelGroups; chGrpID++)
1578     {
1579         if (nvBitFieldTest(pChidMgr->channelGrpMgr.pHwIdInUse,
1580                            pChidMgr->channelGrpMgr.hwIdInUseSz,
1581                            chGrpID))
1582         {
1583             numChannelGroupsInUse++;
1584         }
1585     }
1586     return numChannelGroupsInUse;
1587 }
1588 
1589 /**
1590  * @brief Sets the timeslice for the specified channel group.
1591  *
1592  * @returns NV_OK if success, appropriate error otherwise
1593  */
1594 NV_STATUS
1595 kfifoChannelGroupSetTimeslice_IMPL
1596 (
1597     OBJGPU             *pGpu,
1598     KernelFifo         *pKernelFifo,
1599     KernelChannelGroup *pKernelChannelGroup,
1600     NvU64               timesliceUs,
1601     NvBool              bSkipSubmit
1602 )
1603 {
1604     NV_STATUS status = NV_OK;
1605 
1606     NV_PRINTF(LEVEL_INFO, "Setting TSG %d Timeslice to %lldus\n",
1607               pKernelChannelGroup->grpID, timesliceUs);
1608 
1609     if (timesliceUs < kfifoRunlistGetMinTimeSlice_HAL(pKernelFifo))
1610     {
1611         NV_PRINTF(LEVEL_ERROR,
1612                   "Setting Timeslice to %lldus not allowed. Min value is %lldus\n",
1613                   timesliceUs, kfifoRunlistGetMinTimeSlice_HAL(pKernelFifo));
1614         return NV_ERR_NOT_SUPPORTED;
1615     }
1616 
1617     pKernelChannelGroup->timesliceUs = timesliceUs;
1618 
1619     NV_ASSERT_OK_OR_RETURN(kfifoChannelGroupSetTimesliceSched(pGpu,
1620                                                               pKernelFifo,
1621                                                               pKernelChannelGroup,
1622                                                               timesliceUs,
1623                                                               bSkipSubmit));
1624 
1625     return status;
1626 }
1627 
1628 void
1629 kfifoFillMemInfo_IMPL
1630 (
1631     KernelFifo                *pKernelFifo,
1632     MEMORY_DESCRIPTOR         *pMemDesc,
1633     NV2080_CTRL_FIFO_MEM_INFO *pMemory
1634 )
1635 {
1636     if (pMemDesc == NULL)
1637     {
1638         pMemory->aperture = NV2080_CTRL_CMD_FIFO_GET_CHANNEL_MEM_APERTURE_INVALID;
1639         NV_PRINTF(LEVEL_ERROR, "kfifoFillMemInfo: pMemDesc = NULL\n");
1640     }
1641     else
1642     {
1643         if (memdescGetAddressSpace(pMemDesc) == ADDR_FBMEM)
1644         {
1645             pMemory->aperture = NV2080_CTRL_CMD_FIFO_GET_CHANNEL_MEM_APERTURE_VIDMEM;
1646         }
1647         else if (memdescGetAddressSpace(pMemDesc) == ADDR_SYSMEM)
1648         {
1649             if (memdescGetCpuCacheAttrib(pMemDesc) == NV_MEMORY_CACHED)
1650             {
1651                 pMemory->aperture = NV2080_CTRL_CMD_FIFO_GET_CHANNEL_MEM_APERTURE_SYSMEM_COH;
1652             }
1653             else if (memdescGetCpuCacheAttrib(pMemDesc) == NV_MEMORY_UNCACHED)
1654             {
1655                 pMemory->aperture = NV2080_CTRL_CMD_FIFO_GET_CHANNEL_MEM_APERTURE_SYSMEM_NCOH;
1656             }
1657             else
1658             {
1659                 NV_PRINTF(LEVEL_ERROR,
1660                           "kfifoFillMemInfo: Unknown cache attribute for sysmem aperture\n");
1661                 NV_ASSERT(NV_FALSE);
1662             }
1663         }
1664         pMemory->base = memdescGetPhysAddr(pMemDesc, AT_GPU, 0);
1665         pMemory->size = pMemDesc->Size;
1666     }
1667 }
1668 
1669 void
1670 kfifoGetChannelIterator_IMPL
1671 (
1672     OBJGPU *pGpu,
1673     KernelFifo *pKernelFifo,
1674     CHANNEL_ITERATOR *pIt
1675 )
1676 {
1677     portMemSet(pIt, 0, sizeof(*pIt));
1678     pIt->physicalChannelID = 0;
1679     pIt->pFifoDataBlock    = NULL;
1680     pIt->runlistId         = 0;
1681     pIt->numRunlists       = 1;
1682     if (kfifoIsPerRunlistChramEnabled(pKernelFifo))
1683     {
1684         pIt->numRunlists = kfifoGetMaxNumRunlists_HAL(pGpu, pKernelFifo);
1685     }
1686 }
1687 
1688 /**
1689  * @brief Returns the next KernelChannel from the iterator.
1690  *
1691  * Iterates over runlist IDs and ChIDs and returns the next KernelChannel found
1692  * on the heap, if any.
1693  *
1694  * (error guaranteed if pointer is NULL; non-NULL pointer guaranteed if NV_OK)
1695  *
1696  * @param[in]  pGpu
1697  * @param[in]  pKernelFifo
1698  * @param[in]  pIt                   the channel iterator
1699  * @param[out] ppKernelChannel      returns a KernelChannel *
1700  *
1701  * @return NV_OK if the returned pointer is valid or error
1702  */
1703 NV_STATUS kfifoGetNextKernelChannel_IMPL
1704 (
1705     OBJGPU              *pGpu,
1706     KernelFifo          *pKernelFifo,
1707     CHANNEL_ITERATOR    *pIt,
1708     KernelChannel      **ppKernelChannel
1709 )
1710 {
1711     KernelChannel *pKernelChannel;
1712 
1713     if (ppKernelChannel == NULL)
1714         return NV_ERR_INVALID_ARGUMENT;
1715 
1716     *ppKernelChannel = NULL;
1717 
1718     while (pIt->runlistId < pIt->numRunlists)
1719     {
1720         CHID_MGR *pChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, pIt->runlistId);
1721 
1722         if (pChidMgr == NULL)
1723         {
1724             pIt->runlistId++;
1725             continue;
1726         }
1727 
1728         pIt->numChannels = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
1729 
1730         if (pIt->pFifoDataBlock == NULL)
1731         {
1732             pIt->pFifoDataBlock = pChidMgr->pFifoDataHeap->eheapGetBlock(
1733                 pChidMgr->pFifoDataHeap,
1734                 pIt->physicalChannelID,
1735                 NV_TRUE);
1736         }
1737 
1738         while (pIt->physicalChannelID < pIt->numChannels)
1739         {
1740             if (pIt->pFifoDataBlock->owner == NVOS32_BLOCK_TYPE_FREE)
1741             {
1742                 pIt->physicalChannelID = pIt->pFifoDataBlock->end + 1;
1743             }
1744             else
1745             {
1746                 pIt->physicalChannelID++;
1747                 pKernelChannel = (KernelChannel *)pIt->pFifoDataBlock->pData;
1748 
1749                 //
1750                 // This iterator can be used during an interrupt, when a KernelChannel may
1751                 // be in the process of being destroyed. If a KernelChannel expects a pChannel
1752                 // but does not have one, it means it's being destroyed and we don't want to
1753                 // return it.
1754                 //
1755                 if (pKernelChannel != NULL && kchannelIsValid_HAL(pKernelChannel))
1756                 {
1757                     // Prepare iterator to check next block in pChidMgr->pFifoDataHeap
1758                     pIt->pFifoDataBlock = pIt->pFifoDataBlock->next;
1759                     *ppKernelChannel = pKernelChannel;
1760                     return NV_OK;
1761                 }
1762             }
1763 
1764             // Check next block in pChidMgr->pFifoDataHeap
1765             pIt->pFifoDataBlock = pIt->pFifoDataBlock->next;
1766         }
1767 
1768         pIt->runlistId++;
1769         // Reset iterator for next runlist
1770         pIt->physicalChannelID = 0;
1771         pIt->pFifoDataBlock = NULL;
1772     }
1773 
1774     return NV_ERR_OBJECT_NOT_FOUND;
1775 }
1776 
1777 /*!
1778  * @brief Localize the engine info received from the host
1779  *
1780  * The host and the guest can run in different version of drivers, the guest driver
1781  * can not directly use the raw MC_ENGINE_IDX values from the host.
1782  * RM does not guarantee those values are consistent cross branches.
1783  *
1784  * To keep the compatibility between different version of branches, this function reconstructs
1785  * of RM_ENGINE_TYPE, MC_ENGINE_IDX values based on NV2080_ENGINE_TYPE
1786  *
1787  * @param[in] pGpu                 OBJGPU pointer
1788  * @param[in] pFifo                KernelFifo pointer
1789  * @param[in/out] pEngine          Pointer to engine info table to update
1790  *
1791  */
1792 static void
1793 _kfifoLocalizeGuestEngineData
1794 (
1795     OBJGPU *pGpu,
1796     KernelFifo *pKernelFifo,
1797     ENGINE_INFO *pEngineInfo
1798 )
1799 {
1800     const FIFO_GUEST_ENGINE_TABLE *guestEngineTable;
1801     NvU32 guestEngineTableSz;
1802     NvU32 nv2080EngineType;
1803     NvU32 engineIdx;
1804     NvU32 newEngineIdx;
1805     NvU32 guestTableIdx;
1806 
1807     // This function should only be called in a vgpu guest RM
1808     NV_ASSERT_OR_RETURN_VOID(IS_VIRTUAL(pGpu));
1809 
1810     guestEngineTable = kfifoGetGuestEngineLookupTable(&guestEngineTableSz);
1811 
1812     newEngineIdx = 0;
1813 
1814     for (engineIdx = 0; engineIdx < pEngineInfo->engineInfoListSize; engineIdx++)
1815     {
1816         FIFO_ENGINE_LIST *pEngine = &pEngineInfo->engineInfoList[engineIdx];
1817 
1818         // The actual data in engineData[ENGINE_INFO_TYPE_RM_ENGINE_TYPE] is NV2080 ENGINE TYPE.
1819         nv2080EngineType = pEngine->engineData[ENGINE_INFO_TYPE_RM_ENGINE_TYPE];
1820 
1821         for (guestTableIdx = 0; guestTableIdx < guestEngineTableSz; guestTableIdx++)
1822         {
1823             // Find the engine type supported by the guest
1824             if (guestEngineTable[guestTableIdx].nv2080EngineType == nv2080EngineType)
1825                 break;
1826         }
1827 
1828         if (guestTableIdx < guestEngineTableSz)
1829         {
1830             // Update the MC for the guest
1831             pEngine->engineData[ENGINE_INFO_TYPE_MC] = guestEngineTable[guestTableIdx].mcIdx;
1832 
1833             // Update it with correct engine type
1834             pEngine->engineData[ENGINE_INFO_TYPE_RM_ENGINE_TYPE] = gpuGetRmEngineType(nv2080EngineType);
1835 
1836             if (newEngineIdx != engineIdx)
1837             {
1838                 //
1839                 // Move the engine info up to make sure the engine info table only contains data for
1840                 // guest supported engine types.
1841                 //
1842                 portMemCopy(&pEngineInfo->engineInfoList[newEngineIdx], sizeof(FIFO_ENGINE_LIST),
1843                             &pEngineInfo->engineInfoList[engineIdx], sizeof(FIFO_ENGINE_LIST));
1844             }
1845 
1846             newEngineIdx++;
1847         }
1848     }
1849 
1850     pEngineInfo->engineInfoListSize = newEngineIdx;
1851 }
1852 
1853 /*!
1854  * @brief Performs an RPC into Host RM to read its device info table.
1855  *
1856  * This is necessary because in virtual environments, we cannot directly read
1857  * the device info table, and do not have the physical GPU partitioning
1858  * information to determine which engines belong to this guest, so we have Host
1859  * RM do the filtering and send us the filtered table.
1860  *
1861  * @param[in] pGpu
1862  * @param[in] pKernelFifo
1863  *
1864  * @return NV_OK if succcessful,
1865  *         NV_ERR_NOT_SUPPORTED if Host RM calls this interface
1866  *         NV_ERR_INVALID_STATE if host supplied invalid data
1867  *         NV_STATUS supplied by RPC response from Host
1868  */
1869 
1870 NV_STATUS
1871 kfifoGetHostDeviceInfoTable_KERNEL
1872 (
1873     OBJGPU      *pGpu,
1874     KernelFifo  *pKernelFifo,
1875     ENGINE_INFO *pEngineInfo,
1876     NvHandle     hMigClient
1877 )
1878 {
1879     NV_STATUS status = NV_OK;
1880     NvHandle hClient = NV01_NULL_OBJECT;
1881     NvHandle hObject = NV01_NULL_OBJECT;
1882     NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_PARAMS *pParams;
1883     NV2080_CTRL_FIFO_DEVICE_ENTRY *pFetchedTable;
1884     NvU32 numEntries;
1885     NvU32 device;
1886     NvU32 entry;
1887     NvU32 numRunlists;
1888     NvU32 maxRunlistId;
1889     NvU32 maxPbdmaId;
1890     NvU32 i;
1891     struct {
1892         NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_PARAMS params;
1893         NV2080_CTRL_FIFO_DEVICE_ENTRY entries[NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_MAX_DEVICES];
1894     } *pLocals;
1895 
1896 
1897     NV_ASSERT_OR_RETURN(IS_VIRTUAL(pGpu) || IS_GSP_CLIENT(pGpu),
1898                         NV_ERR_NOT_SUPPORTED);
1899 
1900     // RPC call for GSP will throw INVALID_CLIENT error with NULL handles
1901     if (IS_GSP_CLIENT(pGpu))
1902     {
1903         if (!IS_MIG_IN_USE(pGpu))
1904         {
1905             hClient = pGpu->hInternalClient;
1906             hObject = pGpu->hInternalSubdevice;
1907         }
1908         else
1909         {
1910             RsClient *pClient;
1911             Subdevice *pSubdevice;
1912 
1913             hClient = hMigClient;
1914 
1915             NV_ASSERT_OK_OR_RETURN(
1916                     serverGetClientUnderLock(&g_resServ, hClient, &pClient));
1917 
1918             NV_ASSERT_OK_OR_RETURN(subdeviceGetByGpu(pClient, pGpu, &pSubdevice));
1919 
1920             GPU_RES_SET_THREAD_BC_STATE(pSubdevice);
1921 
1922             hObject = RES_GET_HANDLE(pSubdevice);
1923         }
1924     }
1925 
1926     // Allocate pFetchedTable and params on the heap to avoid stack overflow
1927     pLocals = portMemAllocNonPaged(sizeof(*pLocals));
1928     NV_ASSERT_OR_RETURN((pLocals != NULL), NV_ERR_NO_MEMORY);
1929 
1930     pParams = &pLocals->params;
1931     pFetchedTable = pLocals->entries;
1932 
1933     //
1934     // Read device info table entries from Host RM until Host indicates that
1935     // there are no more valid entries in the table (by setting bMore flag)
1936     //
1937     numEntries = 0;
1938     for (device = 0;
1939          device < NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_MAX_DEVICES;
1940          device += NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_MAX_ENTRIES)
1941     {
1942         portMemSet(pParams, 0x0, sizeof(*pParams));
1943         pParams->baseIndex = device;
1944 
1945         NV_RM_RPC_CONTROL(pGpu,
1946                           hClient,
1947                           hObject,
1948                           NV2080_CTRL_CMD_FIFO_GET_DEVICE_INFO_TABLE,
1949                           pParams,
1950                           sizeof(*pParams),
1951                           status);
1952 
1953         if (status != NV_OK)
1954             goto cleanup;
1955 
1956         // Assert that host RM didn't tell us an invalid number of entries
1957         if (pParams->numEntries >
1958             NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_MAX_ENTRIES)
1959         {
1960             DBG_BREAKPOINT();
1961             status = NV_ERR_INVALID_STATE;
1962             goto cleanup;
1963         }
1964 
1965         portMemCopy(&pFetchedTable[device],
1966                     pParams->numEntries * (sizeof *pFetchedTable),
1967                     pParams->entries,
1968                     pParams->numEntries * (sizeof *pParams->entries));
1969 
1970         numEntries += pParams->numEntries;
1971 
1972         if (!pParams->bMore)
1973         {
1974             break;
1975         }
1976     }
1977 
1978     pEngineInfo->engineInfoListSize = numEntries;
1979     pEngineInfo->engineInfoList = portMemAllocNonPaged(sizeof(*pEngineInfo->engineInfoList) *
1980                                                        pEngineInfo->engineInfoListSize);
1981     if (pEngineInfo->engineInfoList == NULL)
1982     {
1983         NV_CHECK(LEVEL_ERROR, pEngineInfo->engineInfoList != NULL);
1984         status = NV_ERR_NO_MEMORY;
1985         goto cleanup;
1986     }
1987 
1988     // Copy each entry from the table
1989     numRunlists = 0;
1990     maxRunlistId = 0;
1991     maxPbdmaId = 0;
1992     for (entry = 0; entry < numEntries; ++entry)
1993     {
1994         FIFO_ENGINE_LIST *pLocalEntry = &pEngineInfo->engineInfoList[entry];
1995         NV2080_CTRL_FIFO_DEVICE_ENTRY *pFetchedEntry = &pFetchedTable[entry];
1996 
1997         ct_assert(sizeof pLocalEntry->engineData <=
1998                   sizeof pFetchedEntry->engineData);
1999         portMemCopy(pLocalEntry->engineData,
2000                     sizeof pLocalEntry->engineData,
2001                     pFetchedEntry->engineData,
2002                     NV_ARRAY_ELEMENTS(pLocalEntry->engineData) *
2003                         (sizeof *pFetchedEntry->engineData));
2004 
2005         pLocalEntry->numPbdmas = pFetchedEntry->numPbdmas;
2006         NV_ASSERT_TRUE_OR_GOTO(status,
2007             pLocalEntry->numPbdmas <=
2008                     NV_ARRAY_ELEMENTS(pLocalEntry->pbdmaIds) &&
2009                 pLocalEntry->numPbdmas <=
2010                     NV_ARRAY_ELEMENTS(pLocalEntry->pbdmaFaultIds),
2011             NV_ERR_INVALID_STATE,
2012             cleanup);
2013         portMemCopy(
2014             pLocalEntry->pbdmaIds,
2015             pLocalEntry->numPbdmas * (sizeof *(pLocalEntry->pbdmaIds)),
2016             pFetchedEntry->pbdmaIds,
2017             pLocalEntry->numPbdmas * (sizeof *(pFetchedEntry->pbdmaIds)));
2018         portMemCopy(
2019             pLocalEntry->pbdmaFaultIds,
2020             pLocalEntry->numPbdmas * (sizeof *(pLocalEntry->pbdmaFaultIds)),
2021             pFetchedEntry->pbdmaFaultIds,
2022             pLocalEntry->numPbdmas * (sizeof *(pFetchedEntry->pbdmaFaultIds)));
2023 
2024         portStringCopy(pLocalEntry->engineName,
2025                        sizeof pLocalEntry->engineName,
2026                        pFetchedEntry->engineName,
2027                        sizeof pFetchedEntry->engineName);
2028 
2029         if (pLocalEntry->engineData[ENGINE_INFO_TYPE_IS_HOST_DRIVEN_ENGINE] != 0)
2030         {
2031             numRunlists++;
2032         }
2033         maxRunlistId = NV_MAX(maxRunlistId,
2034                               pFetchedEntry->engineData[ENGINE_INFO_TYPE_RUNLIST]);
2035 
2036         for (i = 0; i < pLocalEntry->numPbdmas; i++)
2037         {
2038             maxPbdmaId = NV_MAX(maxPbdmaId, pLocalEntry->pbdmaIds[i]);
2039 
2040             //
2041             // SW engine while being constructed does not populate any PBDMA Fault IDs.
2042             // Hence, skipping it.
2043             //
2044             if (pLocalEntry->engineData[ENGINE_INFO_TYPE_ENG_DESC] != ENG_SW)
2045             {
2046                 bitVectorSet(&pEngineInfo->validEngineIdsForPbdmas, pLocalEntry->pbdmaFaultIds[i]);
2047             }
2048         }
2049     }
2050 
2051     NV_ASSERT_OK_OR_GOTO(status,
2052         kfifoReservePbdmaFaultIds_HAL(pGpu, pKernelFifo,
2053                                       pEngineInfo->engineInfoList,
2054                                       pEngineInfo->engineInfoListSize),
2055         cleanup);
2056 
2057     if (IS_VIRTUAL(pGpu))
2058     {
2059         _kfifoLocalizeGuestEngineData(pGpu, pKernelFifo, pEngineInfo);
2060     }
2061 
2062     pEngineInfo->numRunlists = numRunlists;
2063     pEngineInfo->maxNumRunlists = maxRunlistId + 1;
2064     pEngineInfo->maxNumPbdmas = maxPbdmaId + 1;
2065 
2066 cleanup:
2067     portMemFree(pLocals);
2068 
2069     return status;
2070 }
2071 
2072 /*!
2073  * @brief Constructs EngineInfo List
2074  *
2075  * @param[in] pGpu
2076  * @param[in] pKernelFifo
2077  *
2078  * @return NV_OK if succcessful,
2079  *         NV_STATUS supplied by HALs called
2080  */
2081 NV_STATUS
2082 kfifoConstructEngineList_KERNEL
2083 (
2084     OBJGPU     *pGpu,
2085     KernelFifo *pKernelFifo
2086 )
2087 {
2088     ENGINE_INFO *pEngineInfo = &pKernelFifo->engineInfo;
2089 
2090     // Return early if EngineList is already constructed
2091     if (pEngineInfo->engineInfoList != NULL)
2092         return NV_OK;
2093 
2094     if (IS_GSP_CLIENT(pGpu))
2095     {
2096         NV_ASSERT_OK_OR_RETURN(gpuConstructDeviceInfoTable_HAL(pGpu));
2097     }
2098 
2099     NV_ASSERT_OK_OR_RETURN(kfifoGetHostDeviceInfoTable_HAL(pGpu, pKernelFifo, pEngineInfo, 0));
2100 
2101     return NV_OK;
2102 }
2103 
2104 /**
2105  * @brief Create a list of channels.
2106  *
2107  * @param pGpu
2108  * @param pKernelFifo
2109  * @param pList
2110  */
2111 NV_STATUS
2112 kfifoChannelListCreate_IMPL
2113 (
2114     OBJGPU *pGpu,
2115     KernelFifo *pKernelFifo,
2116     CHANNEL_LIST **ppList
2117 )
2118 {
2119     if (!ppList)
2120         return NV_ERR_INVALID_ARGUMENT;
2121 
2122     *ppList = portMemAllocNonPaged(sizeof(CHANNEL_LIST));
2123     NV_ASSERT_OR_RETURN((*ppList != NULL), NV_ERR_NO_MEMORY);
2124 
2125     (*ppList)->pHead = NULL;
2126     (*ppList)->pTail = NULL;
2127 
2128     return NV_OK;
2129 }
2130 
2131 /**
2132  * @brief Append a channel to a channel list.
2133  *
2134  * @param pGpu
2135  * @param pKernelFifo
2136  * @param pKernelChannel
2137  * @param pList
2138  */
2139 
2140 NV_STATUS
2141 kfifoChannelListAppend_IMPL
2142 (
2143     OBJGPU *pGpu,
2144     KernelFifo *pKernel,
2145     KernelChannel *pKernelChannel,
2146     CHANNEL_LIST *pList
2147 )
2148 {
2149     PCHANNEL_NODE pNewNode = NULL;
2150 
2151     if (!pKernelChannel || !pList)
2152         return NV_ERR_INVALID_ARGUMENT;
2153 
2154     pNewNode = portMemAllocNonPaged(sizeof(CHANNEL_NODE));
2155     NV_ASSERT_OR_RETURN((pNewNode != NULL), NV_ERR_NO_MEMORY);
2156 
2157     pNewNode->pKernelChannel = pKernelChannel;
2158     pKernelChannel->refCount++;
2159 
2160     pNewNode->pNext = NULL;
2161 
2162     // Searching based on the ChID
2163     if (pList->pTail)
2164     {
2165         pList->pTail->pNext = pNewNode;
2166         pList->pTail = pNewNode;
2167     }
2168     else
2169     {
2170         pList->pHead = pNewNode;
2171         pList->pTail = pNewNode;
2172     }
2173 
2174     return NV_OK;
2175 }
2176 
2177 /**
2178  * @brief remove channel from the given channel list
2179  *  look for duplicates.
2180  *
2181  * @param pGpu
2182  * @param pKernelFifo
2183  * @param pKernelChannel
2184  * @param pList
2185  */
2186 NV_STATUS
2187 kfifoChannelListRemove_IMPL
2188 (
2189     OBJGPU *pGpu,
2190     KernelFifo *pKernelFifo,
2191     KernelChannel *pKernelChannel,
2192     CHANNEL_LIST *pList
2193 )
2194 {
2195     PCHANNEL_NODE pNewNode   = NULL;
2196     PCHANNEL_NODE pPrevNode  = NULL;
2197     PCHANNEL_NODE pTempNode  = NULL;
2198     NvBool        bFoundOnce = NV_FALSE;
2199     NV_STATUS     status     = NV_OK;
2200 
2201     if (!pKernelChannel)
2202         return NV_ERR_INVALID_ARGUMENT;
2203 
2204     if (!pList)
2205         return NV_ERR_INVALID_ARGUMENT;
2206 
2207     pNewNode = pList->pHead;
2208     pPrevNode = NULL;
2209 
2210     while (pNewNode)
2211     {
2212 
2213         if (pKernelChannel != pNewNode->pKernelChannel)
2214         {
2215             pPrevNode = pNewNode;
2216             pNewNode  = pNewNode->pNext;
2217             continue;
2218         }
2219 
2220         // Deleting first node
2221         if (pList->pHead == pNewNode)
2222         {
2223             pList->pHead = pNewNode->pNext;
2224         }
2225 
2226         // Deleting tail node
2227         if (pList->pTail == pNewNode)
2228         {
2229             pList->pTail =  pPrevNode;
2230         }
2231 
2232         // First node does not have previous node.
2233         if (pPrevNode)
2234         {
2235             pPrevNode->pNext = pNewNode->pNext;
2236         }
2237 
2238         pTempNode        = pNewNode;
2239         pNewNode         = pNewNode->pNext;
2240         portMemFree(pTempNode);
2241 
2242         bFoundOnce       = NV_TRUE;
2243 
2244         if (0 == pKernelChannel->refCount)
2245         {
2246             NV_PRINTF(LEVEL_ERROR, "RefCount for channel is not right!!!\n");
2247             DBG_BREAKPOINT();
2248             status = NV_ERR_GENERIC;
2249             break;
2250         }
2251 
2252         pKernelChannel->refCount--;
2253     }
2254 
2255 
2256     if (!bFoundOnce)
2257     {
2258         NV_PRINTF(LEVEL_INFO,
2259                   "Can't find channel in channelGroupList (Normal during RC Recovery on "
2260                   "GK110+ or if software scheduling is enabled).\n");
2261 
2262         status =  NV_ERR_INVALID_CHANNEL;
2263     }
2264 
2265     return status;
2266 }
2267 
2268 /**
2269  * @brief Destroy channel list.
2270  *
2271  * @param pGpu
2272  * @param pKernelFifo
2273  * @param pList
2274  */
2275 NV_STATUS
2276 kfifoChannelListDestroy_IMPL
2277 (
2278     OBJGPU *pGpu,
2279     KernelFifo *pKernel,
2280     CHANNEL_LIST *pList
2281 )
2282 {
2283     PCHANNEL_NODE pTempNode;
2284 
2285     if (!pList)
2286         return NV_OK;
2287 
2288     while (pList->pHead)
2289     {
2290         pTempNode = pList->pHead;
2291 
2292         NV_ASSERT_OR_RETURN(pTempNode->pKernelChannel && pTempNode->pKernelChannel->refCount, NV_ERR_INVALID_STATE);
2293 
2294         pTempNode->pKernelChannel->refCount--;
2295 
2296         pList->pHead = pTempNode->pNext;
2297         portMemFree(pTempNode);
2298     }
2299 
2300     portMemFree(pList);
2301 
2302     return NV_OK;
2303 }
2304 
2305 /*!
2306  * @brief   Determines whether provided engines have any channels/contexts assigned
2307  *
2308  * @param[IN]   pGpu             OBJGPU
2309  * @param[IN]   pKernelFifo      KernelFifo
2310  * @param[IN]   pEngines         Which engines to check (RM_ENGINE_TYPE_***)
2311  * @param[IN]   engineCount      Number of engines to check
2312  *
2313  * @return  Returns NV_TRUE if any provided engines are active
2314  */
2315 NvBool
2316 kfifoEngineListHasChannel_IMPL
2317 (
2318     OBJGPU     *pGpu,
2319     KernelFifo *pKernelFifo,
2320     RM_ENGINE_TYPE *pEngines,
2321     NvU32       engineCount
2322 )
2323 {
2324     KernelChannel *pKernelChannel;
2325     CHANNEL_ITERATOR it;
2326     NvU32 i;
2327 
2328     NV_ASSERT_OR_RETURN((pEngines != NULL) && (engineCount > 0), NV_TRUE);
2329 
2330     // Find any channels or contexts on passed engines
2331     kfifoGetChannelIterator(pGpu, pKernelFifo, &it);
2332     while (kchannelGetNextKernelChannel(pGpu, &it, &pKernelChannel) == NV_OK)
2333     {
2334         NV_ASSERT_OR_ELSE(pKernelChannel != NULL, continue);
2335 
2336         // If the client supplied the engine type, directly check it
2337         if (RM_ENGINE_TYPE_IS_VALID(kchannelGetEngineType(pKernelChannel)))
2338         {
2339             for (i = 0; i < engineCount; ++i)
2340             {
2341                 if (kchannelGetEngineType(pKernelChannel) == pEngines[i])
2342                 {
2343                     NV_PRINTF(LEVEL_ERROR,
2344                         "Found channel on engine 0x%x owned by 0x%x\n",
2345                          kchannelGetEngineType(pKernelChannel), RES_GET_CLIENT_HANDLE(pKernelChannel));
2346 
2347                     return NV_TRUE;
2348                 }
2349             }
2350         }
2351         else
2352         {
2353             NvU32 runlistId;
2354 
2355             // Ideally valid engine Id should always be set in channel if this property is enabled
2356             NV_ASSERT_OR_RETURN(!kfifoIsPerRunlistChramEnabled(pKernelFifo), NV_TRUE);
2357 
2358             //
2359             // If runlist Id for channel is set then check if it matches with any of the engines
2360             // If channel is not associated with any engine then there is a chance
2361             // it can be created on one of the engines we care about.
2362             //
2363             if (kchannelIsRunlistSet(pGpu, pKernelChannel))
2364             {
2365                 for (i = 0; i < engineCount; ++i)
2366                 {
2367                     NV_ASSERT_OR_RETURN((kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo,
2368                                            ENGINE_INFO_TYPE_RM_ENGINE_TYPE, (NvU32)pEngines[i],
2369                                            ENGINE_INFO_TYPE_RUNLIST, &runlistId) == NV_OK), NV_TRUE);
2370                     if (kchannelGetRunlistId(pKernelChannel) == runlistId)
2371                     {
2372                         NV_PRINTF(LEVEL_ERROR,
2373                             "Found channel on runlistId 0x%x owned by 0x%x\n",
2374                              kchannelGetRunlistId(pKernelChannel), RES_GET_CLIENT_HANDLE(pKernelChannel));
2375 
2376                         return NV_TRUE;
2377                     }
2378                 }
2379             }
2380             else
2381             {
2382                 NV_PRINTF(LEVEL_ERROR,
2383                     "Found channel owned by 0x%x that can be associated to any engine\n",
2384                      RES_GET_CLIENT_HANDLE(pKernelChannel));
2385 
2386                 return NV_TRUE;
2387             }
2388         }
2389     }
2390 
2391     return NV_FALSE;
2392 }
2393 
2394 /**
2395  * @brief Maximum number of subcontexts for a non-legacy mode TSG
2396  */
2397 CTX_BUF_POOL_INFO *
2398 kfifoGetRunlistBufPool_IMPL
2399 (
2400     OBJGPU *pGpu,
2401     KernelFifo *pKernelFifo,
2402     RM_ENGINE_TYPE rmEngineType
2403 )
2404 {
2405     return pKernelFifo->pRunlistBufPool[rmEngineType];
2406 }
2407 
2408 /**
2409  * @brief Get size and alignment requirements for runlist buffers
2410  *
2411  * @param[in]  pGpu                 Pointer to OBJGPU
2412  * @param[in]  pKernelFifo          Pointer to KernelFifo
2413  * @param[in]  runlistId            Runlist ID
2414  * @param[in]  bTsgSupported        Is TSG supported
2415  * @param[in]  maxRunlistEntries    Max entries to be supported in a runlist
2416  * @param[out] pSize                Size of runlist buffer
2417  * @param[out] pAlignment           Alignment for runlist buffer
2418  */
2419 NV_STATUS
2420 kfifoGetRunlistBufInfo_IMPL
2421 (
2422     OBJGPU       *pGpu,
2423     KernelFifo   *pKernelFifo,
2424     NvU32         runlistId,
2425     NvBool        bTsgSupported,
2426     NvU32         maxRunlistEntries,
2427     NvU64        *pSize,
2428     NvU64        *pAlignment
2429 )
2430 {
2431     NvU32           runlistEntrySize = 0;
2432     NvU32           maxRunlistEntriesSupported = 0;
2433     CHID_MGR       *pChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, runlistId);
2434 
2435     NV_ASSERT_OR_RETURN(pSize != NULL, NV_ERR_INVALID_ARGUMENT);
2436     NV_ASSERT_OR_RETURN(pAlignment != NULL, NV_ERR_INVALID_ARGUMENT);
2437 
2438     if (kfifoIsPerRunlistChramEnabled(pKernelFifo))
2439     {
2440         NV_ASSERT_OR_RETURN(pChidMgr != NULL, NV_ERR_INVALID_ARGUMENT);
2441         //
2442         // We assume worst case of one TSG wrapper per channel, and
2443         // the number of TSGs + number of channels is how we get
2444         // the 2 x number of fifos.
2445         //
2446         maxRunlistEntriesSupported = 2 * kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
2447     }
2448     else
2449     {
2450         maxRunlistEntriesSupported = kfifoGetMaxChannelsInSystem(pGpu, pKernelFifo);
2451         maxRunlistEntriesSupported += (bTsgSupported ?
2452                                        kfifoGetMaxChannelGroupsInSystem(pGpu, pKernelFifo)
2453                                        : 0);
2454     }
2455 
2456     NV_ASSERT_OR_RETURN(maxRunlistEntries <= maxRunlistEntriesSupported, NV_ERR_INVALID_ARGUMENT);
2457 
2458     if (maxRunlistEntries == 0)
2459     {
2460         maxRunlistEntries = maxRunlistEntriesSupported;
2461     }
2462 
2463     runlistEntrySize = kfifoRunlistGetEntrySize_HAL(pKernelFifo);
2464     *pSize = (NvU64)runlistEntrySize * maxRunlistEntries;
2465 
2466     *pAlignment = NVBIT64(kfifoRunlistGetBaseShift_HAL(pKernelFifo));
2467     return NV_OK;
2468 }
2469 
2470 /*!
2471  * @brief Gets total number of channels supported by the system
2472  */
2473 NvU32
2474 kfifoGetMaxChannelsInSystem_IMPL
2475 (
2476     OBJGPU *pGpu,
2477     KernelFifo *pKernelFifo
2478 )
2479 {
2480     NvU32 numChannels = 0;
2481     NvU32 i;
2482 
2483     for (i = 0; i < pKernelFifo->numChidMgrs; i++)
2484     {
2485         if (pKernelFifo->ppChidMgr[i] != NULL)
2486         {
2487             numChannels += kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pKernelFifo->ppChidMgr[i]);
2488         }
2489     }
2490     return numChannels;
2491 }
2492 
2493 /*!
2494  * @brief Gets total number of channel groups supported by the system
2495  */
2496 NvU32
2497 kfifoGetMaxChannelGroupsInSystem_IMPL
2498 (
2499     OBJGPU *pGpu,
2500     KernelFifo *pKernelFifo
2501 )
2502 {
2503     // Max channel groups is the same as max channels
2504     return kfifoGetMaxChannelsInSystem(pGpu, pKernelFifo);
2505 }
2506 
2507 /*!
2508  * @brief Get runlist buffer allocation params
2509  *
2510  * @param[in]    pGpu
2511  * @param[out]  *pAperture      Aperture to use for runlist buffer allocation
2512  * @param[out]  *pAttr          Attributes to use for runlits buffer allocation
2513  * @param[out]  *pAllocFlags    Allocation flags to use for runlist buffer allocation
2514  */
2515 void
2516 kfifoRunlistGetBufAllocParams_IMPL
2517 (
2518     OBJGPU           *pGpu,
2519     NV_ADDRESS_SPACE *pAperture,
2520     NvU32            *pAttr,
2521     NvU64            *pAllocFlags
2522 )
2523 {
2524     *pAperture = ADDR_FBMEM;
2525     *pAttr = NV_MEMORY_WRITECOMBINED;
2526 
2527     memdescOverrideInstLoc(DRF_VAL(_REG_STR_RM, _INST_LOC, _RUNLIST, pGpu->instLocOverrides),
2528                            "runlist", pAperture, pAttr);
2529 
2530     *pAllocFlags = FLD_TEST_DRF(_REG_STR_RM, _INST_VPR, _RUNLIST, _TRUE, pGpu->instVprOverrides)
2531                        ? MEMDESC_ALLOC_FLAGS_PROTECTED : MEMDESC_FLAGS_NONE;
2532 }
2533 
2534 /*!
2535  * @brief Allocate Runlist buffers for a single runlistId
2536  *
2537  * @param[in]   pGpu
2538  * @param[in]   pKernelFifo
2539  * @param[in]   bSupportTsg         Will this runlist support TSGs?
2540  * @param[in]   aperture            NV_ADDRESS_SPACE requested
2541  * @param[in]   runlistId           runlistId to allocate buffer for
2542  * @param[in]   attr                CPU cacheability requested
2543  * @param[in]   allocFlags          MEMDESC_FLAGS_*
2544  * @param[in]   maxRunlistEntries   Can pass zero to determine in function
2545  * @param[in]   bHWRL               Is this runlist a HW runlist? (verif feature specific)
2546  * @param[out]  ppMemDesc           memdesc created/allocated by function
2547  */
2548 NV_STATUS
2549 kfifoRunlistAllocBuffers_IMPL
2550 (
2551     OBJGPU             *pGpu,
2552     KernelFifo         *pKernelFifo,
2553     NvBool              bSupportTsg,
2554     NV_ADDRESS_SPACE    aperture,
2555     NvU32               runlistId,
2556     NvU32               attr,
2557     NvU64               allocFlags,
2558     NvU64               maxRunlistEntries,
2559     NvBool              bHWRL,
2560     MEMORY_DESCRIPTOR **ppMemDesc
2561 )
2562 {
2563     NV_STATUS status        = NV_OK;
2564     NvU64     runlistSz     = 0;
2565     NvU64     runlistAlign  = 0;
2566     NvU32     counter;
2567 
2568     status = kfifoGetRunlistBufInfo(pGpu, pKernelFifo, runlistId, bSupportTsg,
2569         maxRunlistEntries, &runlistSz, &runlistAlign);
2570     if (status != NV_OK)
2571     {
2572         NV_PRINTF(LEVEL_ERROR, "failed to get runlist buffer info 0x%08x\n",
2573                   status);
2574         DBG_BREAKPOINT();
2575         goto failed;
2576     }
2577 
2578     for (counter = 0; counter < NUM_BUFFERS_PER_RUNLIST; ++counter)
2579     {
2580         ppMemDesc[counter] = NULL;
2581 
2582         status = memdescCreate(&ppMemDesc[counter], pGpu, runlistSz, runlistAlign,
2583                                NV_TRUE, aperture, attr, allocFlags);
2584         if (status != NV_OK)
2585         {
2586             NV_PRINTF(LEVEL_ERROR,
2587                       "Runlist buffer memdesc create failed 0x%08x\n", status);
2588             DBG_BREAKPOINT();
2589             goto failed;
2590         }
2591 
2592         // If flag is set then allocate runlist from ctx buf pool
2593         if (allocFlags & MEMDESC_FLAGS_OWNED_BY_CTX_BUF_POOL)
2594         {
2595             RM_ENGINE_TYPE rmEngineType;
2596             CTX_BUF_POOL_INFO *pCtxBufPool = NULL;
2597             status = kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo, ENGINE_INFO_TYPE_RUNLIST,
2598                 runlistId, ENGINE_INFO_TYPE_RM_ENGINE_TYPE, (NvU32 *)&rmEngineType);
2599             if (status != NV_OK)
2600             {
2601                 NV_PRINTF(LEVEL_ERROR,
2602                           "Failed to translate runlistId 0x%x to NV2080 engine type\n", runlistId);
2603                 DBG_BREAKPOINT();
2604                 goto failed;
2605             }
2606             status = ctxBufPoolGetGlobalPool(pGpu, CTX_BUF_ID_RUNLIST, rmEngineType, &pCtxBufPool);
2607             if (status != NV_OK)
2608             {
2609                 NV_PRINTF(LEVEL_ERROR,
2610                           "Failed to get ctx buf pool for engine type 0x%x (0x%x)\n",
2611                           gpuGetNv2080EngineType(rmEngineType), rmEngineType);
2612                 DBG_BREAKPOINT();
2613                 goto failed;
2614             }
2615             status = memdescSetCtxBufPool(ppMemDesc[counter], pCtxBufPool);
2616             if (status != NV_OK)
2617             {
2618                 NV_PRINTF(LEVEL_ERROR,
2619                           "Failed to set ctx buf pool for runlistId 0x%x\n", runlistId);
2620                 DBG_BREAKPOINT();
2621                 goto failed;
2622             }
2623         }
2624 
2625         status = memdescAlloc(ppMemDesc[counter]);
2626         if (status != NV_OK)
2627         {
2628             NV_PRINTF(LEVEL_ERROR, "Runlist buffer mem alloc failed 0x%08x\n",
2629                       status);
2630             DBG_BREAKPOINT();
2631             goto failed;
2632         }
2633     }
2634 
2635     return NV_OK;
2636 
2637 failed:
2638     for (counter = 0; counter < NUM_BUFFERS_PER_RUNLIST; counter++)
2639     {
2640         if (ppMemDesc[counter])
2641         {
2642             memdescFree(ppMemDesc[counter]);
2643             memdescDestroy(ppMemDesc[counter]);
2644             ppMemDesc[counter] = NULL;
2645         }
2646     }
2647     return status;
2648 }
2649 
2650 NvU32
2651 kfifoGetMaxSubcontextFromGr_KERNEL
2652 (
2653     OBJGPU *pGpu,
2654     KernelFifo *pKernelFifo
2655 )
2656 {
2657     KernelGraphicsManager *pKernelGraphicsManager = GPU_GET_KERNEL_GRAPHICS_MANAGER(pGpu);
2658 
2659     NV_ASSERT_OR_RETURN(pKernelGraphicsManager != NULL, 0);
2660     NV_ASSERT_OR_RETURN(pKernelGraphicsManager->legacyKgraphicsStaticInfo.bInitialized, 0);
2661     NV_ASSERT_OR_RETURN(pKernelGraphicsManager->legacyKgraphicsStaticInfo.pGrInfo != NULL, 0);
2662 
2663     return pKernelGraphicsManager->legacyKgraphicsStaticInfo.pGrInfo->infoList[NV0080_CTRL_GR_INFO_INDEX_MAX_SUBCONTEXT_COUNT].data;
2664 }
2665 
2666 NvU32
2667 kfifoReturnPushbufferCaps_IMPL
2668 (
2669     OBJGPU     *pGpu,
2670     KernelFifo *pKernelFifo
2671 )
2672 {
2673     NvU32 kfifoBitMask = 0;
2674 
2675     // PCI is always supported
2676     kfifoBitMask = PCI_PB_ALLOWED;
2677 
2678     if (!gpuIsUnifiedMemorySpaceEnabled(pGpu))
2679     {
2680         kfifoBitMask |= VID_PB_ALLOWED;
2681     }
2682 
2683     return kfifoBitMask;
2684 }
2685 
2686 /*!
2687  * @brief kfifoGetDeviceCaps
2688  *
2689  * This routine gets cap bits in unicast. If pbCapsInitialized is passed as
2690  * NV_FALSE, the caps will be copied into pKfifoCaps without OR/ANDING.
2691  * Otherwise, the caps bits for the current GPU will be ORed/ANDed together with
2692  * pKfifoCaps to create a single set of caps that accurately represents the
2693  * functionality of the device.
2694  */
2695 void kfifoGetDeviceCaps_IMPL
2696 (
2697     OBJGPU     *pGpu,
2698     KernelFifo *pKernelFifo,
2699     NvU8       *pKfifoCaps,
2700     NvBool      bCapsInitialized
2701 )
2702 {
2703     NvU8        tempCaps[NV0080_CTRL_FIFO_CAPS_TBL_SIZE];
2704     NvU8        temp;
2705     NvU32       kfifoBitMask;
2706 
2707     NV_ASSERT(!gpumgrGetBcEnabledStatus(pGpu));
2708 
2709     portMemSet(tempCaps, 0, NV0080_CTRL_FIFO_CAPS_TBL_SIZE);
2710 
2711     kfifoBitMask = kfifoReturnPushbufferCaps(pGpu, pKernelFifo);
2712 
2713     if (kfifoBitMask & PCI_PB_ALLOWED)
2714         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _SUPPORT_PCI_PB);
2715     if (kfifoBitMask & VID_PB_ALLOWED)
2716         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _SUPPORT_VID_PB);
2717 
2718     if ((IS_VIRTUAL(pGpu) || IS_GSP_CLIENT(pGpu)) &&
2719         !gpuIsPipelinedPteMemEnabled(pGpu))
2720         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _NO_PIPELINED_PTE_BLIT);
2721 
2722     if (kfifoIsUserdInSystemMemory(pKernelFifo))
2723         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _USERD_IN_SYSMEM);
2724 
2725     if (kfifoIsUserdMapDmaSupported(pKernelFifo))
2726         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _GPU_MAP_CHANNEL);
2727 
2728     if (kfifoHostHasLbOverflow(pKernelFifo))
2729         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _HAS_HOST_LB_OVERFLOW_BUG_1667921);
2730 
2731     if (kfifoIsSubcontextSupported(pKernelFifo))
2732         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _MULTI_VAS_PER_CHANGRP);
2733 
2734     if (kfifoIsWddmInterleavingPolicyEnabled(pKernelFifo))
2735         RMCTRL_SET_CAP(tempCaps, NV0080_CTRL_FIFO_CAPS, _SUPPORT_WDDM_INTERLEAVING);
2736 
2737     // if this is the first GPU in the device, then start with it's caps
2738     if (bCapsInitialized == NV_FALSE)
2739     {
2740         portMemCopy(pKfifoCaps, NV0080_CTRL_FIFO_CAPS_TBL_SIZE,
2741                     tempCaps, NV0080_CTRL_FIFO_CAPS_TBL_SIZE);
2742         return;
2743     }
2744 
2745     RMCTRL_AND_CAP(pKfifoCaps, tempCaps, temp,
2746                    NV0080_CTRL_FIFO_CAPS, _SUPPORT_PCI_PB);
2747     RMCTRL_AND_CAP(pKfifoCaps, tempCaps, temp,
2748                    NV0080_CTRL_FIFO_CAPS, _SUPPORT_VID_PB);
2749     RMCTRL_AND_CAP(pKfifoCaps, tempCaps, temp,
2750                    NV0080_CTRL_FIFO_CAPS, _GPU_MAP_CHANNEL);
2751 
2752     RMCTRL_OR_CAP(pKfifoCaps, tempCaps, temp,
2753                    NV0080_CTRL_FIFO_CAPS, _MULTI_VAS_PER_CHANGRP);
2754 
2755     RMCTRL_OR_CAP(pKfifoCaps, tempCaps, temp,
2756                    NV0080_CTRL_FIFO_CAPS, _HAS_HOST_LB_OVERFLOW_BUG_1667921);
2757 
2758     RMCTRL_OR_CAP(pKfifoCaps, tempCaps, temp,
2759                    NV0080_CTRL_FIFO_CAPS, _SUPPORT_WDDM_INTERLEAVING);
2760     return;
2761 }
2762 
2763 /*!
2764  * @brief Add handlers for scheduling enable and/or disable.
2765  *
2766  * @param[in] pGpu                             OBJGPU pointer
2767  * @param[in] pKernelFifo                      KernelFifo pointer
2768  * @param[in] pPostSchedulingEnableHandler     No action if NULL
2769  * @param[in] pPostSchedulingEnableHandlerData Data to pass to
2770  *                                             @p pPostSchedulingEnableHandler
2771  * @param[in] pPreSchedulingDisableHandler     No action if NULL
2772  * @param[in] pPreSchedulingDisableHandlerData Data to pass to
2773  *                                             @p pPreSchedulingDisableHandler
2774  *
2775  * @returns  NV_OK if successfully processed both handlers
2776  *           NV_WARN_NOTHING_TO_DO if:  - Both handlers are NULL
2777  *                                      - Both handlers are already installed
2778  *           NV_ERR_INVALID_STATE if one handler is already installed, but not both
2779  */
2780 NV_STATUS
2781 kfifoAddSchedulingHandler_IMPL
2782 (
2783     OBJGPU                 *pGpu,
2784     KernelFifo             *pKernelFifo,
2785     PFifoSchedulingHandler  pPostSchedulingEnableHandler,
2786     void                   *pPostSchedulingEnableHandlerData,
2787     PFifoSchedulingHandler  pPreSchedulingDisableHandler,
2788     void                   *pPreSchedulingDisableHandlerData
2789 )
2790 {
2791     FifoSchedulingHandlerEntry *pEntry;
2792     NvBool bPostHandlerAlreadyPresent = NV_FALSE;
2793     NvBool bPreHandlerAlreadyPresent = NV_FALSE;
2794     FifoSchedulingHandlerEntry postEntry;
2795     FifoSchedulingHandlerEntry preEntry;
2796 
2797     NV_CHECK_OR_RETURN(LEVEL_SILENT,
2798                        (pPostSchedulingEnableHandler != NULL) ||
2799                        (pPreSchedulingDisableHandler != NULL),
2800                        NV_WARN_NOTHING_TO_DO);
2801 
2802     // Check for already installed handler if non-NULL
2803     if (pPostSchedulingEnableHandler != NULL)
2804     {
2805         for (pEntry = listHead(&pKernelFifo->postSchedulingEnableHandlerList);
2806              pEntry != NULL;
2807              pEntry = listNext(&pKernelFifo->postSchedulingEnableHandlerList, pEntry))
2808         {
2809             if (pEntry->pCallback == pPostSchedulingEnableHandler &&
2810                 pEntry->pCallbackParam == pPostSchedulingEnableHandlerData)
2811             {
2812                 bPostHandlerAlreadyPresent = NV_TRUE;
2813                 break;
2814             }
2815         }
2816     }
2817 
2818     // Check for already installed handler if non-NULL
2819     if (pPreSchedulingDisableHandler != NULL)
2820     {
2821         for (pEntry = listHead(&pKernelFifo->preSchedulingDisableHandlerList);
2822              pEntry != NULL;
2823              pEntry = listNext(&pKernelFifo->preSchedulingDisableHandlerList, pEntry))
2824         {
2825             if (pEntry->pCallback == pPreSchedulingDisableHandler &&
2826                 pEntry->pCallbackParam == pPreSchedulingDisableHandlerData)
2827             {
2828                 bPreHandlerAlreadyPresent = NV_TRUE;
2829                 break;
2830             }
2831         }
2832     }
2833 
2834     //
2835     // If we are installing both handlers, and one is already present, but not
2836     // the other, we will do nothing, so assert loudly in that case
2837     //
2838     if ((pPostSchedulingEnableHandler != NULL) && (pPreSchedulingDisableHandler != NULL))
2839     {
2840         NV_ASSERT_OR_RETURN(!(bPostHandlerAlreadyPresent ^ bPreHandlerAlreadyPresent),
2841                             NV_ERR_INVALID_STATE);
2842     }
2843 
2844     // Return early unless all non-null handlers are not already installed
2845     NV_CHECK_OR_RETURN(LEVEL_SILENT,
2846                        !bPostHandlerAlreadyPresent && !bPreHandlerAlreadyPresent,
2847                        NV_WARN_NOTHING_TO_DO);
2848 
2849     // Add handler entry to list unless NULL
2850     if (pPostSchedulingEnableHandler != NULL)
2851     {
2852         postEntry.pCallback = pPostSchedulingEnableHandler;
2853         postEntry.pCallbackParam = pPostSchedulingEnableHandlerData;
2854         postEntry.bHandled = NV_FALSE;
2855         NV_ASSERT_OR_RETURN(listPrependValue(&pKernelFifo->postSchedulingEnableHandlerList, &postEntry),
2856                             NV_ERR_NO_MEMORY);
2857     }
2858 
2859     // Add handler entry to list unless NULL
2860     if (pPreSchedulingDisableHandler != NULL)
2861     {
2862         preEntry.pCallback = pPreSchedulingDisableHandler;
2863         preEntry.pCallbackParam = pPreSchedulingDisableHandlerData;
2864         preEntry.bHandled = NV_FALSE;
2865         NV_ASSERT_OR_RETURN(listPrependValue(&pKernelFifo->preSchedulingDisableHandlerList, &preEntry),
2866                             NV_ERR_NO_MEMORY);
2867     }
2868 
2869     return NV_OK;
2870 }
2871 
2872 /*!
2873  * @brief Remove handlers for scheduling enable and/or disable.
2874  *
2875  * @param[in] pGpu                             OBJGPU pointer
2876  * @param[in] pKernelFifo                      KernelFifo pointer
2877  * @param[in] pPostSchedulingEnableHandler     No action if NULL
2878  * @param[in] pPostSchedulingEnableHandlerData Data argument set for the
2879  *                                             handler.
2880  * @param[in] pPreSchedulingDisableHandler     No action if NULL
2881  * @param[in] pPreSchedulingDisableHandlerData Data argument set for the
2882  *                                             handler.
2883  */
2884 void
2885 kfifoRemoveSchedulingHandler_IMPL
2886 (
2887     OBJGPU                 *pGpu,
2888     KernelFifo             *pKernelFifo,
2889     PFifoSchedulingHandler  pPostSchedulingEnableHandler,
2890     void                   *pPostSchedulingEnableHandlerData,
2891     PFifoSchedulingHandler  pPreSchedulingDisableHandler,
2892     void                   *pPreSchedulingDisableHandlerData
2893 )
2894 {
2895     FifoSchedulingHandlerEntry *pEntry;
2896     FifoSchedulingHandlerEntry *pTemp;
2897 
2898     // Search for the post handler in the post handler list and remove it if present
2899     pEntry = listHead(&pKernelFifo->postSchedulingEnableHandlerList);
2900     while (pEntry != NULL)
2901     {
2902         pTemp = listNext(&pKernelFifo->postSchedulingEnableHandlerList, pEntry);
2903 
2904         if (pEntry->pCallback == pPostSchedulingEnableHandler &&
2905             pEntry->pCallbackParam == pPostSchedulingEnableHandlerData)
2906         {
2907             listRemove(&pKernelFifo->postSchedulingEnableHandlerList, pEntry);
2908         }
2909 
2910         pEntry = pTemp;
2911     }
2912 
2913     // Search for the pre handler in the pre handler list and remove it if present
2914     pEntry = listHead(&pKernelFifo->preSchedulingDisableHandlerList);
2915     while (pEntry != NULL)
2916     {
2917         pTemp = listNext(&pKernelFifo->preSchedulingDisableHandlerList, pEntry);
2918 
2919         if (pEntry->pCallback == pPreSchedulingDisableHandler &&
2920             pEntry->pCallbackParam == pPreSchedulingDisableHandlerData)
2921         {
2922             listRemove(&pKernelFifo->preSchedulingDisableHandlerList, pEntry);
2923         }
2924 
2925         pEntry = pTemp;
2926     }
2927 }
2928 
2929 
2930 /*!
2931  * @brief Notify handlers that scheduling has been enabled.
2932  *
2933  * @param[in]  pGpu          OBJGPU pointer
2934  * @param[in]  pKernelFifo   KernelFifo pointer
2935  *
2936  * @returns  NV_STATUS
2937  */
2938 NV_STATUS
2939 kfifoTriggerPostSchedulingEnableCallback_IMPL
2940 (
2941     OBJGPU     *pGpu,
2942     KernelFifo *pKernelFifo
2943 )
2944 {
2945     NV_STATUS status = NV_OK;
2946     FifoSchedulingHandlerEntry *pEntry;
2947     NvBool bRetry = NV_FALSE;
2948 
2949     for (pEntry = listHead(&pKernelFifo->postSchedulingEnableHandlerList);
2950          pEntry != NULL;
2951          pEntry = listNext(&pKernelFifo->postSchedulingEnableHandlerList, pEntry))
2952     {
2953         NV_ASSERT_OR_ELSE(pEntry->pCallback != NULL,
2954             status = NV_ERR_INVALID_STATE; break;);
2955 
2956         pEntry->bHandled = NV_FALSE;
2957         status = pEntry->pCallback(pGpu, pEntry->pCallbackParam);
2958 
2959         // Retry mechanism: Some callbacks depend on other callbacks in this list.
2960         bRetry = bRetry || (status == NV_WARN_MORE_PROCESSING_REQUIRED);
2961 
2962         if (status == NV_WARN_MORE_PROCESSING_REQUIRED)
2963             // Quash retry status
2964             status = NV_OK;
2965         else if (status == NV_OK)
2966             // Successfully handled, no need to retry
2967             pEntry->bHandled = NV_TRUE;
2968         else
2969             // Actual error, abort
2970             break;
2971     }
2972 
2973     // If we hit an actual error or completed everything successfully, return early.
2974     if ((status != NV_OK) || !bRetry)
2975         return status;
2976 
2977     // Second pass, retry anything that asked nicely to be deferred
2978     for (pEntry = listHead(&pKernelFifo->postSchedulingEnableHandlerList);
2979          pEntry != NULL;
2980          pEntry = listNext(&pKernelFifo->postSchedulingEnableHandlerList, pEntry))
2981     {
2982         NV_ASSERT_OR_ELSE(pEntry->pCallback != NULL,
2983             status = NV_ERR_INVALID_STATE; break;);
2984 
2985         // Skip anything that was completed successfully
2986         if (pEntry->bHandled)
2987             continue;
2988 
2989         NV_CHECK_OK_OR_ELSE(status, LEVEL_ERROR,
2990             pEntry->pCallback(pGpu, pEntry->pCallbackParam),
2991             break; );
2992     }
2993 
2994     return status;
2995 }
2996 
2997 
2998 /*!
2999  * @brief Notify handlers that scheduling will soon be disabled.
3000  *
3001  * @param[in]  pGpu          OBJGPU pointer
3002  * @param[in]  pKernelFifo   KernelFifo pointer
3003  *
3004  * @returns  NV_STATUS
3005  */
3006 NV_STATUS
3007 kfifoTriggerPreSchedulingDisableCallback_IMPL
3008 (
3009     OBJGPU     *pGpu,
3010     KernelFifo *pKernelFifo
3011 )
3012 {
3013     NV_STATUS status = NV_OK;
3014     FifoSchedulingHandlerEntry *pEntry;
3015     NvBool bRetry = NV_FALSE;
3016 
3017     // First pass
3018     for (pEntry = listHead(&pKernelFifo->preSchedulingDisableHandlerList);
3019          pEntry != NULL;
3020          pEntry = listNext(&pKernelFifo->preSchedulingDisableHandlerList, pEntry))
3021     {
3022         NV_ASSERT_OR_ELSE(pEntry->pCallback != NULL,
3023             status = NV_ERR_INVALID_STATE; break;);
3024 
3025         pEntry->bHandled = NV_FALSE;
3026         status = pEntry->pCallback(pGpu, pEntry->pCallbackParam);
3027 
3028         // Retry mechanism: Some callbacks depend on other callbacks in this list.
3029         bRetry = bRetry || (status == NV_WARN_MORE_PROCESSING_REQUIRED);
3030 
3031         if (status == NV_WARN_MORE_PROCESSING_REQUIRED)
3032             // Quash retry status
3033             status = NV_OK;
3034         else if (status == NV_OK)
3035             // Successfully handled, no need to retry
3036             pEntry->bHandled = NV_TRUE;
3037         else
3038             // Actual error, abort
3039             break;
3040     }
3041 
3042     // If we hit an actual error or completed everything successfully, return early.
3043     if ((status != NV_OK) || !bRetry)
3044         return status;
3045 
3046     // Second pass, retry anything that asked nicely to be deferred
3047     for (pEntry = listHead(&pKernelFifo->preSchedulingDisableHandlerList);
3048          pEntry != NULL;
3049          pEntry = listNext(&pKernelFifo->preSchedulingDisableHandlerList, pEntry))
3050     {
3051         NV_ASSERT_OR_ELSE(pEntry->pCallback != NULL,
3052             status = NV_ERR_INVALID_STATE; break;);
3053 
3054         // Skip anything that was completed successfully
3055         if (pEntry->bHandled)
3056             continue;
3057 
3058         NV_CHECK_OK_OR_ELSE(status, LEVEL_ERROR,
3059             pEntry->pCallback(pGpu, pEntry->pCallbackParam),
3060             break; );
3061     }
3062 
3063     return status;
3064 }
3065 
3066 /**
3067  * @brief Gets vChid corresponding to a sChid
3068  */
3069 NV_STATUS
3070 kfifoGetVChIdForSChId_FWCLIENT
3071 (
3072     OBJGPU     *pGpu,
3073     KernelFifo *pKernelFifo,
3074     NvU32       sChId,
3075     NvU32       gfid,
3076     NvU32       engineId,
3077     NvU32      *pVChid
3078 )
3079 {
3080     KERNEL_HOST_VGPU_DEVICE *pKernelHostVgpuDevice = NULL;
3081 
3082     NV_ASSERT_OR_RETURN(pVChid != NULL, NV_ERR_INVALID_ARGUMENT);
3083     *pVChid = sChId;
3084 
3085     NV_CHECK_OR_RETURN(LEVEL_INFO, IS_GFID_VF(gfid), NV_OK);
3086     NV_ASSERT_OK_OR_RETURN(vgpuGetCallingContextKernelHostVgpuDevice(pGpu, &pKernelHostVgpuDevice));
3087     NV_ASSERT_OR_RETURN(pKernelHostVgpuDevice->gfid == gfid, NV_ERR_INVALID_ARGUMENT);
3088     NV_ASSERT_OR_RETURN(engineId < (sizeof(pKernelHostVgpuDevice->chidOffset) / sizeof(pKernelHostVgpuDevice->chidOffset[0])),
3089                         NV_ERR_INVALID_STATE);
3090     NV_ASSERT_OR_RETURN(pKernelHostVgpuDevice->chidOffset[engineId] != 0, NV_ERR_INVALID_STATE);
3091 
3092     NV_ASSERT_OR_RETURN(sChId >= pKernelHostVgpuDevice->chidOffset[engineId], NV_ERR_INVALID_ARGUMENT);
3093 
3094     *pVChid = sChId - pKernelHostVgpuDevice->chidOffset[engineId];
3095 
3096     return NV_OK;
3097 }
3098 
3099 /**
3100  * @brief cache vChid <-> sChid offset
3101  */
3102 NV_STATUS
3103 kfifoSetChidOffset_IMPL
3104 (
3105     OBJGPU           *pGpu,
3106     KernelFifo       *pKernelFifo,
3107     CHID_MGR         *pChidMgr,
3108     NvU32             offset,
3109     NvU32             numChannels,
3110     NvU32             gfid,
3111     NvU32            *pChidOffset,
3112     NvU32            *pChannelCount,
3113     NvU32             hMigClient,
3114     NvU32             engineFifoListNumEntries,
3115     FIFO_ENGINE_LIST *pEngineFifoList
3116 )
3117 {
3118     NV_STATUS status = NV_OK;
3119     RM_ENGINE_TYPE *pEngineIds = NULL;
3120     NvU32 maxEngines = kfifoGetNumEngines_HAL(pGpu, pKernelFifo);
3121     NvU32 numEngines, i;
3122 
3123     NV_ASSERT_OR_RETURN(pChidMgr != NULL, NV_ERR_INVALID_ARGUMENT);
3124     NV_ASSERT_OR_RETURN(pChidOffset != NULL, NV_ERR_INVALID_ARGUMENT);
3125 
3126     pEngineIds = portMemAllocNonPaged(sizeof(RM_ENGINE_TYPE) * maxEngines);
3127     NV_ASSERT_OR_RETURN((pEngineIds != NULL), NV_ERR_NO_MEMORY);
3128     portMemSet(pEngineIds, 0, sizeof(RM_ENGINE_TYPE) * maxEngines);
3129 
3130     NV_ASSERT_OK_OR_GOTO(status, kfifoGetEngineListForRunlist(pGpu, pKernelFifo, pChidMgr->runlistId, pEngineIds, &numEngines), cleanup)
3131 
3132     for (i = 0; i < numEngines; i++)
3133     {
3134         NV_ASSERT_OR_ELSE(NV2080_ENGINE_TYPE_IS_VALID(pEngineIds[i]), status = NV_ERR_INVALID_STATE; goto cleanup);
3135         pChidOffset[pEngineIds[i]] = offset;
3136         pChannelCount[pEngineIds[i]] = numChannels;
3137     }
3138 
3139     NV_ASSERT_OK_OR_GOTO(status, kfifoProgramChIdTable_HAL(pGpu, pKernelFifo, pChidMgr, offset, numChannels, gfid,
3140                                                            hMigClient, engineFifoListNumEntries, pEngineFifoList), cleanup);
3141 
3142 cleanup:
3143 
3144     portMemFree(pEngineIds);
3145 
3146     return status;
3147 }
3148 
3149 /*
3150  * @brief Gets a list of engine ids that use this runlist
3151  *
3152  * @param[in]  runlistId           Runlist id
3153  * @param[out] pOutEngineIds       List of engineids
3154  * @param[out] pNumEngines         # of entries in pOutEngines
3155  */
3156 NV_STATUS
3157 kfifoGetEngineListForRunlist_IMPL
3158 (
3159     OBJGPU     *pGpu,
3160     KernelFifo *pKernelFifo,
3161     NvU32       runlistId,
3162     RM_ENGINE_TYPE *pOutEngineIds,
3163     NvU32      *pNumEngines
3164 )
3165 {
3166     NV_STATUS  status      = NV_OK;
3167     NvU32      numEngines  = kfifoGetNumEngines_HAL(pGpu, pKernelFifo);
3168     NvU32      i;
3169 
3170     // Sanity check the input
3171     NV_CHECK_OR_RETURN(LEVEL_ERROR, pOutEngineIds != NULL, NV_ERR_INVALID_ARGUMENT);
3172     NV_CHECK_OR_RETURN(LEVEL_ERROR, pNumEngines != NULL, NV_ERR_INVALID_ARGUMENT);
3173 
3174     *pNumEngines = 0;
3175     NV_PRINTF(LEVEL_INFO, "Engine list for runlistId 0x%x:\n", runlistId);
3176 
3177     for (i = 0; i < numEngines; i++)
3178     {
3179         RM_ENGINE_TYPE rmEngineType;
3180         NvU32 thisRunlistId;
3181 
3182         NV_ASSERT_OK_OR_GOTO(status,
3183                              kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo,
3184                                                      ENGINE_INFO_TYPE_INVALID,
3185                                                      i,
3186                                                      ENGINE_INFO_TYPE_RUNLIST,
3187                                                      &thisRunlistId), done);
3188         if (runlistId == thisRunlistId)
3189         {
3190             NV_ASSERT_OK_OR_GOTO(status,
3191                                  kfifoEngineInfoXlate_HAL(pGpu, pKernelFifo,
3192                                                          ENGINE_INFO_TYPE_INVALID,
3193                                                          i,
3194                                                          ENGINE_INFO_TYPE_RM_ENGINE_TYPE,
3195                                                          (NvU32 *)&rmEngineType), done);
3196             pOutEngineIds[(*pNumEngines)++] = rmEngineType;
3197 
3198             NV_PRINTF(LEVEL_INFO, "Engine name: %s\n",
3199                        kfifoGetEngineName_HAL(pKernelFifo, ENGINE_INFO_TYPE_RM_ENGINE_TYPE,
3200                                              (NvU32)rmEngineType));
3201         }
3202     }
3203 done:
3204     if ((status != NV_OK) && (*pNumEngines != 0))
3205     {
3206         portMemSet(pOutEngineIds, 0, sizeof(NvU32) * (*pNumEngines));
3207         *pNumEngines = 0;
3208     }
3209     return status;
3210 }
3211 
3212 /**
3213  * @brief Return bitmask of currently allocated channels for a given runlist
3214  *
3215  * @param[in] bitMaskSize @p pBitMask size in bytes
3216  *
3217  */
3218 NV_STATUS
3219 kfifoGetAllocatedChannelMask_IMPL
3220 (
3221     OBJGPU     *pGpu,
3222     KernelFifo *pKernelFifo,
3223     NvU32       runlistId,
3224     NvU32      *pBitMask,
3225     NvLength    bitMaskSize
3226 )
3227 {
3228     CHID_MGR *pChidMgr;
3229     NvU32     chId;
3230     NvU32     numChannels;
3231 
3232     NV_ASSERT(pBitMask != NULL);
3233     portMemSet(pBitMask, 0, bitMaskSize);
3234 
3235     NV_ASSERT_OR_RETURN(bitMaskSize % sizeof(NvU32) == 0,
3236                         NV_ERR_INVALID_ARGUMENT);
3237 
3238     if (!kfifoIsPerRunlistChramEnabled(pKernelFifo))
3239     {
3240         if (runlistId > 0)
3241         {
3242             return NV_ERR_OUT_OF_RANGE;
3243         }
3244         else
3245         {
3246             runlistId = CHIDMGR_RUNLIST_ID_LEGACY;
3247         }
3248     }
3249     else
3250     {
3251         if (!(runlistId < kfifoGetMaxNumRunlists_HAL(pGpu, pKernelFifo)))
3252         {
3253             return NV_ERR_OUT_OF_RANGE;
3254         }
3255     }
3256 
3257     pChidMgr = kfifoGetChidMgr(pGpu, pKernelFifo, runlistId);
3258     if (pChidMgr == NULL)
3259     {
3260         //
3261         // This runlist is not valid. This is not an error since it might be
3262         // possible for some runlists between [0, maxRunlists) to be invalid.
3263         // Simply return that it has no channels to simplify clients iterating
3264         // over all runlists.
3265         //
3266         numChannels = 0;
3267     }
3268     else
3269     {
3270         numChannels = kfifoChidMgrGetNumChannels(pGpu, pKernelFifo, pChidMgr);
3271     }
3272 
3273     if (((numChannels + 7) / 8) > bitMaskSize)
3274     {
3275         return NV_ERR_BUFFER_TOO_SMALL;
3276     }
3277 
3278     for (chId = 0; chId < numChannels; chId++)
3279     {
3280         KernelChannel *pKernelChannel;
3281         pKernelChannel = kfifoChidMgrGetKernelChannel(pGpu, pKernelFifo,
3282                                                       pChidMgr,
3283                                                       chId);
3284         if (pKernelChannel != NULL)
3285         {
3286             NV_BITMASK32_SET(pBitMask, chId);
3287         }
3288     }
3289 
3290     return NV_OK;
3291 }
3292 
3293 /*!
3294  * Get host channel class
3295  */
3296 NvU32
3297 kfifoGetChannelClassId_IMPL
3298 (
3299     OBJGPU     *pGpu,
3300     KernelFifo *pKernelFifo
3301 )
3302 {
3303     NvU32 numClasses;
3304     NvU32 *pClassList = NULL;
3305     CLI_CHANNEL_CLASS_INFO classInfo;
3306     NvU32 i;
3307     NvU32 class = 0;
3308 
3309     NV_ASSERT_OR_RETURN(NV_OK == gpuGetClassList(pGpu, &numClasses, NULL, ENG_KERNEL_FIFO), 0);
3310     NV_ASSERT_OR_RETURN(numClasses > 0, 0);
3311     pClassList = portMemAllocNonPaged(sizeof(NvU32) * numClasses);
3312     NV_ASSERT_OR_RETURN((pClassList != NULL), 0);
3313 
3314     if (NV_OK == gpuGetClassList(pGpu, &numClasses, pClassList, ENG_KERNEL_FIFO))
3315     {
3316         for (i = 0; i < numClasses; i++)
3317         {
3318             if (pClassList[i] == PHYSICAL_CHANNEL_GPFIFO)
3319             {
3320                 // Skip the physical channel class
3321                 continue;
3322             }
3323             CliGetChannelClassInfo(pClassList[i], &classInfo);
3324             if (classInfo.classType == CHANNEL_CLASS_TYPE_GPFIFO)
3325                 class = NV_MAX(class, pClassList[i]);
3326         }
3327     }
3328 
3329     NV_ASSERT(class);
3330     portMemFree(pClassList);
3331     return class;
3332 }
3333 
3334 /**
3335  * @brief Return the FIFO_GUEST_ENGINE_TABLE
3336  *
3337  * @param[out]  pEngLookupTblSize   To get the table size
3338  *
3339  * @return a pointer to FIFO_GUEST_ENGINE_TABLE
3340  *
3341  */
3342 const FIFO_GUEST_ENGINE_TABLE *
3343 kfifoGetGuestEngineLookupTable_IMPL
3344 (
3345     NvU32 *pEngLookupTblSize
3346 )
3347 {
3348     //
3349     // This table is used for a guest RM to reconstruct the engine list data
3350     // received from the host RM. The host and guest RM can be running on
3351     // different versions.
3352     //
3353     // This table does not need to be HALified. It need to match NV2080_ENGINE_TYPE
3354     // defined in cl2080_notification.h
3355     //
3356     const static FIFO_GUEST_ENGINE_TABLE guestEngineLookupTable[] =
3357     {
3358         // nv2080EngineType             mcIdx
3359         {NV2080_ENGINE_TYPE_GR0,        MC_ENGINE_IDX_GR0},
3360         {NV2080_ENGINE_TYPE_GR1,        MC_ENGINE_IDX_GR1},
3361         {NV2080_ENGINE_TYPE_GR2,        MC_ENGINE_IDX_GR2},
3362         {NV2080_ENGINE_TYPE_GR3,        MC_ENGINE_IDX_GR3},
3363         {NV2080_ENGINE_TYPE_GR4,        MC_ENGINE_IDX_GR4},
3364         {NV2080_ENGINE_TYPE_GR5,        MC_ENGINE_IDX_GR5},
3365         {NV2080_ENGINE_TYPE_GR6,        MC_ENGINE_IDX_GR6},
3366         {NV2080_ENGINE_TYPE_GR7,        MC_ENGINE_IDX_GR7},
3367         {NV2080_ENGINE_TYPE_COPY0,      MC_ENGINE_IDX_CE0},
3368         {NV2080_ENGINE_TYPE_COPY1,      MC_ENGINE_IDX_CE1},
3369         {NV2080_ENGINE_TYPE_COPY2,      MC_ENGINE_IDX_CE2},
3370         {NV2080_ENGINE_TYPE_COPY3,      MC_ENGINE_IDX_CE3},
3371         {NV2080_ENGINE_TYPE_COPY4,      MC_ENGINE_IDX_CE4},
3372         {NV2080_ENGINE_TYPE_COPY5,      MC_ENGINE_IDX_CE5},
3373         {NV2080_ENGINE_TYPE_COPY6,      MC_ENGINE_IDX_CE6},
3374         {NV2080_ENGINE_TYPE_COPY7,      MC_ENGINE_IDX_CE7},
3375         {NV2080_ENGINE_TYPE_COPY8,      MC_ENGINE_IDX_CE8},
3376         {NV2080_ENGINE_TYPE_COPY9,      MC_ENGINE_IDX_CE9},
3377         {NV2080_ENGINE_TYPE_NVENC0,     MC_ENGINE_IDX_MSENC},
3378         {NV2080_ENGINE_TYPE_NVENC1,     MC_ENGINE_IDX_MSENC1},
3379         {NV2080_ENGINE_TYPE_NVENC2,     MC_ENGINE_IDX_MSENC2},
3380         {NV2080_ENGINE_TYPE_NVDEC0,     MC_ENGINE_IDX_NVDEC0},
3381         {NV2080_ENGINE_TYPE_NVDEC1,     MC_ENGINE_IDX_NVDEC1},
3382         {NV2080_ENGINE_TYPE_NVDEC2,     MC_ENGINE_IDX_NVDEC2},
3383         {NV2080_ENGINE_TYPE_NVDEC3,     MC_ENGINE_IDX_NVDEC3},
3384         {NV2080_ENGINE_TYPE_NVDEC4,     MC_ENGINE_IDX_NVDEC4},
3385         {NV2080_ENGINE_TYPE_NVDEC5,     MC_ENGINE_IDX_NVDEC5},
3386         {NV2080_ENGINE_TYPE_NVDEC6,     MC_ENGINE_IDX_NVDEC6},
3387         {NV2080_ENGINE_TYPE_NVDEC7,     MC_ENGINE_IDX_NVDEC7},
3388         {NV2080_ENGINE_TYPE_SW,         MC_ENGINE_IDX_NULL},
3389         {NV2080_ENGINE_TYPE_SEC2,       MC_ENGINE_IDX_SEC2},
3390         {NV2080_ENGINE_TYPE_NVJPEG0,    MC_ENGINE_IDX_NVJPEG0},
3391         {NV2080_ENGINE_TYPE_NVJPEG1,    MC_ENGINE_IDX_NVJPEG1},
3392         {NV2080_ENGINE_TYPE_NVJPEG2,    MC_ENGINE_IDX_NVJPEG2},
3393         {NV2080_ENGINE_TYPE_NVJPEG3,    MC_ENGINE_IDX_NVJPEG3},
3394         {NV2080_ENGINE_TYPE_NVJPEG4,    MC_ENGINE_IDX_NVJPEG4},
3395         {NV2080_ENGINE_TYPE_NVJPEG5,    MC_ENGINE_IDX_NVJPEG5},
3396         {NV2080_ENGINE_TYPE_NVJPEG6,    MC_ENGINE_IDX_NVJPEG6},
3397         {NV2080_ENGINE_TYPE_NVJPEG7,    MC_ENGINE_IDX_NVJPEG7},
3398         {NV2080_ENGINE_TYPE_OFA,        MC_ENGINE_IDX_OFA0},
3399     };
3400 
3401     //
3402     // To trap NV2080_ENGINE_TYPE expansions.
3403     // Please update the table guestEngineLookupTable if this assertion is triggered.
3404     //
3405     ct_assert(NV2080_ENGINE_TYPE_LAST == 0x0000003e);
3406 
3407     *pEngLookupTblSize = NV_ARRAY_ELEMENTS(guestEngineLookupTable);
3408 
3409     return guestEngineLookupTable;
3410 };
3411 
3412 /**
3413  * @brief Fetch the maximum number of secure channels supported by SEC2
3414  *        and secure CEs when confidential compute is enabled
3415  *
3416  */
3417 NV_STATUS
3418 kfifoGetMaxSecureChannels_KERNEL
3419 (
3420     OBJGPU     *pGpu,
3421     KernelFifo *pKernelFifo
3422 )
3423 {
3424     RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
3425     NV2080_CTRL_INTERNAL_FIFO_GET_NUM_SECURE_CHANNELS_PARAMS numSecureChannelsParams = {0};
3426 
3427     NV_ASSERT_OK_OR_RETURN(
3428         pRmApi->Control(pRmApi,
3429                         pGpu->hInternalClient,
3430                         pGpu->hInternalSubdevice,
3431                         NV2080_CTRL_CMD_INTERNAL_FIFO_GET_NUM_SECURE_CHANNELS,
3432                         &numSecureChannelsParams,
3433                         sizeof(numSecureChannelsParams)));
3434 
3435     pKernelFifo->maxSec2SecureChannels = numSecureChannelsParams.maxSec2SecureChannels;
3436     pKernelFifo->maxCeSecureChannels = numSecureChannelsParams.maxCeSecureChannels;
3437 
3438     return NV_OK;
3439 }
3440