1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2018-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 /******************************************************************************
25  *
26  *   Description:
27  *       This file contains the functions managing GPU instance subscriptions
28  *
29  *****************************************************************************/
30 
31 // FIXME XXX
32 #define NVOC_KERNEL_GRAPHICS_MANAGER_H_PRIVATE_ACCESS_ALLOWED
33 #define NVOC_COMPUTE_INSTANCE_SUBSCRIPTION_H_PRIVATE_ACCESS_ALLOWED
34 
35 #define NVOC_GPU_INSTANCE_SUBSCRIPTION_H_PRIVATE_ACCESS_ALLOWED
36 
37 #include "core/core.h"
38 #include "core/system.h"
39 #include "gpu/gpu.h"
40 #include "os/os.h"
41 #include "gpu/device/device.h"
42 #include "gpu/subdevice/subdevice.h"
43 #include "virtualization/hypervisor/hypervisor.h"
44 
45 #include "kernel/gpu/mig_mgr/gpu_instance_subscription.h"
46 #include "kernel/gpu/mig_mgr/compute_instance_subscription.h"
47 #include "kernel/gpu/mig_mgr/kernel_mig_manager.h"
48 
49 #include "ctrl/ctrlc637.h"
50 #include "core/locks.h"
51 #include "rmapi/rs_utils.h"
52 #include "gpu/gpu_uuid.h"
53 #include "vgpu/rpc.h"
54 #include "rmapi/control.h"
55 
56 static inline NvBool
57 _gisubscriptionClientSharesVASCrossPartition
58 (
59     GPUInstanceSubscription *pGPUInstanceSubscription,
60     CALL_CONTEXT *pCallContext,
61     NvU32 targetedSwizzId
62 )
63 {
64     NV_STATUS status = NV_OK;
65     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
66     KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
67     RsResourceRef *pDeviceRef;
68     Device *pDevice;
69     MIG_INSTANCE_REF shareRef;
70 
71     NV_ASSERT_OR_RETURN(pGPUInstanceSubscription != NULL, NV_TRUE);
72 
73     NV_ASSERT_OK(
74         refFindAncestorOfType(pCallContext->pResourceRef,
75                               classId(Device), &pDeviceRef));
76     pDevice = dynamicCast(pDeviceRef->pResource, Device);
77 
78     if (pDevice->hClientShare == NV01_NULL_OBJECT)
79     {
80         // NULL Client Share : Legacy Global VASpace. This is cross-GPU-instance.
81         return NV_TRUE;
82     }
83     else if (pDevice->hClientShare == pCallContext->pClient->hClient)
84     {
85         // Same Client Share : Self Scoped VASpace. This is not cross-GPU-instance.
86         return NV_FALSE;
87     }
88 
89     //
90     // Different Client Share. Device default VASpace is shared between this
91     // client and hClientShare. The VAS is cross-GPU-instance if the sharing client
92     // is subscribed to a different GPU instance than the subscription request, or
93     // if the sharing client isn't subscribed to any GPU instance.
94     //
95     status = kmigmgrGetInstanceRefFromClient(pGpu, pKernelMIGManager,
96                                              pDevice->hClientShare,
97                                              &shareRef);
98 
99 
100     return (status != NV_OK) || (shareRef.pKernelMIGGpuInstance->swizzId != targetedSwizzId);
101 }
102 
103 NV_STATUS
104 gisubscriptionConstruct_IMPL
105 (
106     GPUInstanceSubscription      *pGPUInstanceSubscription,
107     CALL_CONTEXT                 *pCallContext,
108     RS_RES_ALLOC_PARAMS_INTERNAL *pRmAllocParams
109 )
110 {
111     NVC637_ALLOCATION_PARAMETERS *pUserParams = pRmAllocParams->pAllocParams;
112     RsClient *pRsClient = pCallContext->pClient;
113     OBJGPU *pGpu;
114     KernelMIGManager *pKernelMIGManager;
115     NvU32 swizzId;
116     NV_STATUS status;
117 
118     pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
119 
120     osRmCapInitDescriptor(&pGPUInstanceSubscription->dupedCapDescriptor);
121 
122     if (RS_IS_COPY_CTOR(pRmAllocParams))
123         return gisubscriptionCopyConstruct_IMPL(pGPUInstanceSubscription, pCallContext, pRmAllocParams);
124 
125     pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
126     swizzId = pUserParams->swizzId;
127 
128     if (!IS_MIG_ENABLED(pGpu))
129     {
130         NV_ASSERT_FAILED("Subscription failed: MIG not enabled\n");
131         return NV_ERR_NOT_SUPPORTED;
132     }
133 
134     //
135     // Disable RMCTRL Cache before subscribe to GPU instance.
136     // RMCTRL-CACHE-TODO: remove the workaround when CORERM-5016 is done.
137     //
138     rmapiControlCacheSetMode(NV0000_CTRL_SYSTEM_RMCTRL_CACHE_MODE_CTRL_MODE_DISABLE);
139 
140     //
141     // Root-SwizzID is a special swizzID which doesn't have any GPU instance
142     // associated with it. It can be subscribed to even without GPU instances
143     //
144     if (swizzId == NVC637_DEVICE_PROFILING_SWIZZID)
145     {
146         // Check if this is a root-client or un-privileged device profiling is allowed
147         if (gpuIsRmProfilingPrivileged(pGpu) &&
148             !rmclientIsAdminByHandle(pRmAllocParams->hClient, pCallContext->secInfo.privLevel))
149         {
150             return NV_ERR_INSUFFICIENT_PERMISSIONS;
151         }
152 
153         if (kmigmgrIsDeviceProfilingInUse(pGpu, pKernelMIGManager))
154         {
155             // Only one DeviceProfiling session is allowed to be used with-in a system
156             NV_PRINTF(LEVEL_ERROR,
157                       "Subscription failed: Device-Level-Monitoring already in use\n");
158             return NV_ERR_INVALID_STATE;
159         }
160 
161         // Mark the root swizzID in use and return
162         NV_ASSERT_OK_OR_RETURN(kmigmgrSetDeviceProfilingInUse(pGpu, pKernelMIGManager));
163         pGPUInstanceSubscription->bDeviceProfiling = NV_TRUE;
164         goto done;
165     }
166     else
167     {
168         pGPUInstanceSubscription->bDeviceProfiling = NV_FALSE;
169     }
170 
171     if (!IS_MIG_IN_USE(pGpu))
172     {
173         NV_ASSERT_FAILED("Subscription failed: MIG GPU instancing not done\n");
174         return NV_ERR_NOT_SUPPORTED;
175     }
176 
177     if (!kmigmgrIsSwizzIdInUse(pGpu, pKernelMIGManager, swizzId))
178     {
179         NV_PRINTF(LEVEL_ERROR,
180                   "Subscription failed: swizzid 0x%0x doesn't exist!\n",
181                   swizzId);
182         return NV_ERR_INVALID_ARGUMENT;
183     }
184 
185     if (_gisubscriptionClientSharesVASCrossPartition(pGPUInstanceSubscription, pCallContext, swizzId))
186     {
187         NV_PRINTF(LEVEL_ERROR,
188                   "Subscription failed: Client shares VAS with client not subscribed to target GPU instance!\n");
189         return NV_ERR_STATE_IN_USE;
190     }
191 
192     NV_ASSERT_OK_OR_RETURN(
193         kmigmgrGetGPUInstanceInfo(pGpu, pKernelMIGManager, swizzId,
194                                   &pGPUInstanceSubscription->pKernelMIGGpuInstance));
195 
196     // For now skip kernel clients, such as UVM, until Bug 2729768 is fixed.
197     if (pRsClient->type == CLIENT_TYPE_USER)
198     {
199         status = osRmCapAcquire(pGPUInstanceSubscription->pKernelMIGGpuInstance->pOsRmCaps,
200                                 NV_RM_CAP_SMC_PARTITION_ACCESS,
201                                 pUserParams->capDescriptor,
202                                 &pGPUInstanceSubscription->dupedCapDescriptor);
203         if ((status != NV_ERR_NOT_SUPPORTED) && (status != NV_OK))
204         {
205             NV_PRINTF(LEVEL_ERROR,
206                       "Capability validation failed: swizzid 0x%0x!\n",
207                       swizzId);
208             return status;
209         }
210     }
211 
212     status = kmigmgrIncRefCount(pGPUInstanceSubscription->pKernelMIGGpuInstance->pShare);
213     if (status != NV_OK)
214     {
215         NV_PRINTF(LEVEL_ERROR,
216                   "GPU instance ref-counting failed: swizzid 0x%0x!\n",
217                   swizzId);
218         goto cleanup_duped_desc;
219     }
220 
221 done:
222     NV_PRINTF(LEVEL_INFO, "Client 0x%x subscribed to swizzid 0x%0x.\n",
223               pRmAllocParams->hClient, swizzId);
224 
225     return NV_OK;
226 
227 cleanup_duped_desc:
228     osRmCapRelease(pGPUInstanceSubscription->dupedCapDescriptor);
229 
230     return status;
231 }
232 
233 NV_STATUS
234 gisubscriptionCopyConstruct_IMPL
235 (
236     GPUInstanceSubscription *pGPUInstanceSubscription,
237     CALL_CONTEXT *pCallContext,
238     RS_RES_ALLOC_PARAMS_INTERNAL *pParams
239 )
240 {
241     RsResourceRef *pSrcRef = pParams->pSrcRef;
242     GPUInstanceSubscription *pGPUInstanceSubscriptionSrc = dynamicCast(pSrcRef->pResource, GPUInstanceSubscription);
243     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
244 
245     {
246         // non kernel clients are not allowed to dup GPU instances
247         NV_CHECK_OR_RETURN(LEVEL_SILENT, pCallContext->secInfo.privLevel >= RS_PRIV_LEVEL_KERNEL,
248                            NV_ERR_NOT_SUPPORTED);
249     }
250 
251     if (pGPUInstanceSubscriptionSrc->bDeviceProfiling)
252     {
253         // Duping of root-swizzId is not allowed
254         NV_PRINTF(LEVEL_ERROR,
255                       "Subscription failed: Duping not allowed for Device-level-SwizzId\n");
256         return NV_ERR_NOT_SUPPORTED;
257     }
258 
259     if (IS_VIRTUAL_WITH_SRIOV(pGpu) || IS_GSP_CLIENT(pGpu))
260     {
261         RsResourceRef *pDstRef = pCallContext->pResourceRef;
262         NV_STATUS status = NV_OK;
263 
264         NV_RM_RPC_DUP_OBJECT(pGpu,
265             pCallContext->pClient->hClient,
266             pDstRef->pParentRef->hResource,
267             pDstRef->hResource,
268             pParams->pSrcClient->hClient,
269             pSrcRef->hResource,
270             0, NV_TRUE, // Send RPC for object free
271             pDstRef, status);
272         NV_CHECK_OK_OR_RETURN(LEVEL_ERROR, status);
273     }
274 
275     pGPUInstanceSubscription->pKernelMIGGpuInstance = pGPUInstanceSubscriptionSrc->pKernelMIGGpuInstance;
276     // TODO XXX tracking this to support CI subscription bypass path for UVM
277     pGPUInstanceSubscription->bIsDuped = NV_TRUE;
278 
279     NV_ASSERT_OK(
280         kmigmgrIncRefCount(pGPUInstanceSubscription->pKernelMIGGpuInstance->pShare));
281 
282     return NV_OK;
283 }
284 
285 void
286 gisubscriptionDestruct_IMPL
287 (
288     GPUInstanceSubscription *pGPUInstanceSubscription
289 )
290 {
291     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
292     CALL_CONTEXT *pCallContext;
293     RS_RES_FREE_PARAMS_INTERNAL *pParams;
294     KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
295 
296     resGetFreeParams(staticCast(pGPUInstanceSubscription, RsResource), &pCallContext, &pParams);
297 
298     if (pGPUInstanceSubscription->bDeviceProfiling)
299     {
300         kmigmgrClearDeviceProfilingInUse(pGpu, pKernelMIGManager);
301         pGPUInstanceSubscription->bDeviceProfiling = NV_FALSE;
302         return;
303     }
304 
305     NV_ASSERT_OK(
306         kmigmgrDecRefCount(pGPUInstanceSubscription->pKernelMIGGpuInstance->pShare));
307 
308     osRmCapRelease(pGPUInstanceSubscription->dupedCapDescriptor);
309 
310     gisubscriptionCleanupOnUnsubscribe(pCallContext);
311 
312     NV_PRINTF(LEVEL_INFO, "Client 0x%x unsubscribed from swizzid 0x%0x.\n",
313               RES_GET_CLIENT(pGPUInstanceSubscription)->hClient, pGPUInstanceSubscription->pKernelMIGGpuInstance->swizzId);
314 }
315 
316 NvBool
317 gisubscriptionIsDuped_IMPL
318 (
319     GPUInstanceSubscription *pGPUInstanceSubscription
320 )
321 {
322     return pGPUInstanceSubscription->bIsDuped;
323 }
324 
325 NV_STATUS
326 gisubscriptionGetGPUInstanceSubscription_IMPL
327 (
328     RsClient *pClient,
329     NvHandle  hParent,
330     GPUInstanceSubscription **ppGPUInstanceSubscription
331 )
332 {
333     RsResourceRef *pResourceRef;
334 
335     NV_ASSERT_OR_RETURN(NULL != ppGPUInstanceSubscription, NV_ERR_INVALID_ARGUMENT);
336 
337     pResourceRef = serverutilFindChildRefByType(pClient->hClient,
338                                                 hParent, classId(GPUInstanceSubscription),
339                                                 NV_TRUE);
340     if (pResourceRef == NULL)
341         return NV_ERR_OBJECT_NOT_FOUND;
342 
343     *ppGPUInstanceSubscription = dynamicCast(pResourceRef->pResource, GPUInstanceSubscription);
344 
345     return NV_OK;
346 }
347 
348 NvBool
349 gisubscriptionCanCopy_IMPL
350 (
351     GPUInstanceSubscription *pGPUInstanceSubscription
352 )
353 {
354     return NV_TRUE;
355 }
356 
357 //
358 // gisubscriptionCtrlCmdExecPartitionsCreate
359 //
360 // Lock Requirements:
361 //      Assert that API and GPUs lock held on entry
362 //
363 NV_STATUS
364 gisubscriptionCtrlCmdExecPartitionsCreate_IMPL
365 (
366     GPUInstanceSubscription *pGPUInstanceSubscription,
367     NVC637_CTRL_EXEC_PARTITIONS_CREATE_PARAMS *pParams
368 )
369 {
370     NV_STATUS status = NV_OK;
371     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
372     KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
373     KERNEL_MIG_GPU_INSTANCE *pKernelMIGGpuInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
374 
375     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner() && rmGpuLockIsOwner());
376 
377 {
378     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
379 
380     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
381 
382     if (!rmclientIsCapableOrAdminByHandle(RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription),
383                                           NV_RM_CAP_SYS_SMC_CONFIG,
384                                           pCallContext->secInfo.privLevel))
385     {
386         NV_PRINTF(LEVEL_ERROR, "Non-privileged context issued privileged cmd\n");
387         return NV_ERR_INSUFFICIENT_PERMISSIONS;
388     }
389 }
390 
391     NV_ASSERT_OR_RETURN(pGpu->getProperty(pGpu, PDB_PROP_GPU_MIG_SUPPORTED), NV_ERR_NOT_SUPPORTED);
392     NV_ASSERT_OR_RETURN(IS_MIG_IN_USE(pGpu), NV_ERR_INVALID_STATE);
393 
394     NV_CHECK_OR_RETURN(LEVEL_SILENT, (pParams->execPartCount <= NVC637_CTRL_MAX_EXEC_PARTITIONS),
395                      NV_ERR_INVALID_ARGUMENT);
396 
397     // Check for trivial arguments
398     NV_CHECK_OR_RETURN(LEVEL_SILENT, pParams->execPartCount > 0, NV_WARN_NOTHING_TO_DO);
399 
400     if (IS_VIRTUAL(pGpu) || IS_GSP_CLIENT(pGpu))
401     {
402         CALL_CONTEXT *pCallContext  = resservGetTlsCallContext();
403         RmCtrlParams *pRmCtrlParams = pCallContext->pControlParams;
404 
405         NV_RM_RPC_CONTROL(pGpu, pRmCtrlParams->hClient,
406                           pRmCtrlParams->hObject, pRmCtrlParams->cmd,
407                           pRmCtrlParams->pParams,
408                           pRmCtrlParams->paramsSize, status);
409 
410         // Only continue if execution partition creation succeeded in the host
411         NV_ASSERT_OK_OR_RETURN(status);
412     }
413 
414     if (!IS_GSP_CLIENT(pGpu))
415     {
416         KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS request =
417         {
418             .type = KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS_TYPE_REQUEST,
419             .inst.request.count = pParams->execPartCount,
420             .inst.request.pReqComputeInstanceInfo = pParams->execPartInfo,
421             .inst.request.requestFlags = pParams->flags
422         };
423 
424         if (!hypervisorIsVgxHyper())
425         {
426             request.inst.request.requestFlags = FLD_SET_DRF(C637_CTRL, _DMA_EXEC_PARTITIONS_CREATE_REQUEST, _WITH_PART_ID, _FALSE, request.inst.request.requestFlags);
427         }
428 
429         if (IS_VIRTUAL(pGpu))
430         {
431             status = kmigmgrCreateComputeInstances_HAL(pGpu, pKernelMIGManager, pKernelMIGGpuInstance,
432                                                        pParams->bQuery,
433                                                        request,
434                                                        pParams->execPartId,
435                                                        NV_TRUE /* create MIG compute instance capabilities */);
436         }
437         else
438         {
439             return NV_ERR_NOT_SUPPORTED;
440         }
441     }
442     else
443     {
444         NvU32 i;
445 
446         for (i = 0; i < pParams->execPartCount; i++)
447         {
448             RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
449             NVC637_CTRL_EXEC_PARTITIONS_IMPORT_EXPORT_PARAMS export;
450             GPUMGR_SAVE_COMPUTE_INSTANCE save;
451             KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS restore =
452             {
453                 .type = KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS_TYPE_RESTORE,
454                 .inst.restore.pComputeInstanceSave = &save,
455             };
456             portMemSet(&export, 0, sizeof(export));
457             export.id = pParams->execPartId[i];
458 
459             // Retrieve the CI state created by GSP-RM, then restore it to CPU-RM
460             NV_ASSERT_OK_OR_RETURN(
461                 pRmApi->Control(pRmApi,
462                                 pKernelMIGGpuInstance->instanceHandles.hClient,
463                                 pKernelMIGGpuInstance->instanceHandles.hSubscription,
464                                 NVC637_CTRL_CMD_EXEC_PARTITIONS_EXPORT,
465                                 &export,
466                                 sizeof(export)));
467 
468             portMemSet(&save, 0, sizeof(save));
469             save.bValid = NV_TRUE;
470             save.id = pParams->execPartId[i];
471             save.ciInfo = export.info;
472 
473             NV_ASSERT_OK_OR_RETURN(
474                 kmigmgrCreateComputeInstances_HAL(pGpu, pKernelMIGManager, pKernelMIGGpuInstance,
475                                                   NV_FALSE, restore, &pParams->execPartId[i], NV_TRUE));
476         }
477     }
478 
479     //
480     // Generate a subdevice event stating something has changed in GPU instance
481     // config. Clients currently do not care about changes and their scope
482     //
483     if (!pParams->bQuery)
484     {
485         gpuNotifySubDeviceEvent(pGpu, NV2080_NOTIFIERS_SMC_CONFIG_UPDATE, NULL,
486                                 0, 0, 0);
487     }
488 
489     return status;
490 }
491 
492 //
493 // gisubscriptionCtrlCmdExecPartitionsDelete
494 //
495 // Lock Requirements:
496 //      Assert that API and GPUs lock held on entry
497 //
498 NV_STATUS
499 gisubscriptionCtrlCmdExecPartitionsDelete_IMPL
500 (
501     GPUInstanceSubscription *pGPUInstanceSubscription,
502     NVC637_CTRL_EXEC_PARTITIONS_DELETE_PARAMS *pParams
503 )
504 {
505     NV_STATUS status = NV_OK;
506     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
507     KERNEL_MIG_GPU_INSTANCE *pKernelMIGGpuInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
508     NvU32 execPartIdx;
509 
510     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner() && rmGpuLockIsOwner());
511 
512 {
513     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
514 
515     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
516 
517     if (!rmclientIsCapableOrAdminByHandle(RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription),
518                                           NV_RM_CAP_SYS_SMC_CONFIG,
519                                           pCallContext->secInfo.privLevel))
520     {
521         NV_PRINTF(LEVEL_ERROR, "Non-privileged context issued privileged cmd\n");
522         return NV_ERR_INSUFFICIENT_PERMISSIONS;
523     }
524 }
525 
526     NV_ASSERT_OR_RETURN(pGpu->getProperty(pGpu, PDB_PROP_GPU_MIG_SUPPORTED),
527                         NV_ERR_NOT_SUPPORTED);
528 
529     NV_ASSERT_OR_RETURN(IS_MIG_IN_USE(pGpu), NV_ERR_INVALID_STATE);
530 
531     NV_CHECK_OR_RETURN(LEVEL_SILENT, pParams->execPartCount <= NVC637_CTRL_MAX_EXEC_PARTITIONS,
532                      NV_ERR_INVALID_ARGUMENT);
533 
534     // Check for trivial arguments
535     NV_CHECK_OR_RETURN(LEVEL_SILENT, pParams->execPartCount > 0, NV_WARN_NOTHING_TO_DO);
536 
537     // Check that the passed indices are valid compute instances
538     for (execPartIdx = 0; execPartIdx < pParams->execPartCount; ++execPartIdx)
539     {
540         NvU32 execPartId = pParams->execPartId[execPartIdx];
541         NV_CHECK_OR_RETURN(LEVEL_ERROR,
542             execPartId < KMIGMGR_MAX_COMPUTE_INSTANCES,
543             NV_ERR_INVALID_ARGUMENT);
544         NV_CHECK_OR_RETURN(LEVEL_ERROR,
545             pKernelMIGGpuInstance->MIGComputeInstance[execPartId].bValid,
546             NV_ERR_INVALID_ARGUMENT);
547     }
548 
549     for (execPartIdx = 0; execPartIdx < pParams->execPartCount; ++execPartIdx)
550     {
551         if (IS_VIRTUAL(pGpu) || IS_GSP_CLIENT(pGpu))
552         {
553             KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
554             NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
555                 kmigmgrDeleteComputeInstance(pGpu, pKernelMIGManager, pKernelMIGGpuInstance,
556                                              pParams->execPartId[execPartIdx],
557                                              NV_FALSE));
558         }
559         else
560         {
561             return NV_ERR_NOT_SUPPORTED;
562         }
563     }
564 
565     //
566     // Generate a subdevice event stating something has changed in GPU instance
567     // config. Clients currently do not care about changes and their scope
568     //
569     gpuNotifySubDeviceEvent(pGpu, NV2080_NOTIFIERS_SMC_CONFIG_UPDATE, NULL, 0, 0, 0);
570 
571     if (IS_VIRTUAL(pGpu) || IS_GSP_CLIENT(pGpu))
572     {
573         CALL_CONTEXT *pCallContext  = resservGetTlsCallContext();
574         RmCtrlParams *pRmCtrlParams = pCallContext->pControlParams;
575 
576         NV_RM_RPC_CONTROL(pGpu, pRmCtrlParams->hClient,
577                           pRmCtrlParams->hObject, pRmCtrlParams->cmd,
578                           pRmCtrlParams->pParams,
579                           pRmCtrlParams->paramsSize, status);
580 
581         NV_ASSERT_OK_OR_RETURN(status);
582     }
583 
584     return status;
585 }
586 
587 //
588 // gisubscriptionCtrlCmdExecPartitionsGet
589 //
590 // Lock Requirements:
591 //      Assert that API and GPUs lock held on entry
592 //
593 NV_STATUS
594 gisubscriptionCtrlCmdExecPartitionsGet_IMPL
595 (
596     GPUInstanceSubscription *pGPUInstanceSubscription,
597     NVC637_CTRL_EXEC_PARTITIONS_GET_PARAMS *pParams
598 )
599 {
600     NV_STATUS status = NV_OK;
601     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
602     ComputeInstanceSubscription *pComputeInstanceSubscription = NULL;
603     KERNEL_MIG_GPU_INSTANCE *pKernelMIGGpuInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
604     NvU32 ciIdx;
605     NvHandle hClient = RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription);
606     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
607     NvBool bEnumerateAll = NV_FALSE;
608 
609     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
610 
611     // Capability checks shouldn't be done on
612     if (!RMCFG_FEATURE_PLATFORM_GSP)
613     {
614         bEnumerateAll = rmclientIsCapableOrAdminByHandle(hClient,
615                                                          NV_RM_CAP_SYS_SMC_CONFIG,
616                                                          pCallContext->secInfo.privLevel);
617     }
618 
619     MIG_COMPUTE_INSTANCE *pTargetComputeInstanceInfo = NULL;
620 
621     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner() && rmGpuLockIsOwner());
622 
623     NV_ASSERT_OR_RETURN(pGpu->getProperty(pGpu, PDB_PROP_GPU_MIG_SUPPORTED),
624                         NV_ERR_NOT_SUPPORTED);
625 
626     NV_ASSERT_OR_RETURN(IS_MIG_IN_USE(pGpu), NV_ERR_INVALID_STATE);
627 
628     (void)cisubscriptionGetComputeInstanceSubscription(RES_GET_CLIENT(pGPUInstanceSubscription), RES_GET_HANDLE(pGPUInstanceSubscription), &pComputeInstanceSubscription);
629     if (pComputeInstanceSubscription != NULL)
630     {
631         pTargetComputeInstanceInfo = pComputeInstanceSubscription->pMIGComputeInstance;
632     }
633     else if (!bEnumerateAll)
634     {
635         return NV_ERR_INSUFFICIENT_PERMISSIONS;
636     }
637 
638     pParams->execPartCount = 0;
639     for (ciIdx = 0;
640          ciIdx < NV_ARRAY_ELEMENTS(pKernelMIGGpuInstance->MIGComputeInstance);
641          ++ciIdx)
642     {
643         NVC637_CTRL_EXEC_PARTITIONS_INFO *pOutInfo;
644         MIG_COMPUTE_INSTANCE *pMIGComputeInstance =
645             &pKernelMIGGpuInstance->MIGComputeInstance[ciIdx];
646 
647         if (!pMIGComputeInstance->bValid)
648             continue;
649 
650         if (!bEnumerateAll && (pMIGComputeInstance != pTargetComputeInstanceInfo))
651             continue;
652 
653         pParams->execPartId[pParams->execPartCount] = ciIdx;
654         pOutInfo = &pParams->execPartInfo[pParams->execPartCount];
655         ++pParams->execPartCount;
656 
657         pOutInfo->gpcCount = pMIGComputeInstance->resourceAllocation.gpcCount;
658         pOutInfo->gfxGpcCount = pMIGComputeInstance->resourceAllocation.gfxGpcCount;
659         pOutInfo->veidCount = pMIGComputeInstance->resourceAllocation.veidCount;
660         pOutInfo->ceCount = kmigmgrCountEnginesOfType(&pMIGComputeInstance->resourceAllocation.engines,
661                                                       RM_ENGINE_TYPE_COPY(0));
662         pOutInfo->nvEncCount = kmigmgrCountEnginesOfType(&pMIGComputeInstance->resourceAllocation.engines,
663                                                       RM_ENGINE_TYPE_NVENC(0));
664         pOutInfo->nvDecCount = kmigmgrCountEnginesOfType(&pMIGComputeInstance->resourceAllocation.engines,
665                                                       RM_ENGINE_TYPE_NVDEC(0));
666         pOutInfo->nvJpgCount = kmigmgrCountEnginesOfType(&pMIGComputeInstance->resourceAllocation.engines,
667                                                       RM_ENGINE_TYPE_NVJPG);
668         pOutInfo->ofaCount = kmigmgrCountEnginesOfType(&pMIGComputeInstance->resourceAllocation.engines,
669                                                       RM_ENGINE_TYPE_OFA);
670         pOutInfo->sharedEngFlag = pMIGComputeInstance->sharedEngFlag;
671         pOutInfo->veidStartOffset = pMIGComputeInstance->resourceAllocation.veidOffset;
672         pOutInfo->smCount = pMIGComputeInstance->resourceAllocation.smCount;
673         pOutInfo->computeSize = pMIGComputeInstance->computeSize;
674         pOutInfo->spanStart = pMIGComputeInstance->spanStart;
675     }
676 
677     return status;
678 }
679 
680 //
681 // gisubscriptionCtrlCmdExecPartitionsGetActiveIds
682 //
683 // Lock Requirements:
684 //      Assert that API and GPUs lock held on entry
685 //
686 NV_STATUS
687 gisubscriptionCtrlCmdExecPartitionsGetActiveIds_IMPL
688 (
689     GPUInstanceSubscription *pGPUInstanceSubscription,
690     NVC637_CTRL_EXEC_PARTITIONS_GET_ACTIVE_IDS_PARAMS *pParams
691 )
692 {
693     NV_STATUS status = NV_OK;
694     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
695     KERNEL_MIG_GPU_INSTANCE *pKernelMIGGpuInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
696     NvU32 ciIdx;
697 
698     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner() && rmGpuLockIsOwner());
699 
700     NV_ASSERT_OR_RETURN(pGpu->getProperty(pGpu, PDB_PROP_GPU_MIG_SUPPORTED),
701                         NV_ERR_NOT_SUPPORTED);
702 
703     NV_ASSERT_OR_RETURN(IS_MIG_IN_USE(pGpu), NV_ERR_INVALID_STATE);
704 
705     pParams->execPartCount = 0;
706     for (ciIdx = 0;
707          ciIdx < NV_ARRAY_ELEMENTS(pKernelMIGGpuInstance->MIGComputeInstance);
708          ++ciIdx)
709     {
710         MIG_COMPUTE_INSTANCE *pMIGComputeInstance =
711             &pKernelMIGGpuInstance->MIGComputeInstance[ciIdx];
712 
713         if (!pMIGComputeInstance->bValid)
714             continue;
715 
716         pParams->execPartId[pParams->execPartCount] = ciIdx;
717 
718         ct_assert(NV_UUID_LEN == NVC637_UUID_LEN);
719         ct_assert(NV_UUID_STR_LEN == NVC637_UUID_STR_LEN);
720 
721         nvGetSmcUuidString(&pMIGComputeInstance->uuid,
722                            pParams->execPartUuid[pParams->execPartCount].str);
723 
724         ++pParams->execPartCount;
725     }
726 
727     return status;
728 }
729 
730 NV_STATUS
731 gisubscriptionCtrlCmdExecPartitionsExport_IMPL
732 (
733     GPUInstanceSubscription *pGPUInstanceSubscription,
734     NVC637_CTRL_EXEC_PARTITIONS_IMPORT_EXPORT_PARAMS *pParams
735 )
736 {
737     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
738     KERNEL_MIG_GPU_INSTANCE *pGPUInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
739     MIG_COMPUTE_INSTANCE *pMIGComputeInstance;
740     NvU32 gpcIdx;
741 
742     // No partitions to export
743     if (!IS_MIG_IN_USE(pGpu))
744         return NV_ERR_NOT_SUPPORTED;
745 
746 {
747     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
748 
749     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
750 
751     // An unprivileged client has no use case for import/export
752     if (!rmclientIsCapableOrAdminByHandle(RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription),
753                                           NV_RM_CAP_SYS_SMC_CONFIG,
754                                           pCallContext->secInfo.privLevel))
755     {
756         return NV_ERR_INSUFFICIENT_PERMISSIONS;
757     }
758 }
759 
760     if (IS_VIRTUAL(pGpu))
761     {
762         // Guest RM does not support import/export
763         return NV_ERR_NOT_SUPPORTED;
764     }
765 
766     if (IS_GSP_CLIENT(pGpu))
767     {
768         CALL_CONTEXT *pCallContext  = resservGetTlsCallContext();
769         RmCtrlParams *pRmCtrlParams = pCallContext->pControlParams;
770         NV_STATUS status = NV_OK;
771 
772         NV_RM_RPC_CONTROL(pGpu,
773                           pRmCtrlParams->hClient,
774                           pRmCtrlParams->hObject,
775                           pRmCtrlParams->cmd,
776                           pRmCtrlParams->pParams,
777                           pRmCtrlParams->paramsSize,
778                           status);
779 
780         return status;
781     }
782 
783     if (pParams->id >= NV_ARRAY_ELEMENTS(pGPUInstance->MIGComputeInstance))
784         return NV_ERR_INVALID_ARGUMENT;
785 
786     if (!pGPUInstance->MIGComputeInstance[pParams->id].bValid)
787         return NV_ERR_OBJECT_NOT_FOUND;
788 
789     pMIGComputeInstance = &pGPUInstance->MIGComputeInstance[pParams->id];
790 
791     portMemCopy(pParams->info.uuid, sizeof(pParams->info.uuid),
792                 pMIGComputeInstance->uuid.uuid, sizeof(pMIGComputeInstance->uuid.uuid));
793     pParams->info.sharedEngFlags = pMIGComputeInstance->sharedEngFlag;
794     pParams->info.veidOffset     = pMIGComputeInstance->resourceAllocation.veidOffset;
795     pParams->info.veidCount      = pMIGComputeInstance->resourceAllocation.veidCount;
796     pParams->info.smCount        = pMIGComputeInstance->resourceAllocation.smCount;
797     pParams->info.spanStart      = pMIGComputeInstance->spanStart;
798     pParams->info.computeSize    = pMIGComputeInstance->computeSize;
799 
800     for (gpcIdx = 0; gpcIdx < pMIGComputeInstance->resourceAllocation.gpcCount; ++gpcIdx)
801     {
802          pParams->info.gpcMask |= NVBIT32(pMIGComputeInstance->resourceAllocation.gpcIds[gpcIdx]);
803     }
804     bitVectorToRaw(&pMIGComputeInstance->resourceAllocation.engines,
805                    pParams->info.enginesMask, sizeof(pParams->info.enginesMask));
806 
807     return NV_OK;
808 }
809 
810 NV_STATUS
811 gisubscriptionCtrlCmdExecPartitionsImport_IMPL
812 (
813     GPUInstanceSubscription *pGPUInstanceSubscription,
814     NVC637_CTRL_EXEC_PARTITIONS_IMPORT_EXPORT_PARAMS *pParams
815 )
816 {
817     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
818     KERNEL_MIG_GPU_INSTANCE *pGPUInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
819     NV_STATUS status = NV_OK;
820 
821     if (!pGpu->getProperty(pGpu, PDB_PROP_GPU_MIG_SUPPORTED))
822         return NV_ERR_NOT_SUPPORTED;
823 
824 {
825     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
826 
827     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
828 
829     // An unprivileged client has no use case for import/export
830     if (!rmclientIsCapableOrAdminByHandle(RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription),
831                                           NV_RM_CAP_SYS_SMC_CONFIG,
832                                           pCallContext->secInfo.privLevel))
833     {
834         return NV_ERR_INSUFFICIENT_PERMISSIONS;
835     }
836 }
837 
838     if (IS_VIRTUAL(pGpu))
839     {
840         // Guest RM does not support import/export
841         return NV_ERR_NOT_SUPPORTED;
842     }
843 
844     if (IS_GSP_CLIENT(pGpu))
845     {
846         CALL_CONTEXT *pCallContext  = resservGetTlsCallContext();
847         RmCtrlParams *pRmCtrlParams = pCallContext->pControlParams;
848 
849         NV_RM_RPC_CONTROL(pGpu,
850                           pRmCtrlParams->hClient,
851                           pRmCtrlParams->hObject,
852                           pRmCtrlParams->cmd,
853                           pRmCtrlParams->pParams,
854                           pRmCtrlParams->paramsSize,
855                           status);
856 
857         if (status != NV_OK)
858             return status;
859     }
860 
861     {
862         GPUMGR_SAVE_COMPUTE_INSTANCE save;
863         KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS restore =
864         {
865             .type = KMIGMGR_CREATE_COMPUTE_INSTANCE_PARAMS_TYPE_RESTORE,
866             .inst.restore.pComputeInstanceSave = &save,
867         };
868 
869         portMemSet(&save, 0, sizeof(save));
870         save.bValid = NV_TRUE;
871         save.id = pParams->id;
872         save.ciInfo = pParams->info;
873 
874         if (IS_GSP_CLIENT(pGpu))
875         {
876             KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
877             NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,
878                 kmigmgrCreateComputeInstances_HAL(pGpu, pKernelMIGManager,
879                  pGPUInstance, NV_FALSE, restore, &pParams->id, pParams->bCreateCap),
880                 cleanup_rpc);
881         }
882         else
883         {
884             return NV_ERR_NOT_SUPPORTED;
885         }
886     }
887 
888     return NV_OK;
889 
890 cleanup_rpc:
891     if (IS_GSP_CLIENT(pGpu))
892     {
893         NVC637_CTRL_EXEC_PARTITIONS_DELETE_PARAMS params;
894         RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
895 
896         portMemSet(&params, 0, sizeof(params));
897         params.execPartCount = 1;
898         params.execPartId[0] = pParams->id;
899 
900         NV_ASSERT_OK(
901             pRmApi->Control(pRmApi,
902                             RES_GET_CLIENT_HANDLE(pGPUInstanceSubscription),
903                             RES_GET_HANDLE(pGPUInstanceSubscription),
904                             NVC637_CTRL_CMD_EXEC_PARTITIONS_DELETE,
905                             &params,
906                             sizeof(params)));
907     }
908 
909     return status;
910 }
911 
912 /*!
913  * @brief Determines whether an object of the given class id is affected by
914  * gpu/compute instance subscription and should be automatically freed if a
915  * client unsubscribes from a gpu/compute instance.
916  */
917 NvBool
918 gisubscriptionShouldClassBeFreedOnUnsubscribe_IMPL
919 (
920     NvU32 internalClassId
921 )
922 {
923     NvBool bShouldFree = NV_TRUE;
924 
925     switch (internalClassId)
926     {
927         case (classId(Device)):
928             // fall-through
929         case (classId(Subdevice)):
930             // fall-through
931         case (classId(GPUInstanceSubscription)):
932             // fall-through
933         case (classId(ComputeInstanceSubscription)):
934             bShouldFree = NV_FALSE;
935             break;
936         default:
937             break;
938     }
939 
940     return bShouldFree;
941 }
942 
943 /*!
944  * @brief Automatically frees client resources which may be affected by
945  * subscription objects. This is intended to be called on unsubscription.
946  *
947  * @see gisubscriptionShouldClassBeFreedOnUnsubscribe
948  *
949  * @param[in] pCallContext         Call context of client to clean up
950  */
951 void
952 gisubscriptionCleanupOnUnsubscribe_IMPL
953 (
954     CALL_CONTEXT *pCallContext
955 )
956 {
957     RsResourceRef *pDeviceRef;
958     RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
959     RS_ITERATOR iter;
960     NvHandle *pHandles;
961     NvU32 handleCount;
962     NvU32 i;
963 
964     NV_ASSERT_OK(
965         refFindAncestorOfType(pCallContext->pResourceRef, classId(Device), &pDeviceRef));
966 
967     // Determine the number of handles we need to free
968     handleCount = 0;
969     iter = serverutilRefIter(pCallContext->pClient->hClient,
970                              pDeviceRef->hResource,
971                              0,
972                              RS_ITERATE_DESCENDANTS,
973                              NV_FALSE);
974     while (clientRefIterNext(iter.pClient, &iter))
975     {
976         RsResourceRef *pResourceRef = iter.pResourceRef;
977 
978         if (!gisubscriptionShouldClassBeFreedOnUnsubscribe(pResourceRef->internalClassId))
979             continue;
980 
981         ++handleCount;
982         NV_PRINTF(LEVEL_INFO,
983                   "Will be freeing resource class id 0x%x on unsubscription!\n",
984                   pResourceRef->internalClassId);
985     }
986 
987     // If we have nothing to free then bail early
988     if (handleCount == 0)
989         goto done;
990 
991     // Allocate an array large enough to store the handles we need to free
992     pHandles = portMemAllocNonPaged(handleCount * sizeof(*pHandles));
993     if (NULL == pHandles)
994     {
995         NV_ASSERT(0);
996         goto done;
997     }
998 
999     // Store the handles that we need to free
1000     i = 0;
1001     iter = serverutilRefIter(pCallContext->pClient->hClient,
1002                              pDeviceRef->hResource,
1003                              0,
1004                              RS_ITERATE_DESCENDANTS,
1005                              NV_FALSE);
1006     while (clientRefIterNext(iter.pClient, &iter))
1007     {
1008         RsResourceRef *pResourceRef = iter.pResourceRef;
1009 
1010         if (!gisubscriptionShouldClassBeFreedOnUnsubscribe(pResourceRef->internalClassId))
1011             continue;
1012 
1013         NV_ASSERT_OR_GOTO(i < handleCount, cleanup);
1014         pHandles[i++] = pResourceRef->hResource;
1015     }
1016 
1017     //
1018     // Free all of the handles we flagged for deletion.
1019     // Note - some of these resources will free other dependant resources, so
1020     // some of these free calls will do nothing. That's fine for our purposes.
1021     //
1022     NV_ASSERT_OR_GOTO(i == handleCount, cleanup);
1023     for (i = 0; i < handleCount; ++i)
1024         pRmApi->Free(pRmApi, pCallContext->pClient->hClient, pHandles[i]);
1025 
1026 cleanup:
1027     portMemFree(pHandles);
1028 
1029 done:
1030     return;
1031 }
1032 
1033 NV_STATUS
1034 gisubscriptionCtrlCmdExecPartitionsGetProfileCapacity_IMPL
1035 (
1036     GPUInstanceSubscription *pGPUInstanceSubscription,
1037     NVC637_CTRL_EXEC_PARTITIONS_GET_PROFILE_CAPACITY_PARAMS *pParams
1038 )
1039 {
1040     OBJGPU *pGpu = GPU_RES_GET_GPU(pGPUInstanceSubscription);
1041     KERNEL_MIG_GPU_INSTANCE *pKernelMIGGpuInstance = pGPUInstanceSubscription->pKernelMIGGpuInstance;
1042     KernelMIGManager *pKernelMIGManager = GPU_GET_KERNEL_MIG_MANAGER(pGpu);
1043     NvBool bCtsRequired = kmigmgrIsCTSAlignmentRequired(pGpu, pKernelMIGManager);
1044 
1045     NV_CHECK_OR_RETURN(LEVEL_ERROR,
1046         pParams->computeSize < NV2080_CTRL_GPU_PARTITION_FLAG_COMPUTE_SIZE__SIZE,
1047         NV_ERR_INVALID_ARGUMENT);
1048 
1049     if (bCtsRequired)
1050     {
1051         NV_RANGE totalRange = kmigmgrComputeProfileSizeToCTSIdRange(pParams->computeSize);
1052         NvU32 slotBasisComputeSize = kmigmgrSmallestComputeProfileSize(pGpu, pKernelMIGManager);
1053         NvU64 slotBasisMask;
1054         NvU64 validQueryMask;
1055         NvU64 inUseIdMask;
1056         NvU64 ctsId;
1057         NvU32 totalSpanCount;
1058         NvU32 availableSpanCount;
1059         NV_RANGE slotBasisIdRange;
1060 
1061         NV_CHECK_OR_RETURN(LEVEL_ERROR, slotBasisComputeSize != KMIGMGR_COMPUTE_SIZE_INVALID, NV_ERR_INVALID_STATE);
1062 
1063         slotBasisIdRange = kmigmgrComputeProfileSizeToCTSIdRange(slotBasisComputeSize);
1064 
1065         NV_CHECK_OR_RETURN(LEVEL_ERROR, !rangeIsEmpty(totalRange), NV_ERR_INVALID_ARGUMENT);
1066         NV_CHECK_OR_RETURN(LEVEL_ERROR, !rangeIsEmpty(slotBasisIdRange), NV_ERR_INVALID_ARGUMENT);
1067 
1068         slotBasisMask = DRF_SHIFTMASK64(slotBasisIdRange.hi:slotBasisIdRange.lo);
1069         validQueryMask = DRF_SHIFTMASK64(totalRange.hi:totalRange.lo) & pKernelMIGGpuInstance->pProfile->validCTSIdMask;
1070 
1071         // Find mask of un-usable IDs due to current in-use CTS Ids
1072         inUseIdMask = 0x0;
1073         FOR_EACH_INDEX_IN_MASK(64, ctsId, pKernelMIGGpuInstance->ctsIdsInUseMask)
1074         {
1075             NvU64 invalidMask;
1076 
1077             NV_ASSERT_OK(kmigmgrGetInvalidCTSIdMask(pGpu, pKernelMIGManager, ctsId, &invalidMask));
1078 
1079             inUseIdMask |= invalidMask;
1080         }
1081         FOR_EACH_INDEX_IN_MASK_END;
1082 
1083         //
1084         // The slot basis defines the smallest divison of the GPU instance.
1085         // CTS IDs from this range are used as a means to specify span placements
1086         // for compute profiles.
1087         //
1088         totalSpanCount = 0;
1089         availableSpanCount = 0;
1090 
1091         FOR_EACH_INDEX_IN_MASK(64, ctsId, validQueryMask)
1092         {
1093             NvU64 invalidMask;
1094 
1095             NV_ASSERT_OK(kmigmgrGetInvalidCTSIdMask(pGpu, pKernelMIGManager, ctsId, &invalidMask));
1096 
1097             invalidMask &= slotBasisMask;
1098             pParams->totalSpans[totalSpanCount].lo = portUtilCountTrailingZeros64(invalidMask) - slotBasisIdRange.lo;
1099             pParams->totalSpans[totalSpanCount].hi = nvPopCount64(invalidMask) + pParams->totalSpans[totalSpanCount].lo - 1;
1100 
1101             if (!(NVBIT64(ctsId) & inUseIdMask))
1102             {
1103                 pParams->availableSpans[availableSpanCount].lo = pParams->totalSpans[totalSpanCount].lo;
1104                 pParams->availableSpans[availableSpanCount].hi = pParams->totalSpans[totalSpanCount].hi;
1105                 availableSpanCount++;
1106             }
1107             totalSpanCount++;
1108         }
1109         FOR_EACH_INDEX_IN_MASK_END;
1110 
1111         pParams->totalSpansCount     = totalSpanCount;
1112         pParams->totalProfileCount   = totalSpanCount;
1113         pParams->availableSpansCount = availableSpanCount;
1114         pParams->profileCount        = availableSpanCount;
1115     }
1116     else
1117     {
1118         KernelGraphicsManager *pKernelGraphicsManager = GPU_GET_KERNEL_GRAPHICS_MANAGER(pGpu);
1119         NV2080_CTRL_INTERNAL_MIGMGR_COMPUTE_PROFILE profile;
1120         NvU64 veidMask;
1121         NvU32 GPUInstanceVeidEnd;
1122         NvU64 GPUInstanceVeidMask;
1123         NvU64 GPUInstanceFreeVeidMask;
1124         NvU64 GPUInstancePseudoMask;
1125         NvU32 availableSpanCount;
1126         NvU32 totalSpanCount;
1127         NvU32 veidStepSize;
1128         NvU32 veidSlotCount;
1129         NvU32 count;
1130         NvU32 i;
1131 
1132         NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
1133             kmigmgrGetComputeProfileFromSize(pGpu, pKernelMIGManager, pParams->computeSize, &profile));
1134         NV_ASSERT_OK_OR_RETURN(
1135             kgrmgrGetMaxVeidsPerGpc(pGpu, pKernelGraphicsManager, &veidStepSize));
1136 
1137         // Create a mask for VEIDs associated with this GPU instance
1138         veidMask = DRF_SHIFTMASK64(profile.veidCount - 1:0);
1139         GPUInstanceVeidEnd = pKernelMIGGpuInstance->resourceAllocation.veidOffset + pKernelMIGGpuInstance->resourceAllocation.veidCount - 1;
1140         GPUInstanceVeidMask = DRF_SHIFTMASK64(GPUInstanceVeidEnd:pKernelMIGGpuInstance->resourceAllocation.veidOffset);
1141         GPUInstanceFreeVeidMask = GPUInstanceVeidMask & ~pKernelGraphicsManager->veidInUseMask;
1142         GPUInstancePseudoMask = GPUInstanceFreeVeidMask;
1143         veidSlotCount = 0;
1144         availableSpanCount = 0;
1145         totalSpanCount = 0;
1146         count = 0;
1147         for (i = pKernelMIGGpuInstance->resourceAllocation.veidOffset; i < GPUInstanceVeidEnd; i += veidStepSize)
1148         {
1149             // Determine max correctly sized VEID segments
1150             if (((GPUInstanceFreeVeidMask >> i) & veidMask) == veidMask)
1151             {
1152                 pParams->availableSpans[availableSpanCount].lo = count;
1153                 pParams->availableSpans[availableSpanCount].hi = count + (profile.veidCount / veidStepSize) - 1;
1154                 availableSpanCount++;
1155             }
1156 
1157             // Determine max correctly sized VEID segments
1158             if (((GPUInstanceVeidMask >> i) & veidMask) == veidMask)
1159             {
1160                 pParams->totalSpans[totalSpanCount].lo = count;
1161                 pParams->totalSpans[totalSpanCount].hi = count + (profile.veidCount / veidStepSize) - 1;
1162                 totalSpanCount++;
1163             }
1164 
1165             // Determine max correctly sized VEID segments
1166             if (((GPUInstancePseudoMask >> i) & veidMask) == veidMask)
1167             {
1168                 veidSlotCount++;
1169                 GPUInstancePseudoMask &= ~(veidMask << i);
1170             }
1171             count++;
1172         }
1173         pParams->totalProfileCount = NV_MIN(pKernelMIGGpuInstance->pProfile->virtualGpcCount / profile.gpcCount,
1174                                                 pKernelMIGGpuInstance->pProfile->veidCount / profile.veidCount);
1175         pParams->totalSpansCount     = totalSpanCount;
1176         pParams->profileCount        = veidSlotCount;
1177         pParams->availableSpansCount = availableSpanCount;
1178     }
1179 
1180     return NV_OK;
1181 }
1182