1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 1993-2021 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 #include "nvrm_registry.h"
26 #include "rmapi/rmapi.h"
27 #include "entry_points.h"
28 #include "resserv/rs_server.h"
29 #include "rmapi/rs_utils.h"
30 #include "gpu/gpu_resource.h"
31 #include "gpu/device/device.h"
32 #include "core/locks.h"
33 #include "gpu/gpu.h"
34 #include "diagnostics/tracer.h"
35 #include "tls/tls.h"
36 #include "core/thread_state.h"
37 #include "gpu_mgr/gpu_mgr.h"
38 #include "resource_desc.h"
39 
40 typedef struct
41 {
42     PORT_RWLOCK *       pLock;
43     NvU64               threadId;
44     NvU64               timestamp;
45     LOCK_TRACE_INFO     traceInfo;
46     NvU64               tlsEntryId;
47 
48 } RMAPI_LOCK;
49 
50 RsServer          g_resServ;
51 static RM_API     g_RmApiList[RMAPI_TYPE_MAX];
52 static NvBool     g_bResServInit = NV_FALSE;
53 static RMAPI_LOCK g_RmApiLock;
54 
55 static void _rmapiInitInterface(RM_API *pRmApi, API_SECURITY_INFO *pDefaultSecurityInfo, NvBool bTlsInternal,
56                                 NvBool bApiLockInternal, NvBool bGpuLockInternal);
57 static NV_STATUS _rmapiLockAlloc(void);
58 static void _rmapiLockFree(void);
59 
60 // from rmapi_stubs.c
61 void rmapiInitStubInterface(RM_API *pRmApi);
62 
63 NV_STATUS
64 rmapiInitialize
65 (
66     void
67 )
68 {
69     NV_STATUS         status = NV_OK;
70     API_SECURITY_INFO secInfo = {0};
71 
72     NV_ASSERT(!g_bResServInit);
73 
74     status = _rmapiLockAlloc();
75 
76     if (status != NV_OK)
77     {
78         NV_PRINTF(LEVEL_ERROR, "*** Cannot allocate rmapi locks\n");
79         goto failed;
80     }
81 
82     status = rmapiControlCacheInit();
83     if (status != NV_OK)
84     {
85         NV_PRINTF(LEVEL_ERROR, "*** Cannot initialize rmapi cache\n");
86         goto failed_free_lock;
87     }
88 
89     RsResInfoInitialize();
90     status = serverConstruct(&g_resServ, RS_PRIV_LEVEL_HOST, 0);
91 
92     if (status != NV_OK)
93     {
94         NV_PRINTF(LEVEL_ERROR, "*** Cannot initialize resource server\n");
95         goto failed_free_cache;
96     }
97 
98     serverSetClientHandleBase(&g_resServ, RS_CLIENT_HANDLE_BASE);
99 
100     listInit(&g_clientListBehindGpusLock, g_resServ.pAllocator);
101     listInit(&g_userInfoList, g_resServ.pAllocator);
102     multimapInit(&g_osInfoList, g_resServ.pAllocator);
103 
104     secInfo.privLevel         = RS_PRIV_LEVEL_KERNEL;
105     secInfo.paramLocation     = PARAM_LOCATION_KERNEL;
106 
107     _rmapiInitInterface(&g_RmApiList[RMAPI_EXTERNAL],                NULL,     NV_FALSE /* bTlsInternal */,  NV_FALSE /* bApiLockInternal */, NV_FALSE /* bGpuLockInternal */);
108     _rmapiInitInterface(&g_RmApiList[RMAPI_EXTERNAL_KERNEL],         &secInfo, NV_FALSE /* bTlsInternal */,  NV_FALSE /* bApiLockInternal */, NV_FALSE /* bGpuLockInternal */);
109     _rmapiInitInterface(&g_RmApiList[RMAPI_MODS_LOCK_BYPASS],        &secInfo, NV_FALSE /* bTlsInternal */,  NV_TRUE  /* bApiLockInternal */, NV_TRUE  /* bGpuLockInternal */);
110     _rmapiInitInterface(&g_RmApiList[RMAPI_API_LOCK_INTERNAL],       &secInfo, NV_TRUE  /* bTlsInternal */,  NV_TRUE  /* bApiLockInternal */, NV_FALSE /* bGpuLockInternal */);
111     _rmapiInitInterface(&g_RmApiList[RMAPI_GPU_LOCK_INTERNAL],       &secInfo, NV_TRUE  /* bTlsInternal */,  NV_TRUE  /* bApiLockInternal */, NV_TRUE  /* bGpuLockInternal */);
112 
113     rmapiInitStubInterface(&g_RmApiList[RMAPI_STUBS]);
114 
115     g_bResServInit = NV_TRUE;
116 
117     return NV_OK;
118 
119 failed_free_cache:
120         rmapiControlCacheFree();
121 failed_free_lock:
122         _rmapiLockFree();
123 failed:
124     return status;
125 }
126 
127 void
128 rmapiShutdown
129 (
130     void
131 )
132 {
133     if (!g_bResServInit)
134         return;
135 
136     serverFreeDomain(&g_resServ, 0);
137     serverDestruct(&g_resServ);
138     _rmapiLockFree();
139 
140     rmapiControlCacheFree();
141 
142     g_bResServInit = NV_FALSE;
143 }
144 
145 static void
146 _rmapiInitInterface
147 (
148     RM_API            *pRmApi,
149     API_SECURITY_INFO *pDefaultSecInfo,
150     NvBool             bTlsInternal,
151     NvBool             bApiLockInternal,
152     NvBool             bGpuLockInternal
153 )
154 {
155     //
156     // Initialize to all stubs first, so any APIs not explicitly set here
157     // will return NV_ERR_NOT_SUPPORTED if called
158     //
159     rmapiInitStubInterface(pRmApi);
160 
161     //
162     // Init members
163     //
164     if (pDefaultSecInfo)
165         pRmApi->defaultSecInfo = *pDefaultSecInfo;
166 
167     pRmApi->bHasDefaultSecInfo = !!pDefaultSecInfo;
168     pRmApi->bTlsInternal = bTlsInternal;
169     pRmApi->bApiLockInternal = bApiLockInternal;
170     pRmApi->bRmSemaInternal = bApiLockInternal;
171     pRmApi->bGpuLockInternal = bGpuLockInternal;
172     pRmApi->pPrivateContext = NULL;
173 
174     //
175     // Init function pointers
176     //
177     pRmApi->Alloc = rmapiAlloc;
178     pRmApi->AllocWithHandle = rmapiAllocWithHandle;
179     pRmApi->AllocWithSecInfo = pRmApi->bTlsInternal ? rmapiAllocWithSecInfo : rmapiAllocWithSecInfoTls;
180 
181     pRmApi->FreeClientList = rmapiFreeClientList;
182     pRmApi->FreeClientListWithSecInfo = pRmApi->bTlsInternal ? rmapiFreeClientListWithSecInfo : rmapiFreeClientListWithSecInfoTls;
183 
184     pRmApi->Free = rmapiFree;
185     pRmApi->FreeWithSecInfo = pRmApi->bTlsInternal ? rmapiFreeWithSecInfo : rmapiFreeWithSecInfoTls;
186 
187     pRmApi->Control = rmapiControl;
188     pRmApi->ControlWithSecInfo = pRmApi->bTlsInternal ? rmapiControlWithSecInfo : rmapiControlWithSecInfoTls;
189 
190     pRmApi->DupObject = rmapiDupObject;
191     pRmApi->DupObjectWithSecInfo = pRmApi->bTlsInternal ? rmapiDupObjectWithSecInfo : rmapiDupObjectWithSecInfoTls;
192 
193     pRmApi->Share = rmapiShare;
194     pRmApi->ShareWithSecInfo = pRmApi->bTlsInternal ? rmapiShareWithSecInfo : rmapiShareWithSecInfoTls;
195 
196     pRmApi->MapToCpu = rmapiMapToCpu;
197     pRmApi->MapToCpuWithSecInfo = pRmApi->bTlsInternal ? rmapiMapToCpuWithSecInfo : rmapiMapToCpuWithSecInfoTls;
198     pRmApi->MapToCpuWithSecInfoV2 = pRmApi->bTlsInternal ? rmapiMapToCpuWithSecInfoV2 : rmapiMapToCpuWithSecInfoTlsV2;
199 
200     pRmApi->UnmapFromCpu = rmapiUnmapFromCpu;
201     pRmApi->UnmapFromCpuWithSecInfo = pRmApi->bTlsInternal ? rmapiUnmapFromCpuWithSecInfo : rmapiUnmapFromCpuWithSecInfoTls;
202 
203     pRmApi->Map = rmapiMap;
204     pRmApi->MapWithSecInfo = pRmApi->bTlsInternal ? rmapiMapWithSecInfo : rmapiMapWithSecInfoTls;
205 
206     pRmApi->Unmap = rmapiUnmap;
207     pRmApi->UnmapWithSecInfo = pRmApi->bTlsInternal ? rmapiUnmapWithSecInfo : rmapiUnmapWithSecInfoTls;
208 }
209 
210 RM_API *
211 rmapiGetInterface
212 (
213     RMAPI_TYPE rmapiType
214 )
215 {
216     return &g_RmApiList[rmapiType];
217 }
218 
219 NV_STATUS
220 rmapiPrologue
221 (
222     RM_API         *pRmApi,
223     RM_API_CONTEXT *pContext
224 )
225 {
226     NV_STATUS       status = NV_OK;
227     return status;
228 }
229 
230 void
231 rmapiEpilogue
232 (
233     RM_API         *pRmApi,
234     RM_API_CONTEXT *pContext
235 )
236 {
237 }
238 
239 void
240 rmapiInitLockInfo
241 (
242     RM_API            *pRmApi,
243     NvHandle           hClient,
244     RS_LOCK_INFO      *pLockInfo
245 )
246 {
247     NV_ASSERT_OR_RETURN_VOID(pLockInfo != NULL);
248     pLockInfo->flags = 0;
249     pLockInfo->state = 0;
250 
251     if (hClient != 0)
252     {
253         CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
254         if ((pCallContext != NULL) && (pCallContext->pLockInfo != NULL))
255         {
256             pLockInfo->state = pCallContext->pLockInfo->state;
257 
258             if ((pCallContext->pLockInfo->pClient != NULL) &&
259                 (pCallContext->pLockInfo->pClient->hClient == hClient))
260             {
261                 pLockInfo->pClient = pCallContext->pLockInfo->pClient;
262             }
263             else
264             {
265                 pLockInfo->state &= ~RM_LOCK_STATES_CLIENT_LOCK_ACQUIRED;
266             }
267         }
268     }
269 
270     if (!pRmApi->bRmSemaInternal)
271         pLockInfo->flags |= RM_LOCK_FLAGS_RM_SEMA;
272 
273     if (pRmApi->bApiLockInternal)
274     {
275         pLockInfo->state |= RM_LOCK_STATES_API_LOCK_ACQUIRED;
276 
277         // RS-TODO: Assert that API rwlock is taken if no client is locked
278         if (pLockInfo->pClient == NULL)
279             pLockInfo->flags |= RM_LOCK_FLAGS_NO_CLIENT_LOCK;
280     }
281 
282     if (pRmApi->bGpuLockInternal)
283         pLockInfo->state |= RM_LOCK_STATES_ALLOW_RECURSIVE_LOCKS;
284 }
285 
286 static NV_STATUS
287 _rmapiLockAlloc(void)
288 {
289     // Turn on by default for Linux to get some soak time
290     // bug 2539044, bug 2536036: Enable by default.
291     g_resServ.bUnlockedParamCopy = NV_TRUE;
292 
293     NvU32 val = 0;
294     if ((osReadRegistryDword(NULL,
295                             NV_REG_STR_RM_PARAM_COPY_NO_LOCK,
296                             &val) == NV_OK))
297     {
298         g_resServ.bUnlockedParamCopy = (val != 0);
299     }
300 
301     portMemSet(&g_RmApiLock, 0, sizeof(g_RmApiLock));
302     g_RmApiLock.threadId = ~((NvU64)(0));
303     g_RmApiLock.pLock = portSyncRwLockCreate(portMemAllocatorGetGlobalNonPaged());
304     if (g_RmApiLock.pLock == NULL)
305         return NV_ERR_INSUFFICIENT_RESOURCES;
306 
307     g_RmApiLock.tlsEntryId = tlsEntryAlloc();
308 
309     return NV_OK;
310 }
311 
312 static void
313 _rmapiLockFree(void)
314 {
315     portSyncRwLockDestroy(g_RmApiLock.pLock);
316 }
317 
318 NV_STATUS
319 rmapiLockAcquire(NvU32 flags, NvU32 module)
320 {
321     NV_STATUS rmStatus = NV_OK;
322     NvU64 threadId = portThreadGetCurrentThreadId();
323 
324     NvU64 myPriority = 0;
325 
326     LOCK_ASSERT_AND_RETURN(!rmapiLockIsOwner());
327 
328     //
329     // If a read-only lock was requested, check to see if the module is allowed
330     // to take read-only locks
331     //
332     if ((flags & RMAPI_LOCK_FLAGS_READ) && (module != RM_LOCK_MODULES_NONE))
333     {
334         OBJSYS *pSys = SYS_GET_INSTANCE();
335         if ((pSys->apiLockModuleMask & RM_LOCK_MODULE_GRP(module)) == 0)
336         {
337             flags &= ~RMAPI_LOCK_FLAGS_READ;
338         }
339     }
340 
341     //
342     // For conditional acquires and DISPATCH_LEVEL we want to exit
343     // immediately without waiting.
344     //
345     // If RM Locking V3 Lite is not enabled, *always* acquire the API
346     // lock in WRITE mode to ensure compatibility with Locking model V2
347     // behavior (providing exclusive access to the resource).
348     //
349     flags = osApiLockAcquireConfigureFlags(flags);
350     if (flags & API_LOCK_FLAGS_COND_ACQUIRE)
351     {
352         if ((flags & RMAPI_LOCK_FLAGS_READ))
353         {
354             if (!portSyncRwLockAcquireReadConditional(g_RmApiLock.pLock))
355                 rmStatus = NV_ERR_TIMEOUT_RETRY;
356         }
357         else
358         {
359             if (portSyncRwLockAcquireWriteConditional(g_RmApiLock.pLock))
360             {
361                 g_RmApiLock.threadId = threadId;
362             }
363             else
364             {
365                 rmStatus = NV_ERR_TIMEOUT_RETRY;
366             }
367         }
368     }
369     else
370     {
371         if ((flags & RMAPI_LOCK_FLAGS_READ))
372         {
373             portSyncRwLockAcquireRead(g_RmApiLock.pLock);
374         }
375         else
376         {
377 
378             portSyncRwLockAcquireWrite(g_RmApiLock.pLock);
379             g_RmApiLock.threadId = threadId;
380         }
381     }
382 
383 
384     if (rmStatus == NV_OK)
385     {
386         NvU64 timestamp;
387         osGetCurrentTick(&timestamp);
388 
389         if (g_RmApiLock.threadId == threadId)
390             g_RmApiLock.timestamp = timestamp;
391 
392         // save off owning thread
393         RMTRACE_RMLOCK(_API_LOCK_ACQUIRE);
394 
395         // add api lock trace record
396         INSERT_LOCK_TRACE(&g_RmApiLock.traceInfo,
397                           NV_RETURN_ADDRESS(),
398                           lockTraceAcquire,
399                           flags, module,
400                           threadId,
401                           !portSyncExSafeToSleep(),
402                           myPriority,
403                           timestamp);
404 
405         //
406         // If enabled, reset the timeout now that we are running and off
407         // the Sleep Queue.
408         //
409         if (threadStateGetSetupFlags() &
410             THREAD_STATE_SETUP_FLAGS_DO_NOT_INCLUDE_SLEEP_TIME_ENABLED)
411         {
412             threadStateResetTimeout(NULL);
413         }
414     }
415 
416     NvP64 *pAcquireAddress = tlsEntryAcquire(g_RmApiLock.tlsEntryId);
417     if (pAcquireAddress != NULL)
418     {
419         *pAcquireAddress = (NvP64)(NvUPtr)NV_RETURN_ADDRESS();
420     }
421 
422     return rmStatus;
423 }
424 
425 void
426 rmapiLockRelease(void)
427 {
428     NvU64 threadId = portThreadGetCurrentThreadId();
429     NvU64 timestamp;
430 
431     osGetCurrentTick(&timestamp);
432 
433     RMTRACE_RMLOCK(_API_LOCK_RELEASE);
434 
435     // add api lock trace record
436     INSERT_LOCK_TRACE(&g_RmApiLock.traceInfo,
437                       NV_RETURN_ADDRESS(),
438                       lockTraceRelease,
439                       0, 0,
440                       threadId,
441                       !portSyncExSafeToSleep(),
442                       0,
443                       timestamp);
444 
445     if (g_RmApiLock.threadId == threadId)
446     {
447         //
448         // If the threadId in the global is same as current thread id, then
449         // we know that it was acquired in WRITE mode.
450         //
451         g_RmApiLock.threadId  = ~0ull;
452         g_RmApiLock.timestamp = timestamp;
453         portSyncRwLockReleaseWrite(g_RmApiLock.pLock);
454 
455     }
456     else
457     {
458         portSyncRwLockReleaseRead(g_RmApiLock.pLock);
459     }
460 
461     tlsEntryRelease(g_RmApiLock.tlsEntryId);
462 }
463 
464 NvBool
465 rmapiLockIsOwner(void)
466 {
467     return tlsEntryGet(g_RmApiLock.tlsEntryId) != NvP64_NULL;
468 }
469 
470 //
471 // Mark for deletion the client resources from the data base, given a GPU mask
472 //
473 void
474 rmapiSetDelPendingClientResourcesFromGpuMask
475 (
476     NvU32 gpuMask
477 )
478 {
479     RS_ITERATOR             it;
480     RmClient              **ppClient;
481     RmClient               *pClient;
482     RsClient               *pRsClient;
483     Device                 *pDevice;
484     NvBool                  bDevicesInMask = NV_FALSE;
485     OBJGPU                 *pGpu;
486 
487     for (ppClient = serverutilGetFirstClientUnderLock();
488          ppClient;
489          ppClient = serverutilGetNextClientUnderLock(ppClient))
490     {
491         pClient = *ppClient;
492         pRsClient = staticCast(pClient, RsClient);
493 
494         it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
495 
496         // Check that one of the devices is in the GPU mask
497         bDevicesInMask = NV_FALSE;
498         while (clientRefIterNext(it.pClient, &it))
499         {
500             pDevice = dynamicCast(it.pResourceRef->pResource, Device);
501 
502             if (!pDevice)
503             {
504                 continue;
505             }
506 
507             pGpu = GPU_RES_GET_GPU(pDevice);
508             if ((gpuMask & NVBIT(gpuGetInstance(pGpu))) != 0)
509             {
510                 bDevicesInMask = NV_TRUE;
511                 break;
512             }
513         }
514 
515         if (bDevicesInMask == NV_FALSE)
516         {
517             continue;
518         }
519 
520         pClient->Flags |= RMAPI_CLIENT_FLAG_DELETE_PENDING;
521     }
522 }
523 
524 void
525 rmapiDelPendingDevices
526 (
527     NvU32 gpuMask
528 )
529 {
530     RmClient **ppClient;
531     RmClient  *pClient;
532     RsClient  *pRsClient;
533     RS_ITERATOR it;
534     RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
535 
536     ppClient = serverutilGetFirstClientUnderLock();
537     while (ppClient)
538     {
539         pClient = *ppClient;
540         pRsClient = staticCast(pClient, RsClient);
541 
542         if (((pClient->Flags & RMAPI_CLIENT_FLAG_DELETE_PENDING) != 0) &&
543             ((pClient->Flags & RMAPI_CLIENT_FLAG_RM_INTERNAL_CLIENT) == 0))
544         {
545             it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
546             while(clientRefIterNext(pRsClient, &it))
547             {
548                 RsResourceRef *pDeviceRef = it.pResourceRef;
549                 Device *pDevice = dynamicCast(pDeviceRef->pResource, Device);
550 
551                 if ((gpuMask & NVBIT(gpuGetInstance(GPU_RES_GET_GPU(pDevice)))) != 0)
552                 {
553                     pRmApi->Free(pRmApi, pRsClient->hClient, pDeviceRef->hResource);
554 
555                     // Client's resource map has been modified, re-snap iterator
556                     it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
557                 }
558             }
559 
560         }
561 
562         ppClient = serverutilGetNextClientUnderLock(ppClient);
563     }
564 }
565 
566 void
567 rmapiReportLeakedDevices
568 (
569     NvU32 gpuMask
570 )
571 {
572     RmClient **ppClient;
573     RmClient  *pClient;
574     RsClient  *pRsClient;
575     RS_ITERATOR it;
576     RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
577 
578     ppClient = serverutilGetFirstClientUnderLock();
579     while (ppClient)
580     {
581         pClient = *ppClient;
582         pRsClient = staticCast(pClient, RsClient);
583 
584         it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
585         while(clientRefIterNext(pRsClient, &it))
586         {
587             RsResourceRef *pDeviceRef = it.pResourceRef;
588             Device *pDevice = dynamicCast(pDeviceRef->pResource, Device);
589 
590             if ((gpuMask & NVBIT(gpuGetInstance(GPU_RES_GET_GPU(pDevice)))) != 0)
591             {
592                 NV_PRINTF(LEVEL_ERROR,
593                           "Device object leak: (0x%x, 0x%x). Please file a bug against RM-core.\n",
594                           pRsClient->hClient, pDeviceRef->hResource);
595                 NV_ASSERT(0);
596 
597                 // Delete leaked resource from database
598                 pRmApi->Free(pRmApi, pRsClient->hClient, pDeviceRef->hResource);
599 
600                 // Client's resource map has been modified, re-snap iterator
601                 it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
602             }
603         }
604 
605         ppClient = serverutilGetNextClientUnderLock(ppClient);
606     }
607 }
608 
609 //
610 // Delete the marked client resources
611 //
612 void
613 rmapiDelPendingClients
614 (
615     void
616 )
617 {
618     RmClient **ppClient;
619     RmClient  *pClient;
620     RsClient  *pRsClient;
621     RS_ITERATOR it;
622     RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
623 
624     ppClient = serverutilGetFirstClientUnderLock();
625     while (ppClient)
626     {
627         pClient = *ppClient;
628         pRsClient = staticCast(pClient, RsClient);
629         ppClient = serverutilGetNextClientUnderLock(ppClient);
630         if ((pClient->Flags & RMAPI_CLIENT_FLAG_DELETE_PENDING) != 0)
631         {
632             // Only free clients that have no devices left
633             it = clientRefIter(pRsClient, NULL, classId(Device), RS_ITERATE_CHILDREN, NV_TRUE);
634             if (!clientRefIterNext(pRsClient, &it))
635                 pRmApi->Free(pRmApi, pRsClient->hClient, pRsClient->hClient);
636         }
637     }
638 }
639 
640 extern OsInfoMap g_osInfoList;
641 
642 NV_STATUS
643 rmapiGetClientHandlesFromOSInfo
644 (
645     void      *pOSInfo,
646     NvHandle **ppClientHandleList,
647     NvU32     *pClientHandleListSize
648 )
649 {
650     OBJSYS *pSys = SYS_GET_INSTANCE();
651 
652     NvHandle   *pClientHandleList;
653     NvU32       clientHandleListSize = 0;
654     NvU32       k;
655 
656     RmClient **ppClient;
657     RmClient **ppFirstClient;
658     RmClient  *pClient;
659     RsClient  *pRsClient;
660 
661     NvBool clientHandleLookup = pSys->getProperty(pSys, PDB_PROP_SYS_CLIENT_HANDLE_LOOKUP);
662 
663     if (!clientHandleLookup)
664     {
665         ppFirstClient = NULL;
666         for (ppClient = serverutilGetFirstClientUnderLock();
667             ppClient;
668             ppClient = serverutilGetNextClientUnderLock(ppClient))
669         {
670             pClient = *ppClient;
671             if (pClient->pOSInfo != pOSInfo)
672             {
673                 continue;
674             }
675             clientHandleListSize++;
676 
677             if (NULL == ppFirstClient)
678                 ppFirstClient = ppClient;
679         }
680 
681         if (clientHandleListSize == 0)
682         {
683             *pClientHandleListSize = 0;
684             *ppClientHandleList = NULL;
685             return NV_ERR_INVALID_ARGUMENT;
686         }
687 
688         pClientHandleList = portMemAllocNonPaged(clientHandleListSize * sizeof(NvU32));
689         if (pClientHandleList == NULL)
690         {
691             return NV_ERR_NO_MEMORY;
692         }
693 
694         *pClientHandleListSize = clientHandleListSize;
695         *ppClientHandleList = pClientHandleList;
696 
697         k = 0;
698         for (ppClient = ppFirstClient;
699             ppClient;
700             ppClient = serverutilGetNextClientUnderLock(ppClient))
701         {
702             pClient = *ppClient;
703             pRsClient = staticCast(pClient, RsClient);
704             if (pClient->pOSInfo != pOSInfo)
705             {
706                 continue;
707             }
708             pClientHandleList[k++] = pRsClient->hClient;
709 
710             if (clientHandleListSize <= k)
711                 break;
712         }
713     }
714     else
715     {
716         OsInfoMapSubmap *pSubmap = NULL;
717         OsInfoMapIter it;
718         NvU64 key1 = (NvUPtr)pOSInfo;
719         pSubmap = multimapFindSubmap(&g_osInfoList, key1);
720         if (pSubmap != NULL)
721         {
722             clientHandleListSize = multimapCountSubmapItems(&g_osInfoList, pSubmap);
723             NV_PRINTF(LEVEL_INFO, "*** Found %d clients for %llx\n", clientHandleListSize, key1);
724         }
725         if (clientHandleListSize == 0)
726         {
727             *pClientHandleListSize = 0;
728             *ppClientHandleList = NULL;
729             return NV_ERR_INVALID_ARGUMENT;
730         }
731         pClientHandleList = portMemAllocNonPaged(clientHandleListSize * sizeof(NvU32));
732         if (pClientHandleList == NULL)
733         {
734             return NV_ERR_NO_MEMORY;
735         }
736         *pClientHandleListSize = clientHandleListSize;
737         *ppClientHandleList = pClientHandleList;
738         k = 0;
739         it = multimapSubmapIterItems(&g_osInfoList, pSubmap);
740         while(multimapItemIterNext(&it))
741         {
742             pClient = *it.pValue;
743             pRsClient = staticCast(pClient, RsClient);
744 
745             NV_CHECK_OR_ELSE_STR(LEVEL_ERROR, pClient->pOSInfo == pOSInfo, "*** OS info mismatch", continue);
746 
747             pClientHandleList[k++] = pRsClient->hClient;
748             NV_PRINTF(LEVEL_INFO, "*** Found: %x\n", pRsClient->hClient);
749             if (clientHandleListSize <= k)
750                 break;
751         }
752     }
753 
754     return NV_OK;
755 }
756 
757