1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2017-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 #include "eventbufferproducer.h"
25 #include "nvport/nvport.h"
26 
27 //
28 // This file contains generic event buffer producer implementation for adding variable length data
29 //
30 // Data format:
31 //
32 // Event Record buffer holds fixed size records
33 //
34 // |---------|---------|---------|---------|...|---------|
35 // | record1 | record2 | record3 | record4 |...| recordn |
36 // |---------|---------|---------|---------|...|---------|
37 //
38 // Variable length data buffer:
39 // The fixed event record can optionally contain a pointer to variable length data.
40 // This buffer stores the varlength data that doesn't fit in the fixed size records.
41 //
42 // |------------|--------|...|---------|
43 // | data2      | data4  |...| data n  |
44 // |------------|--------|...|---------|
45 //
46 
47 static NV_EVENT_BUFFER_RECORD* _eventBufferGetFreeRecord(EVENT_BUFFER_PRODUCER_INFO *);
48 static void _eventBufferAddVardata(EVENT_BUFFER_PRODUCER_INFO*, NvP64, NvU32, NV_EVENT_BUFFER_RECORD_HEADER*);
49 static void _eventBufferUpdateRecordBufferCount(EVENT_BUFFER_PRODUCER_INFO*);
50 static void _eventBufferUpdateVarRemaingSize(EVENT_BUFFER_PRODUCER_INFO* info);
51 
52 void
53 eventBufferInitRecordBuffer
54 (
55     EVENT_BUFFER_PRODUCER_INFO *info,
56     NV_EVENT_BUFFER_HEADER* pHeader,
57     NvP64 recordBuffAddr,
58     NvU32 recordSize,
59     NvU32 recordCount,
60     NvU32 bufferSize,
61     NvU32 notificationThreshold
62 )
63 {
64     RECORD_BUFFER_INFO* pRecordBuffer = &info->recordBuffer;
65     pRecordBuffer->pHeader = pHeader;
66     pRecordBuffer->recordBuffAddr = recordBuffAddr;
67     pRecordBuffer->recordSize = recordSize;
68     pRecordBuffer->totalRecordCount = recordCount;
69     pRecordBuffer->bufferSize = bufferSize;
70     pRecordBuffer->notificationThreshold = notificationThreshold;
71 }
72 
73 void
74 eventBufferInitVardataBuffer
75 (
76     EVENT_BUFFER_PRODUCER_INFO *info,
77     NvP64 vardataBuffAddr,
78     NvU32 bufferSize,
79     NvU32 notificationThreshold
80 )
81 {
82     VARDATA_BUFFER_INFO* pVardataBuffer = &info->vardataBuffer;
83     pVardataBuffer->vardataBuffAddr = vardataBuffAddr;
84     pVardataBuffer->bufferSize = bufferSize;
85     pVardataBuffer->notificationThreshold = notificationThreshold;
86     pVardataBuffer->get = 0;
87     pVardataBuffer->put = 0;
88     pVardataBuffer->remainingSize = bufferSize;
89 }
90 
91 void
92 eventBufferInitNotificationHandle(EVENT_BUFFER_PRODUCER_INFO *info, NvP64 notificationHandle)
93 {
94     info->notificationHandle = notificationHandle;
95 }
96 
97 void
98 eventBufferSetEnable(EVENT_BUFFER_PRODUCER_INFO *info, NvBool isEnabled)
99 {
100     info->isEnabled = isEnabled;
101 }
102 
103 void
104 eventBufferSetKeepNewest(EVENT_BUFFER_PRODUCER_INFO *info,NvBool isKeepNewest)
105 {
106     info->isKeepNewest = isKeepNewest;
107 }
108 
109 void
110 eventBufferUpdateRecordBufferGet(EVENT_BUFFER_PRODUCER_INFO *info, NvU32 get)
111 {
112     RECORD_BUFFER_INFO* pRecordBuffer = &info->recordBuffer;
113     pRecordBuffer->pHeader->recordGet = get;
114 
115     // used for notification
116     _eventBufferUpdateRecordBufferCount(info);
117 
118     // dropCounts get reset on every updateGet call
119     pRecordBuffer->pHeader->recordDropcount = 0;
120     pRecordBuffer->pHeader->vardataDropcount = 0;
121 
122 }
123 
124 void
125 _eventBufferUpdateRecordBufferCount(EVENT_BUFFER_PRODUCER_INFO *info)
126 {
127     RECORD_BUFFER_INFO* pRecordBuffer = &info->recordBuffer;
128     NV_EVENT_BUFFER_HEADER* pHeader = info->recordBuffer.pHeader;
129 
130     if (pHeader->recordGet <= pHeader->recordPut)
131         pHeader->recordCount = (pHeader->recordPut - pHeader->recordGet);
132     else
133         pHeader->recordCount = pHeader->recordPut + (pRecordBuffer->totalRecordCount - pHeader->recordGet);
134 }
135 
136 void
137 eventBufferUpdateVardataBufferGet(EVENT_BUFFER_PRODUCER_INFO *info, NvU32 get)
138 {
139     VARDATA_BUFFER_INFO* pVardataBuffer = &info->vardataBuffer;
140     pVardataBuffer->get = get;
141 
142     _eventBufferUpdateVarRemaingSize(info);
143 }
144 
145 NvU32
146 eventBufferGetRecordBufferCount(EVENT_BUFFER_PRODUCER_INFO *info)
147 {
148     return info->recordBuffer.totalRecordCount;
149 }
150 
151 NvU32
152 eventBufferGetVardataBufferCount(EVENT_BUFFER_PRODUCER_INFO *info)
153 {
154     return info->vardataBuffer.bufferSize;
155 }
156 
157 //
158 // eventBufferProducerAddEvent
159 //
160 // Adds an event to an event buffer
161 // This function is called after acquiring correct locks (depending on which module includes it)
162 // and bound checks for input parameters
163 // eventType : for RM this would be either 2080 subdevice events or 0000 system events
164 // eventSubtype: optional
165 // payloadSize and vardataSize must be 64 bit aligned
166 //
167 void
168 eventBufferProducerAddEvent
169 (
170     EVENT_BUFFER_PRODUCER_INFO *info,
171     NvU16 eventType,
172     NvU16 eventSubtype,
173     EVENT_BUFFER_PRODUCER_DATA *pData
174 )
175 {
176     NV_EVENT_BUFFER_RECORD *record;
177 
178     if (info->isEnabled)
179     {
180         record = _eventBufferGetFreeRecord(info);
181         if (record)
182         {
183             RECORD_BUFFER_INFO *pRecInfo = &info->recordBuffer;
184             NV_EVENT_BUFFER_HEADER *pHeader = pRecInfo->pHeader;
185             NvU32 putNext = (pHeader->recordPut + 1) % pRecInfo->totalRecordCount;
186 
187             record->recordHeader.type = eventType;
188             record->recordHeader.subtype = eventSubtype;
189 
190             if (pData->payloadSize)
191                  portMemCopy(record->inlinePayload, pData->payloadSize,
192                              NvP64_VALUE(pData->pPayload), pData->payloadSize);
193 
194             _eventBufferAddVardata(info, pData->pVardata, pData->vardataSize, &record->recordHeader);
195 
196             pHeader->recordPut = putNext;
197         }
198     }
199 }
200 
201 NV_EVENT_BUFFER_RECORD *
202 _eventBufferGetFreeRecord(EVENT_BUFFER_PRODUCER_INFO *info)
203 {
204     RECORD_BUFFER_INFO *pRecInfo = &info->recordBuffer;
205     NV_EVENT_BUFFER_HEADER *pHeader = pRecInfo->pHeader;
206     NvU32 recordOffset = 0;
207     NV_EVENT_BUFFER_RECORD *pFreeRecord = NULL;
208 
209     NvU32 putNext = (pHeader->recordPut + 1) % pRecInfo->totalRecordCount;
210 
211     if ((!info->isKeepNewest) && (putNext == pHeader->recordGet))
212     {
213         pHeader->recordDropcount++;
214     }
215     else
216     {
217         recordOffset = pHeader->recordPut * pRecInfo->recordSize;
218         pFreeRecord = (NV_EVENT_BUFFER_RECORD *)((NvUPtr)pRecInfo->recordBuffAddr + recordOffset);
219     }
220     return pFreeRecord;
221 }
222 
223 void
224 _eventBufferAddVardata
225 (
226     EVENT_BUFFER_PRODUCER_INFO *info,
227     NvP64 data,
228     NvU32 size,
229     NV_EVENT_BUFFER_RECORD_HEADER* recordHeader
230 )
231 {
232     VARDATA_BUFFER_INFO *pVarInfo = &info->vardataBuffer;
233     NV_EVENT_BUFFER_HEADER* pHeader = info->recordBuffer.pHeader;
234     NvU32 pVardataOffset;
235     NvU32 alignedSize = NV_ALIGN_UP(size, NV_EVENT_VARDATA_GRANULARITY);
236     NvU32 vardataOffsetEnd = pVarInfo->put + alignedSize;
237 
238     if (vardataOffsetEnd <= pVarInfo->bufferSize)
239     {
240         if ((!info->isKeepNewest) && (pVarInfo->remainingSize < alignedSize))
241             goto skip;
242 
243         pVardataOffset = pVarInfo->put;
244         recordHeader->varData = vardataOffsetEnd;
245     }
246     else
247     {
248         // wrap-around; the effective vardataPut=0, vardataOffsetEnd=size
249         vardataOffsetEnd = 0 + alignedSize;
250         if ((!info->isKeepNewest) && (pVarInfo->get <= vardataOffsetEnd))
251             goto skip;
252 
253         recordHeader->varData = vardataOffsetEnd | NV_EVENT_VARDATA_START_OFFSET_ZERO;
254         pVardataOffset = 0;
255     }
256 
257     if(size)
258     {
259          portMemCopy((void*)((NvUPtr)pVarInfo->vardataBuffAddr + pVardataOffset), size, NvP64_VALUE(data), size);
260 
261         if (alignedSize != size)
262         {
263             pVardataOffset += size;
264             portMemSet((void*)((NvUPtr)pVarInfo->vardataBuffAddr + pVardataOffset), 0, (alignedSize - size));
265         }
266     }
267 
268     pVarInfo->put = vardataOffsetEnd;
269     _eventBufferUpdateVarRemaingSize(info);
270     return;
271 
272 skip:
273     recordHeader->varData = pVarInfo->put;
274     pHeader->vardataDropcount += 1;
275 }
276 
277 void
278 _eventBufferUpdateVarRemaingSize(EVENT_BUFFER_PRODUCER_INFO* info)
279 {
280     VARDATA_BUFFER_INFO *pVarInfo = &info->vardataBuffer;
281 
282     if (!info->isKeepNewest)
283     {
284         if (pVarInfo->get <= pVarInfo->put)
285             pVarInfo->remainingSize = pVarInfo->get + (pVarInfo->bufferSize - pVarInfo->put);
286         else
287             pVarInfo->remainingSize = pVarInfo->get - pVarInfo->put;
288     }
289 }
290 
291 NvBool
292 eventBufferIsNotifyThresholdMet(EVENT_BUFFER_PRODUCER_INFO* info)
293 {
294     VARDATA_BUFFER_INFO *pVarInfo = &info->vardataBuffer;
295     RECORD_BUFFER_INFO* pRecInfo = &info->recordBuffer;
296     NV_EVENT_BUFFER_HEADER* pHeader = pRecInfo->pHeader;
297 
298     if (!info->isKeepNewest)
299     {
300         if (((pRecInfo->totalRecordCount - pHeader->recordCount) <= pRecInfo->notificationThreshold) ||
301             (pVarInfo->remainingSize <= pVarInfo->notificationThreshold))
302         {
303             return NV_TRUE;
304         }
305     }
306     return NV_FALSE;
307 }
308 
309