1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2020-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 * Module: videoeventlist.c                                                 *
27 *   Description:                                                           *
28 *       This module contains an implementation of the Event Buffer         *
29 *        callback for video events                                         *
30 *                                                                          *
31 \***************************************************************************/
32 
33 #include "rmapi/client.h"
34 #include "rmapi/event.h"
35 #include "rmapi/event_buffer.h"
36 #include "resserv/rs_server.h"
37 #include "core/locks.h"
38 #include "os/os.h"
39 #include "gpuvideo/video_event.h"
40 #include "gpuvideo/videoeventlist.h"
41 #include "objtmr.h"
42 #include "kernel/gpu/video/kernel_video_engine.h"
43 #include "kernel/gpu/fifo/kernel_channel_group.h"
44 #include "kernel/gpu/bus/kern_bus.h"
45 
46 #include "class/cl90cd.h"
47 #include "class/cl90cdtypes.h"
48 #include "class/cl90cdvideo.h"
49 
50 #include "ctrl/ctrl2080/ctrl2080internal.h"
51 
52 #define NV_VIDEO_TRACE_CALLBACK_TIME_NS 50000000            // Approximating 20Hz callback
53 
54 /*!
55  * This helper function initializes the context used for video event trace.
56  */
57 NV_STATUS
58 videoEventTraceCtxInit
59 (
60     OBJGPU *pGpu,
61     KernelChannel *pKernelChannel,
62     ENGDESCRIPTOR engDesc
63 )
64 {
65     KernelVideoEngine *pKernelVideoEngine;
66     MEMORY_DESCRIPTOR *pCtxMemDesc;
67     VIDEO_ENGINE_EVENT__LOG_INFO logInfo;
68 
69     if (RMCFG_FEATURE_PLATFORM_GSP || !IS_VIDEO_ENGINE(engDesc) || !kvidengIsVideoTraceLogSupported(pGpu))
70         return NV_OK;
71 
72     pKernelVideoEngine = kvidengFromEngDesc(pGpu, engDesc);
73     NV_CHECK_OR_RETURN(LEVEL_ERROR, pKernelVideoEngine != NULL, NV_OK);
74     NV_CHECK_OR_RETURN(LEVEL_SILENT, pKernelVideoEngine->bVideoTraceEnabled, NV_OK);
75 
76     // Fill some channel specific information for event logging
77     logInfo.userInfo = (NvU64)(NvUPtr)pKernelChannel->pUserInfo;
78     logInfo.pid = pKernelChannel->ProcessID;
79     logInfo.context_id = kchannelGetCid(pKernelChannel);
80     logInfo.engine_id = ENGDESC_FIELD(engDesc, _INST);
81     logInfo.gfid = kchannelGetGfid(pKernelChannel);
82 
83     kchangrpGetEngineContextMemDesc(pGpu,
84                                     pKernelChannel->pKernelChannelGroupApi->pKernelChannelGroup,
85                                     &pCtxMemDesc);
86 
87     if (pCtxMemDesc != NULL)
88     {
89         NvU8 *pInstMem;
90         NvU32 i;
91         NvU32 *pLogInfo = (NvU32 *)&logInfo;
92 
93         // Is context allocation too small to hold the client info for event trace?
94         NV_CHECK_OR_RETURN(LEVEL_INFO,
95             memdescGetSize(pCtxMemDesc) >= (VIDEO_ENGINE_EVENT__LOG_INFO__OFFSET + VIDEO_ENGINE_EVENT__LOG_INFO__SIZE),
96             NV_ERR_BUFFER_TOO_SMALL);
97 
98         pInstMem = kbusMapRmAperture_HAL(pGpu, pCtxMemDesc);
99         NV_CHECK_OR_RETURN(LEVEL_ERROR, pInstMem != NULL, NV_ERR_INSUFFICIENT_RESOURCES);
100 
101         for (i = 0; i < sizeof(VIDEO_ENGINE_EVENT__LOG_INFO); i += 4)
102         {
103             // Initialize client information in context allocation.
104             MEM_WR32(pInstMem + VIDEO_ENGINE_EVENT__LOG_INFO__OFFSET + i, pLogInfo[i / sizeof(NvU32)]);
105         }
106 
107         kbusUnmapRmAperture_HAL(pGpu, pCtxMemDesc, &pInstMem, NV_TRUE);
108     }
109 
110     return NV_OK;
111 }
112 
113 static
114 NV_STATUS
115 _videoEventBufferAdd
116 (
117     OBJGPU *pGpu,
118     NV_EVENT_BUFFER_BIND_POINT_VIDEO *pBind,
119     NOTIFY_VIDEO_EVENT *pNotifyRecord,
120     NvU32 *pLogData,
121     NvBool bSanitizeUser,
122     NvBool bSanitizeKernel
123 )
124 {
125     NV_STATUS status;
126     NvBool bNotify;
127     NvP64 notificationHandle;
128     EVENT_BUFFER_PRODUCER_DATA notifyEvent;
129     NvU32 notifyIndex = NV_EVENT_BUFFER_RECORD_TYPE_VIDEO_TRACE;
130     VIDEO_ENGINE_EVENT__RECORD const * pRecord;
131 
132     if (pNotifyRecord == NULL)
133     {
134         return NV_OK;
135     }
136     pRecord = (VIDEO_ENGINE_EVENT__RECORD const *)(pNotifyRecord->pEventData);
137 
138     portMemSet(&notifyEvent, 0, sizeof(notifyEvent));
139     notifyEvent.pVardata = NV_PTR_TO_NvP64(NULL);
140     notifyEvent.vardataSize = 0;
141 
142     NV_EVENT_BUFFER_VIDEO_RECORD_V1 videoRecord;
143     portMemSet(&videoRecord, 0, sizeof(videoRecord));
144     videoRecord.event_id = pRecord->event_id;
145     videoRecord.vmid = pRecord->gfid;
146     videoRecord.timestamp = pRecord->ts;
147     videoRecord.seqno = pRecord->seq_no;
148     videoRecord.context_id = pRecord->context_id;
149     videoRecord.pid = pRecord->pid;
150     videoRecord.api_id = pRecord->api_id;
151 
152     if (bSanitizeKernel)
153     {
154         videoRecord.pid = NV_EVENT_BUFFER_VIDEO_KERNEL_PID;
155         videoRecord.context_id = NV_EVENT_BUFFER_VIDEO_KERNEL_CONTEXT;
156         videoRecord.api_id = NV_EVENT_BUFFER_VIDEO_KERNEL_CONTEXT;
157     }
158     else if (bSanitizeUser)
159     {
160         videoRecord.pid = NV_EVENT_BUFFER_VIDEO_HIDDEN_PID;
161         videoRecord.context_id = NV_EVENT_BUFFER_VIDEO_HIDDEN_CONTEXT;
162         videoRecord.api_id = NV_EVENT_BUFFER_VIDEO_HIDDEN_CONTEXT;
163     }
164 
165 #if PORT_IS_MODULE_SUPPORTED(crypto)
166     // Randomized timestamp if sanitization is needed
167     if (bSanitizeKernel || bSanitizeUser)
168     {
169         //
170         // pNotifyRecord->noisyTimestampStart is copied from pKernelVideoEngine->videoTraceInfo->noisyTimestampStart which is initialized to 0
171         // during engine initialization when trace surface is allocated before session starts. pKernelVideoEngine->videoTraceInfo->noisyTimestampStart
172         // is recorded with the timestamp of a SESSION_START event. Engine will always log event in sequence and every SESSION_END event
173         // should have a SESSION_START event in front of it. Also, we are assuming NSight will discard a SESSION_END event
174         // without a SESSION_START event before it.
175         //
176         if ((pNotifyRecord->noisyTimestampStart !=  videoRecord.timestamp) && (pNotifyRecord->pVideoLogPrng != NULL))
177         {
178             NvU64 noisyTimestampRange;
179             // The range is always non-zero since we had check
180             //    (pNotifyRecord->noisyTimestampStart !=  videoRecord.timestamp)
181             // above.
182             noisyTimestampRange = (videoRecord.timestamp >= pNotifyRecord->noisyTimestampStart)
183                                   ? (videoRecord.timestamp - pNotifyRecord->noisyTimestampStart)
184                                   : (((NvU64)(-1) - pNotifyRecord->noisyTimestampStart) + videoRecord.timestamp);
185             videoRecord.timestamp = pNotifyRecord->noisyTimestampStart
186                                     + portCryptoPseudoRandomGeneratorGetU32(pNotifyRecord->pVideoLogPrng) % noisyTimestampRange;
187         }
188     }
189 #endif // PORT_IS_MODULE_SUPPORTED(crypto)
190 
191     switch (pRecord->event_id)
192     {
193         case VIDEO_ENGINE_EVENT_ID__SESSION_START:
194             videoRecord.session.engine_type = pRecord->event_start.engine_type;
195             videoRecord.session.engine_id = pRecord->event_start.engine_id;
196             videoRecord.session.codec_id = pRecord->event_start.codec_id;
197             break;
198         case VIDEO_ENGINE_EVENT_ID__SESSION_END:
199             videoRecord.session.engine_type = pRecord->event_start.engine_type;
200             videoRecord.session.engine_id = pRecord->event_end.engine_id;
201             videoRecord.session.codec_id = pRecord->event_end.codec_id;
202             videoRecord.session.status = pRecord->event_end.status;
203             break;
204         case VIDEO_ENGINE_EVENT_ID__POWER_STATE_CHANGE:
205             videoRecord.stateChange.to = pRecord->event_pstate_change.to;
206             videoRecord.stateChange.from = pRecord->event_pstate_change.from;
207             break;
208         case VIDEO_ENGINE_EVENT_ID__LOG_DATA:
209             videoRecord.logData.engine_type = pRecord->event_start.engine_type;
210             videoRecord.logData.engine_id = pRecord->event_start.engine_id;
211             videoRecord.logData.codec_id = pRecord->event_start.codec_id;
212             videoRecord.logData.size = pRecord->event_log_data.size;
213             notifyEvent.pVardata = NV_PTR_TO_NvP64(pLogData);
214             notifyEvent.vardataSize = videoRecord.logData.size;
215             break;
216         default:
217             videoRecord.event_data = pRecord->event_data;
218     }
219 
220     notifyEvent.pPayload = NV_PTR_TO_NvP64(&videoRecord);
221     notifyEvent.payloadSize = sizeof(videoRecord);
222 
223     status = eventBufferAdd(pBind->pEventBuffer, &notifyEvent, notifyIndex, &bNotify, &notificationHandle);
224 
225     if ((status == NV_OK) && bNotify && notificationHandle)
226     {
227         osEventNotification(pGpu,
228                 pBind->pEventBuffer->pListeners,
229                 notifyIndex,
230                 &notifyEvent,
231                 0);             // Do not copy structure -- embedded pointers.
232         pBind->pEventBuffer->bNotifyPending = NV_TRUE;
233     }
234 
235     return status;
236 }
237 
238 static void _notifyEventBuffers
239 (
240     OBJGPU *pGpu,
241     VideoEventBufferBindMultiMapSubmap *pSubmap,
242     NOTIFY_VIDEO_EVENT *pNotifyRecord,
243     NvU32 *pLogData
244 )
245 {
246     VIDEO_ENGINE_EVENT__RECORD const * pRecord = (VIDEO_ENGINE_EVENT__RECORD const *)pNotifyRecord->pEventData;
247 
248     if (pSubmap != NULL)
249     {
250         VideoEventBufferBindMultiMapIter iter = multimapSubmapIterItems(&pGpu->videoEventBufferBindingsUid, pSubmap);
251 
252         while (multimapItemIterNext(&iter))
253         {
254             NV_EVENT_BUFFER_BIND_POINT_VIDEO* pBind = iter.pValue;
255             NvBool bSanitizeKernel = (!pBind->bKernel) && (pRecord->userInfo == 0);
256             NvBool bSanitizeUser = (!pBind->bAdmin) && (pBind->pUserInfo != pRecord->userInfo);
257 
258             if (!(NVBIT(pRecord->event_id) & pBind->eventMask))
259                 continue;
260 
261             _videoEventBufferAdd(pGpu, pBind, pNotifyRecord, pLogData, bSanitizeUser, bSanitizeKernel);
262         }
263     }
264 }
265 
266 static void _videoGetTraceEvents
267 (
268     OBJGPU  *pGpu,
269     KernelVideoEngine *pKernelVideoEngine,
270     VideoEventBufferBindMultiMapSubmap *pSubmapAll
271 )
272 {
273     VideoEventBufferBindMultiMapSubmap *pSubmapUserOnly = NULL;
274     NvU64 cachedUserInfo = 0;
275     NvU32 magicHi = ENG_VIDEO_TRACE_EVENT_MAGIC_HI;
276     NvU32 magicLo = ENG_VIDEO_TRACE_EVENT_MAGIC_LO;
277     VIDEO_TRACE_RING_BUFFER *pRingbuffer;
278     VIDEO_ENGINE_EVENT__RECORD videoRecord;
279     NvU32 gotSize;
280 
281     NV_ASSERT_OR_RETURN_VOID(pKernelVideoEngine != NULL);
282     NV_CHECK_OR_RETURN_VOID(LEVEL_INFO, pKernelVideoEngine->bVideoTraceEnabled);
283 
284     pRingbuffer = pKernelVideoEngine->videoTraceInfo.pTraceBufferEngine;
285 
286     if (pRingbuffer == NULL)
287         return;
288 
289     while (kvidengRingbufferGetDataSize(pGpu, pRingbuffer) >= sizeof(VIDEO_ENGINE_EVENT__RECORD))
290     {
291         NOTIFY_VIDEO_EVENT notifyRecord;
292 
293         NvU32 oldReadPtr = pRingbuffer->readPtr;
294 
295         gotSize = kvidengEventbufferGetRecord(pGpu,
296                                               pKernelVideoEngine,
297                                               pRingbuffer,
298                                               &videoRecord,
299                                               magicHi,
300                                               magicLo);
301 
302         // If the read pointer was not moved by us, this record may be invalid
303         if ((oldReadPtr + sizeof(VIDEO_ENGINE_EVENT__RECORD)) != pRingbuffer->readPtr)
304             continue;
305 
306         if (gotSize == 0)
307             continue;
308 
309         if (videoRecord.event_id == VIDEO_ENGINE_EVENT_ID__SESSION_START)
310         {
311             pKernelVideoEngine->videoTraceInfo.noisyTimestampStart = videoRecord.ts;
312         }
313         notifyRecord.noisyTimestampStart = pKernelVideoEngine->videoTraceInfo.noisyTimestampStart;
314         notifyRecord.pVideoLogPrng = pKernelVideoEngine->videoTraceInfo.pVideoLogPrng;
315         notifyRecord.pEventData = (void *)(&videoRecord);
316 
317         if (videoRecord.userInfo != 0)
318         {
319             if (cachedUserInfo != videoRecord.userInfo)
320             {
321                 pSubmapUserOnly = multimapFindSubmap(&pGpu->videoEventBufferBindingsUid, videoRecord.userInfo);
322                 cachedUserInfo = videoRecord.userInfo;
323             }
324 
325             _notifyEventBuffers(pGpu,
326                                 pSubmapUserOnly,
327                                 &notifyRecord,
328                                 (NvU32 *)(pKernelVideoEngine->videoTraceInfo.pTraceBufferVariableData));
329         }
330 
331         _notifyEventBuffers(pGpu,
332                             pSubmapAll,
333                             &notifyRecord,
334                             (NvU32 *)(pKernelVideoEngine->videoTraceInfo.pTraceBufferVariableData));
335     }
336 }
337 
338 static NV_STATUS
339 _videoEventBufferSetFlag(OBJGPU *pGpu, NvU32 flag)
340 {
341     RM_API *pRmApi = GPU_GET_PHYSICAL_RMAPI(pGpu);
342     NV2080_CTRL_INTERNAL_FLCN_SET_VIDEO_EVENT_BUFFER_FLAGS_PARAMS params = {0};
343 
344     params.flags = flag;
345     NV_ASSERT_OK_OR_RETURN(
346         pRmApi->Control(pRmApi,
347                         pGpu->hInternalClient,
348                         pGpu->hInternalSubdevice,
349                         NV2080_CTRL_CMD_INTERNAL_FLCN_SET_VIDEO_EVENT_BUFFER_FLAGS,
350                         &params,
351                         sizeof(params)));
352 
353     return NV_OK;
354 }
355 
356 static void
357 _videoOsWorkItem
358 (
359     NvU32 gpuInstance,
360     void *data
361 )
362 {
363     OBJGPU *pGpu = gpumgrGetGpu(gpuInstance);
364 
365     nvEventBufferVideoCallback(pGpu, NULL);
366 }
367 
368 static NV_STATUS
369 _videoTimerCallback
370 (
371     OBJGPU *pGpu,
372     OBJTMR *pTmr,
373     TMR_EVENT *pTmrEvent
374 )
375 {
376     NV_STATUS status;
377 
378     NV_CHECK_OK(status, LEVEL_ERROR, osQueueWorkItemWithFlags(pGpu, _videoOsWorkItem, NULL, OS_QUEUE_WORKITEM_FLAGS_LOCK_GPU_GROUP_DEVICE_RW));
379 
380     // TMR_FLAG_RECUR does not work, so reschedule it here.
381     NV_CHECK_OK_OR_CAPTURE_FIRST_ERROR(status, LEVEL_ERROR, tmrEventScheduleRel(pTmr, pTmrEvent, NV_VIDEO_TRACE_CALLBACK_TIME_NS));
382 
383     return status;
384 }
385 
386 static NV_STATUS
387 _videoTimerCreate
388 (
389     OBJGPU *pGpu
390 )
391 {
392     OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
393     NvU32 timerFlags = TMR_FLAG_RECUR;
394     // Unix needs to use the OS timer to avoid corrupting records, but Windows doesn't have an OS timer implementation
395     timerFlags |= TMR_FLAG_USE_OS_TIMER;
396 
397     NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
398         tmrEventCreate(pTmr, &pGpu->pVideoTimerEvent, _videoTimerCallback, NULL, timerFlags));
399 
400     // This won't be a true 20Hz timer as the callbacks are scheduled from the time they're called
401     NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
402         tmrEventScheduleRel(pTmr, pGpu->pVideoTimerEvent, NV_VIDEO_TRACE_CALLBACK_TIME_NS));
403 
404     return NV_OK;
405 }
406 
407 static void
408 _videoTimerDestroy
409 (
410     OBJGPU *pGpu
411 )
412 {
413     if (pGpu->pVideoTimerEvent != NULL)
414     {
415         OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
416 
417         tmrEventCancel(pTmr, pGpu->pVideoTimerEvent);
418         tmrEventDestroy(pTmr, pGpu->pVideoTimerEvent);
419         pGpu->pVideoTimerEvent = NULL;
420     }
421 }
422 
423 void
424 nvEventBufferVideoCallback
425 (
426     OBJGPU  *pGpu,
427     void    *pArgs
428 )
429 {
430     VideoEventBufferBindMultiMapSubmap *pSubmapAll = NULL;
431     NvU8 i;
432 
433     if (!rmDeviceGpuLockIsOwner(pGpu->gpuInstance))
434     {
435         NV_ASSERT(0);
436         return;
437     }
438 
439     if (pGpu->videoCtxswLogConsumerCount <= 0)
440     {
441         NV_ASSERT(pGpu->videoCtxswLogConsumerCount >= 0);
442         return;
443     }
444 
445     pSubmapAll = multimapFindSubmap(&pGpu->videoEventBufferBindingsUid, 0);
446     if (pSubmapAll == NULL)
447         return;
448 
449     for (i = 0; i < pGpu->numKernelVideoEngines; i++)
450     {
451         KernelVideoEngine *pKernelVideoEngine = pGpu->kernelVideoEngines[i];
452         _videoGetTraceEvents(pGpu, pKernelVideoEngine, pSubmapAll);
453     }
454 
455 }
456 
457 void
458 videoRemoveBindpoint
459 (
460     OBJGPU *pGpu,
461     NvU64 uid,
462     NV_EVENT_BUFFER_BIND_POINT_VIDEO* pBind
463 )
464 {
465     EventBuffer *pEventBuffer = pBind->pEventBuffer;
466 
467     --pGpu->videoCtxswLogConsumerCount;
468     if (pGpu->videoCtxswLogConsumerCount == 0)
469     {
470         // When last client is unbound, disable engine event logging.
471         _videoEventBufferSetFlag(pGpu, 0);
472     }
473 
474     unregisterEventNotificationWithData(&pEventBuffer->pListeners,
475             pBind->hClient,
476             pBind->hNotifier,
477             pBind->hEventBuffer,
478             NV_TRUE,
479             pEventBuffer->producerInfo.notificationHandle);
480 
481     multimapRemoveItemByKey(&pGpu->videoEventBufferBindingsUid,
482             uid,
483             (NvU64)(NvUPtr)pEventBuffer);
484 }
485 
486 NV_STATUS
487 videoAddBindpoint
488 (
489     OBJGPU *pGpu,
490     RsClient *pClient,
491     RsResourceRef *pEventBufferRef,
492     NvHandle hNotifier,
493     NvBool bAllUsers,
494     NV2080_CTRL_EVENT_VIDEO_BIND_EVTBUF_LOD levelOfDetail,
495     NvU32 eventFilter
496 )
497 {
498     NV_STATUS status;
499     NvHandle hClient = pClient->hClient;
500     RmClient *pRmClient = dynamicCast(pClient, RmClient);
501     NvHandle hEventBuffer = pEventBufferRef->hResource;
502     EventBuffer *pEventBuffer;
503     NvBool bVideoBindingActive = (pGpu->videoCtxswLogConsumerCount > 0);
504     NvU64 targetUser;
505 
506     NvBool bAdmin = osIsAdministrator();
507     NvBool bKernel;
508     NvU32 eventMask = 0;
509     NvBool bSelectLOD;
510 
511     CALL_CONTEXT *pCallContext = resservGetTlsCallContext();
512     NV_ASSERT_OR_RETURN(pCallContext != NULL, NV_ERR_INVALID_STATE);
513     bKernel = pCallContext->secInfo.privLevel >= RS_PRIV_LEVEL_KERNEL;
514     bSelectLOD = bKernel;
515 
516 #if defined(DEBUG) || defined(DEVELOP) || defined(NV_VERIF_FEATURES)
517     bSelectLOD = NV_TRUE;
518 #endif
519 
520     LOCK_ASSERT_AND_RETURN(rmapiLockIsOwner() && rmDeviceGpuLockIsOwner(pGpu->gpuInstance));
521 
522     if (!kvidengIsVideoTraceLogSupported(pGpu))
523         return NV_ERR_NOT_SUPPORTED;
524 
525     if (bSelectLOD)
526     {
527         switch(levelOfDetail)
528         {
529             case NV2080_CTRL_EVENT_VIDEO_BIND_EVTBUF_LOD_FULL:
530                 eventMask = ~0;
531                 break;
532             case NV2080_CTRL_EVENT_VIDEO_BIND_EVTBUF_LOD_CUSTOM:
533                 eventMask = eventFilter;
534                 break;
535             case NV2080_CTRL_EVENT_VIDEO_BIND_EVTBUF_LOD_SIMPLE:
536             default:
537                 // Default to SIMPLIFIED level-of-detail
538                 eventMask |= NV_EVENT_BUFFER_VIDEO_BITMASK_TAG_ENGINE_START |
539                              NV_EVENT_BUFFER_VIDEO_BITMASK_TAG_ENGINE_END;
540         }
541     }
542     else
543     {
544         // Default to SIMPLIFIED level-of-detail
545         eventMask |= NV_EVENT_BUFFER_VIDEO_BITMASK_TAG_ENGINE_START |
546                      NV_EVENT_BUFFER_VIDEO_BITMASK_TAG_ENGINE_END;
547     }
548 
549     if (bAllUsers)
550     {
551         targetUser = 0;
552     }
553     else
554     {
555         // Clients requesting only their own events will not work
556         NV_ASSERT_OR_RETURN(bAllUsers, NV_ERR_INVALID_ARGUMENT);
557     }
558 
559     pEventBuffer = dynamicCast(pEventBufferRef->pResource, EventBuffer);
560     if (NULL == pEventBuffer)
561         return NV_ERR_INVALID_ARGUMENT;
562 
563     if (NULL == multimapFindSubmap(&pGpu->videoEventBufferBindingsUid, targetUser))
564     {
565         if (NULL == multimapInsertSubmap(&pGpu->videoEventBufferBindingsUid, targetUser))
566         {
567             NV_PRINTF(LEVEL_ERROR, "failed to add UID binding!\n");
568             return NV_ERR_INSUFFICIENT_RESOURCES;
569         }
570     }
571 
572     // If the binding exists already, we're done
573     if (NULL != multimapFindItem(&pGpu->videoEventBufferBindingsUid, targetUser, (NvU64)(NvUPtr)pEventBuffer))
574         return NV_OK;
575 
576     NV_EVENT_BUFFER_BIND_POINT_VIDEO* pBind = multimapInsertItemNew(&pGpu->videoEventBufferBindingsUid, 0, (NvU64)(NvUPtr)pEventBuffer);
577     if (pBind == NULL)
578         return NV_ERR_INVALID_ARGUMENT;
579 
580     pBind->hClient = hClient;
581     pBind->hNotifier = hNotifier;
582     pBind->hEventBuffer = hEventBuffer;
583     pBind->pEventBuffer = pEventBuffer;
584     pBind->pUserInfo = (NvU64)(NvUPtr)pRmClient->pUserInfo;
585     pBind->bAdmin = bAdmin;
586     pBind->eventMask = eventMask;
587     pBind->bKernel = bKernel;
588 
589     ++pGpu->videoCtxswLogConsumerCount;
590     if (pGpu->videoCtxswLogConsumerCount == 1)
591     {
592         // When first client is bound, enable engine event logging.
593         NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,
594             _videoEventBufferSetFlag(pGpu, VIDEO_TRACE_FLAG__LOGGING_ENABLED),
595             done);
596     }
597 
598     status = registerEventNotification(&pEventBuffer->pListeners,
599                 pClient,
600                 hNotifier,
601                 hEventBuffer,
602                 NV_EVENT_BUFFER_RECORD_TYPE_VIDEO_TRACE | NV01_EVENT_WITHOUT_EVENT_DATA,
603                 NV_EVENT_BUFFER_BIND,
604                 pEventBuffer->producerInfo.notificationHandle,
605                 NV_FALSE);
606     if (status != NV_OK)
607         goto done;
608 
609     if (!bVideoBindingActive)
610     {
611         NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, _videoTimerCreate(pGpu), done);
612     }
613 
614 done:
615     if (status != NV_OK)
616     {
617         videoRemoveBindpoint(pGpu, 0, pBind);
618 
619         _videoTimerDestroy(pGpu);
620     }
621 
622     return status;
623 }
624 
625 void
626 videoRemoveAllBindpointsForGpu
627 (
628     OBJGPU *pGpu
629 )
630 {
631     NvS16 prevConsumerCount = pGpu->videoCtxswLogConsumerCount;
632     VideoEventBufferBindMultiMapSupermapIter iter;
633 
634     if (pGpu->videoEventBufferBindingsUid.real.base.map.pAllocator == NULL)
635         return;
636 
637     iter = multimapSubmapIterAll(&pGpu->videoEventBufferBindingsUid);
638     while (multimapSubmapIterNext(&iter))
639     {
640         VideoEventBufferBindMultiMapSubmap *pSubmap = iter.pValue;
641         VideoEventBufferBindMultiMapIter subIter = multimapSubmapIterItems(&pGpu->videoEventBufferBindingsUid, pSubmap);
642         NvU64 uid = mapKey_IMPL(iter.iter.pMap, pSubmap);
643 
644         while (multimapItemIterNext(&subIter))
645         {
646             NV_EVENT_BUFFER_BIND_POINT_VIDEO* pBind = subIter.pValue;
647             videoRemoveBindpoint(pGpu, uid, pBind);
648             subIter = multimapSubmapIterItems(&pGpu->videoEventBufferBindingsUid, pSubmap);
649         }
650     }
651 
652     if ((prevConsumerCount != 0) && (pGpu->videoCtxswLogConsumerCount == 0))
653         videoBufferTeardown(pGpu);
654 }
655 
656 void
657 videoRemoveAllBindpoints
658 (
659     EventBuffer *pEventBuffer
660 )
661 {
662     OBJGPU *pGpu = NULL;
663     NvU32 gpuMask = 0;
664     NvU32 gpuIndex = 0;
665     VideoEventBufferBindMultiMapSupermapIter iter;
666 
667     gpumgrGetGpuAttachInfo(NULL, &gpuMask);
668     while ((pGpu = gpumgrGetNextGpu(gpuMask, &gpuIndex)) != NULL)
669     {
670         iter = multimapSubmapIterAll(&pGpu->videoEventBufferBindingsUid);
671         while (multimapSubmapIterNext(&iter))
672         {
673             VideoEventBufferBindMultiMapSubmap *pSubmap = iter.pValue;
674             NV_EVENT_BUFFER_BIND_POINT_VIDEO* pBind = NULL;
675             NvU64 uid = mapKey_IMPL(iter.iter.pMap, pSubmap);
676 
677             while ((pBind = multimapFindItem(&pGpu->videoEventBufferBindingsUid,
678                             uid,
679                             (NvU64)(NvUPtr)pEventBuffer)) != NULL)
680             {
681                 videoRemoveBindpoint(pGpu, uid, pBind);
682             }
683         }
684 
685         if (pGpu->videoCtxswLogConsumerCount == 0)
686             videoBufferTeardown(pGpu);
687     }
688 }
689 
690 void
691 videoBufferTeardown
692 (
693     OBJGPU *pGpu
694 )
695 {
696     _videoTimerDestroy(pGpu);
697 }
698 
699