1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 1993-2014 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  * This file contains functions for dealing with dynamic allocation and
26  * management of resource handles.
27  *
28  * Note that dynamic handles are not suitable for all use cases.  If a
29  * handle is placed in the pushbuffer, and the pushbuffer will be
30  * replayed during channel recovery, the handle value must be kept
31  * constant.  For such handles, use an invariant handle value.
32  *
33  * We keep a bitmap of which handles we've used.
34  *
35  * Composition of an object handle:
36  * [31:16]  Client data
37  * [15:00]  Handle constant
38  */
39 
40 #include <stddef.h>
41 
42 #include "unix_rm_handle.h"
43 
44 #define INVALID_HANDLE 0
45 #define UNIX_RM_HANDLE_CLIENT_DATA_SHIFT         16
46 
47 /* Mask to AND only client data */
48 #define CLIENT_DATA_MASK ((~(NvU32)0) << UNIX_RM_HANDLE_CLIENT_DATA_SHIFT)
49 /* Mask to AND off client data */
50 #define HANDLE_MASK (~(CLIENT_DATA_MASK))
51 /* Handle 0 is reserved, so subtract one from a handle to get its index */
52 #define HANDLE_INDEX(_handle) (((_handle) - 1) & HANDLE_MASK)
53 
54 /* Bits to OR in for client data */
55 #define GET_CLIENT_DATA_BITS(_data) \
56     (((_data) << UNIX_RM_HANDLE_CLIENT_DATA_SHIFT))
57 
58 #define DWORD_FROM_HANDLE(_handle) (HANDLE_INDEX(_handle) >> 5)
59 #define BIT_FROM_HANDLE(_handle) (HANDLE_INDEX(_handle) & 0x1f)
60 
61 /* Check if a handle is used */
62 #define USED(_bitmap, _handle) \
63     ((_bitmap)[DWORD_FROM_HANDLE(_handle)] & (1U << BIT_FROM_HANDLE(_handle)))
64 /* Reserve a handle in the bitmap */
65 #define RESERVE(_bitmap, _handle) \
66     ((_bitmap)[DWORD_FROM_HANDLE(_handle)] |= (1U << BIT_FROM_HANDLE(_handle)))
67 /* Unreserve a handle in the bitmap */
68 #define UNRESERVE(_bitmap, _handle) \
69     ((_bitmap)[DWORD_FROM_HANDLE(_handle)] &= (~(1U << BIT_FROM_HANDLE(_handle))))
70 
71 #if defined(DEBUG)
72 static void
73 nvReportUnfreedUnixRmHandleAllocations(NVUnixRmHandleAllocatorPtr pAllocator);
74 #endif
75 
76 
UnixRmHandleMemset(void * ptr,char data,NvLength size)77 static void UnixRmHandleMemset(void *ptr, char data, NvLength size)
78 {
79     char *byte = (char *)ptr;
80     NvLength i;
81 
82     for (i = 0; i < size; i++) {
83         byte[i] = data;
84     }
85 }
86 
UnixRmHandleReallocBitmap(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 newMaxHandles)87 static NvBool UnixRmHandleReallocBitmap(NVUnixRmHandleAllocatorPtr pAllocator,
88                                         NvU32 newMaxHandles)
89 {
90     NvU32 *newBitmap;
91 #if defined(DEBUG)
92     NVUnixRmHandleAllocationPtr newAllocationTable;
93 #endif /* defined(DEBUG) */
94     const NvLength newMemSize = NV_UNIX_RM_HANDLE_BITMAP_SIZE(newMaxHandles) *
95         sizeof(*newBitmap);
96     const NvU32 oldBitmapSize =
97         NV_UNIX_RM_HANDLE_BITMAP_SIZE(pAllocator->maxHandles);
98 
99     /* New handle limit must fit in the bitmask */
100     if (newMaxHandles > GET_CLIENT_DATA_BITS(1)) {
101         return NV_FALSE;
102     }
103 
104     /* New handle limit must be a power of 2 */
105     nvUnixRmHandleAssert(!(newMaxHandles & (newMaxHandles - 1)));
106 
107     newBitmap = (NvU32 *)nvUnixRmHandleReallocMem(pAllocator->bitmap, newMemSize);
108 
109     if (!newBitmap) {
110         return NV_FALSE;
111     }
112 
113     UnixRmHandleMemset(&newBitmap[oldBitmapSize], 0,
114                        newMemSize - (oldBitmapSize * sizeof(*newBitmap)));
115     pAllocator->bitmap = newBitmap;
116 
117 #if defined(DEBUG)
118     newAllocationTable =
119         (NVUnixRmHandleAllocationPtr)
120         nvUnixRmHandleReallocMem(pAllocator->allocationTable,
121                                  newMaxHandles *
122                                  sizeof(*pAllocator->allocationTable));
123 
124     if (!newAllocationTable) {
125         /*
126          * Leave the new bitmap allocation in place.  If that realloc
127          * succeeded, the old bitmap allocation is gone, and it is at
128          * least big enough to hold the old pAllocator->maxHandles,
129          * since a shrinking of the allocation table shouldn't have
130          * failed, and maxHandles currently never decreases anyway.
131          */
132         nvUnixRmHandleAssert(newMaxHandles >= pAllocator->maxHandles);
133 
134         return NV_FALSE;
135     }
136 
137     pAllocator->allocationTable = newAllocationTable;
138 #endif /* defined(DEBUG) */
139 
140     pAllocator->maxHandles = newMaxHandles;
141 
142     return NV_TRUE;
143 }
144 
nvInitUnixRmHandleAllocator(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 rmClient,NvU32 clientData)145 NvBool nvInitUnixRmHandleAllocator(NVUnixRmHandleAllocatorPtr pAllocator,
146                                    NvU32 rmClient, NvU32 clientData)
147 {
148     nvUnixRmHandleAssert(pAllocator != NULL &&
149                          rmClient != 0 && clientData != 0);
150     nvUnixRmHandleAssert((clientData & 0x0000FFFF) == clientData);
151 
152     UnixRmHandleMemset(pAllocator, 0, sizeof(*pAllocator));
153 
154     pAllocator->rmClient = rmClient;
155     pAllocator->clientData = clientData;
156 
157     if (!UnixRmHandleReallocBitmap(pAllocator,
158                                    NV_UNIX_RM_HANDLE_INITIAL_HANDLES)) {
159         nvUnixRmHandleAssert(!"Failed to init RM handle allocator bitmap");
160         nvTearDownUnixRmHandleAllocator(pAllocator);
161 
162         return NV_FALSE;
163     }
164 
165     /*
166      * If the RM-provided client handle falls within the allocator range
167      * then reserve it up-front.
168      */
169     if ((pAllocator->rmClient & CLIENT_DATA_MASK) ==
170          GET_CLIENT_DATA_BITS(pAllocator->clientData)) {
171         NvU32 handleId = pAllocator->rmClient & HANDLE_MASK;
172 
173         if ((handleId <= pAllocator->maxHandles) &&
174             (handleId != INVALID_HANDLE)) {
175             RESERVE(pAllocator->bitmap, handleId);
176         }
177     }
178 
179     return NV_TRUE;
180 }
181 
182 /*
183  * nvGenerateUnixRmHandleInternal()
184  *   Return a unique, random handle. Be sure to free the handle
185  *   when you're done with it! Returns 0 if we run out of handles.
186  */
nvGenerateUnixRmHandleInternal(NVUnixRmHandleAllocatorPtr pAllocator)187 NvU32 nvGenerateUnixRmHandleInternal(NVUnixRmHandleAllocatorPtr pAllocator)
188 {
189     NvU32 handleId;
190     NvU32 handle;
191 
192     nvUnixRmHandleAssert(pAllocator != NULL &&
193                          pAllocator->rmClient != 0 &&
194                          pAllocator->clientData != 0);
195 
196     /* Find free handle */
197     handleId = 1;
198     while ((handleId <= pAllocator->maxHandles) &&
199            USED(pAllocator->bitmap, handleId)) {
200         handleId++;
201     }
202 
203     if (handleId > pAllocator->maxHandles) {
204         if (!UnixRmHandleReallocBitmap(pAllocator, pAllocator->maxHandles * 2)) {
205             nvUnixRmHandleAssert(!"Failed to grow RM handle allocator bitmap");
206             return INVALID_HANDLE;
207         }
208     }
209 
210     nvUnixRmHandleAssert(!USED(pAllocator->bitmap, handleId));
211 
212     RESERVE(pAllocator->bitmap, handleId);
213 
214     handle = GET_CLIENT_DATA_BITS(pAllocator->clientData) | handleId;
215 
216     nvUnixRmHandleAssert(handle != pAllocator->rmClient);
217 
218     return handle;
219 }
220 
221 /*
222  * nvFreeUnixRmHandleInternal()
223  *   Mark the handle passed in as free in the bitmap.
224  */
nvFreeUnixRmHandleInternal(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 unixHandle)225 void nvFreeUnixRmHandleInternal(NVUnixRmHandleAllocatorPtr pAllocator,
226                                 NvU32 unixHandle)
227 {
228     NvU32 handle = unixHandle & HANDLE_MASK;
229 
230     if (!unixHandle) {
231         return;
232     }
233 
234     nvUnixRmHandleAssert(pAllocator != NULL &&
235                          pAllocator->rmClient != 0 && pAllocator->clientData != 0);
236 
237     nvUnixRmHandleAssert(USED(pAllocator->bitmap, handle));
238 
239     UNRESERVE(pAllocator->bitmap, handle);
240 }
241 
242 /*
243  * This function just makes sure we freed all of the handles we allocated, for
244  * debugging purposes.
245  */
nvTearDownUnixRmHandleAllocator(NVUnixRmHandleAllocatorPtr pAllocator)246 void nvTearDownUnixRmHandleAllocator(NVUnixRmHandleAllocatorPtr pAllocator)
247 {
248     if (pAllocator == NULL) {
249         return;
250     }
251 
252     /*
253      * If the RM-provided client handle falls within the allocator range,
254      * then it is reserved up-front. so make sure that it is get unreserved
255      * before teardown.
256      */
257     if ((pAllocator->rmClient & CLIENT_DATA_MASK) ==
258         GET_CLIENT_DATA_BITS(pAllocator->clientData)) {
259         NvU32 handleId = pAllocator->rmClient & HANDLE_MASK;
260 
261         if ((handleId <= pAllocator->maxHandles) &&
262             (handleId != INVALID_HANDLE)) {
263             UNRESERVE(pAllocator->bitmap, handleId);
264         }
265     }
266 
267 #if defined(DEBUG)
268     nvReportUnfreedUnixRmHandleAllocations(pAllocator);
269     nvUnixRmHandleFreeMem(pAllocator->allocationTable);
270 #endif
271 
272     nvUnixRmHandleFreeMem(pAllocator->bitmap);
273 
274     UnixRmHandleMemset(pAllocator, 0, sizeof(*pAllocator));
275 }
276 
277 /*
278  * Handle allocation tracking code; in a debug build, the below
279  * functions wrap the actual allocation functions above.
280  */
281 
282 #if defined(DEBUG)
283 
284 #define UNIX_RM_HANDLE_ALLOC_LABEL "NVIDIA UNIX RM HANDLE TRACKER: "
285 
286 static NVUnixRmHandleAllocationPtr
FindUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 handle)287 FindUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator, NvU32 handle)
288 {
289     if (((handle & HANDLE_MASK) == INVALID_HANDLE) ||
290         ((handle & HANDLE_MASK) > pAllocator->maxHandles)) {
291         return NULL;
292     }
293 
294     return &pAllocator->allocationTable[HANDLE_INDEX(handle)];
295 }
296 
RecordUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 handle,const char * file,int line)297 static void RecordUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator,
298                                          NvU32 handle, const char *file, int line)
299 {
300     /* Find a free allocation table slot. */
301     NVUnixRmHandleAllocationPtr alloc = FindUnixRmHandleAllocation(pAllocator, handle);
302 
303     if (!alloc) {
304         nvUnixRmHandleLogMsg(NV_UNIX_RM_HANDLE_DEBUG_ERROR,
305                              UNIX_RM_HANDLE_ALLOC_LABEL
306                              "NVUnixRmHandleAllocator is corrupted."
307                              "(table entry not found for handle)");
308         return;
309     }
310 
311     nvUnixRmHandleLogMsg(NV_UNIX_RM_HANDLE_DEBUG_VERBOSE,
312                          UNIX_RM_HANDLE_ALLOC_LABEL
313                          "Recording handle allocation: 0x%08x (%s:%d)",
314                          handle, file, line);
315 
316     alloc->file   = file;
317     alloc->line   = line;
318 }
319 
FreeUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 handle)320 static void FreeUnixRmHandleAllocation(NVUnixRmHandleAllocatorPtr pAllocator,
321                                        NvU32 handle)
322 {
323     NVUnixRmHandleAllocationPtr alloc =
324         FindUnixRmHandleAllocation(pAllocator, handle);
325 
326     if (!alloc) {
327         return;
328     }
329 
330     nvUnixRmHandleLogMsg(NV_UNIX_RM_HANDLE_DEBUG_VERBOSE,
331                          UNIX_RM_HANDLE_ALLOC_LABEL
332                          "Freeing handle allocation: 0x%08x (%s:%d)",
333                          handle, alloc->file, alloc->line);
334 
335     UnixRmHandleMemset(alloc, 0, sizeof(*alloc));
336 }
337 
338 
339 NvU32
nvDebugGenerateUnixRmHandle(NVUnixRmHandleAllocatorPtr pAllocator,const char * file,int line)340 nvDebugGenerateUnixRmHandle(NVUnixRmHandleAllocatorPtr pAllocator,
341                             const char *file, int line)
342 {
343     NvU32 handle = nvGenerateUnixRmHandleInternal(pAllocator);
344 
345     RecordUnixRmHandleAllocation(pAllocator, handle, file, line);
346     return handle;
347 }
348 
nvDebugFreeUnixRmHandle(NVUnixRmHandleAllocatorPtr pAllocator,NvU32 handle)349 void nvDebugFreeUnixRmHandle(NVUnixRmHandleAllocatorPtr pAllocator, NvU32 handle)
350 {
351     if (!handle) {
352         return;
353     }
354 
355     FreeUnixRmHandleAllocation(pAllocator, handle);
356 
357     nvFreeUnixRmHandleInternal(pAllocator, handle);
358 }
359 
nvReportUnfreedUnixRmHandleAllocations(NVUnixRmHandleAllocatorPtr pAllocator)360 void nvReportUnfreedUnixRmHandleAllocations(NVUnixRmHandleAllocatorPtr pAllocator)
361 {
362     NvU32 handleId;
363 
364     for (handleId = 1; handleId <= pAllocator->maxHandles; handleId++) {
365         if (USED(pAllocator->bitmap, handleId)) {
366 
367             NVUnixRmHandleAllocationPtr alloc =
368                 FindUnixRmHandleAllocation(pAllocator, handleId);
369 
370             if (alloc == NULL) {
371                 continue;
372             }
373 
374             nvUnixRmHandleLogMsg(NV_UNIX_RM_HANDLE_DEBUG_MSG,
375                                  UNIX_RM_HANDLE_ALLOC_LABEL
376                                  "Unfreed handle ID allocation: 0x%08x (%s:%d)",
377                                  handleId,
378                                  alloc->file,
379                                  alloc->line);
380         }
381     }
382 }
383 
384 #endif /* DEBUG */
385 
386