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