1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2020 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 "nvlink.h"
25 #include "nvtypes.h"
26 #include "nvlink_export.h"
27 #include "nvlink_os.h"
28 #include "nvlink_ctx.h"
29 #include "nvlink_helper.h"
30 #include "nvlink_lock.h"
31
32 //
33 // Only enabling top level locking for linux as required by Bug 4108674.
34 // Per link locking is still disabled at all times. It will be enabled
35 // after other locking related clean up is done.
36 //
37
38 static void _sort_links(nvlink_link **, NvU32, NvBool (*)(void *, void *));
39 static NvBool _compare(void *, void *);
40
41 #if defined(NV_LINUX)
42 #undef TOP_LEVEL_LOCKING_DISABLED
43 # define TOP_LEVEL_LOCKING_DISABLED 0
44 #endif /* defined(NV_LINUX) */
45 /*
46 * Allocate top level lock. Return NVL_SUCCESS if
47 * the lock was allocated else return NVL_ERR_GENERIC.
48 */
49 NvlStatus
nvlink_lib_top_lock_alloc(void)50 nvlink_lib_top_lock_alloc(void)
51 {
52 if (TOP_LEVEL_LOCKING_DISABLED)
53 {
54 return NVL_SUCCESS;
55 }
56
57 void *top_lock = NULL;
58
59 // Check if top level lock is already allocated
60 if (nvlinkLibCtx.topLevelLock != NULL)
61 {
62 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
63 "%s: Top-level lock already allocated\n",
64 __FUNCTION__));
65
66 return NVL_ERR_GENERIC;
67 }
68
69 top_lock = nvlink_allocLock();
70 if (NULL == top_lock)
71 {
72 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
73 "%s: Failed to allocate top-level lock\n",
74 __FUNCTION__));
75
76 return NVL_ERR_GENERIC;
77 }
78
79 nvlinkLibCtx.topLevelLock = top_lock;
80
81 // Top-level lock allocated
82
83 return NVL_SUCCESS;
84 }
85
86 /*
87 * Free top level lock. Return NVL_SUCCESS if
88 * the lock was freed else return NVL_ERR_GENERIC.
89 */
90 NvlStatus
nvlink_lib_top_lock_free(void)91 nvlink_lib_top_lock_free(void)
92 {
93 if (TOP_LEVEL_LOCKING_DISABLED)
94 {
95 return NVL_SUCCESS;
96 }
97
98 // Check if already freed
99 if (NULL == nvlinkLibCtx.topLevelLock)
100 {
101 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
102 "%s: Top-level lock not allocated/already freed\n",
103 __FUNCTION__));
104
105 return NVL_ERR_GENERIC;
106 }
107
108 nvlink_freeLock(nvlinkLibCtx.topLevelLock);
109 nvlinkLibCtx.topLevelLock = NULL;
110
111 // Top-level lock freed
112
113 return NVL_SUCCESS;
114 }
115
116 /*
117 * Allocate per-link lock. Return NVL_SUCCESS if
118 * the lock was allocated else return NVL_ERR_GENERIC.
119 */
120 NvlStatus
nvlink_lib_link_lock_alloc(nvlink_link * link)121 nvlink_lib_link_lock_alloc
122 (
123 nvlink_link *link
124 )
125 {
126 if (PER_LINK_LOCKING_DISABLED)
127 {
128 return NVL_SUCCESS;
129 }
130
131 void *link_lock = NULL;
132
133 // Check if already allocated
134 if (link->linkLock != NULL)
135 {
136 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
137 "%s: Link lock already allocated on this link\n",
138 __FUNCTION__));
139
140 return NVL_ERR_GENERIC;
141 }
142
143 link_lock = nvlink_allocLock();
144 if (NULL == link_lock)
145 {
146 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
147 "%s: Failed to allocate link lock\n",
148 __FUNCTION__));
149
150 return NVL_ERR_GENERIC;
151 }
152
153 // Link lock allocated
154 link->linkLock = link_lock;
155
156 return NVL_SUCCESS;
157 }
158
159 /*
160 * Free per-link lock. Return NVL_SUCCESS if
161 * the lock was freed else return NVL_ERR_GENERIC.
162 */
163 NvlStatus
nvlink_lib_link_lock_free(nvlink_link * link)164 nvlink_lib_link_lock_free
165 (
166 nvlink_link *link
167 )
168 {
169 if (PER_LINK_LOCKING_DISABLED)
170 {
171 return NVL_SUCCESS;
172 }
173
174 // Check if already freed
175 if (NULL == link->linkLock)
176 {
177 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
178 "%s: Link lock not allocated/already freed\n",
179 __FUNCTION__));
180
181 return NVL_ERR_GENERIC;
182 }
183
184 nvlink_freeLock(link->linkLock);
185 link->linkLock = NULL;
186
187 // Link lock freed
188
189 return NVL_SUCCESS;
190 }
191
192 /*
193 * Acquire top level lock. Return NVL_SUCCESS if
194 * the lock was acquired else return NVL_ERR_STATE_IN_USE.
195 */
196 NvlStatus
nvlink_lib_top_lock_acquire(void)197 nvlink_lib_top_lock_acquire(void)
198 {
199 if (TOP_LEVEL_LOCKING_DISABLED)
200 {
201 return NVL_SUCCESS;
202 }
203
204 // Check if top-level lock is allocated before attempting to acquire
205 if (NULL == nvlinkLibCtx.topLevelLock)
206 {
207 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
208 "%s: Top-level lock is not allocated\n",
209 __FUNCTION__));
210
211 return NVL_ERR_GENERIC;
212 }
213
214 //
215 // ToDo: Check if the lock was acquired succesfully
216 // Currently the nvlink_acquireLock function doesn't report failures
217 //
218 nvlink_acquireLock(nvlinkLibCtx.topLevelLock);
219
220 return NVL_SUCCESS;
221 }
222
223 /*
224 * Release top level lock. Return NVL_SUCCESS if
225 * the lock was released else return NVL_ERR_GENERIC.
226 */
227 NvlStatus
nvlink_lib_top_lock_release(void)228 nvlink_lib_top_lock_release(void)
229 {
230 if (TOP_LEVEL_LOCKING_DISABLED)
231 {
232 return NVL_SUCCESS;
233 }
234
235 // Check if top-level lock is allocated before attempting to release
236 if (NULL == nvlinkLibCtx.topLevelLock)
237 {
238 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
239 "%s: Top-level lock is not allocated\n",
240 __FUNCTION__));
241
242 return NVL_ERR_GENERIC;
243 }
244
245 //
246 // ToDo: Check if the lock was released succesfully
247 // Currently the nvlink_releaseLock function doesn't report failures
248 //
249 nvlink_releaseLock(nvlinkLibCtx.topLevelLock);
250
251 return NVL_SUCCESS;
252 }
253
254 /*
255 * Sort the array of links in order of (DBDF, link#) -
256 * lowest to highest and acquire link locks.
257 * Return NVL_SUCCESS if all the link locks were acquired.
258 * Else if any link lock failed to be acquired, release
259 * all acquired link locks and return NVL_ERR_STATE_IN_USE.
260 */
261 NvlStatus
nvlink_lib_link_locks_acquire(nvlink_link ** links,int numLinks)262 nvlink_lib_link_locks_acquire
263 (
264 nvlink_link **links,
265 int numLinks
266 )
267 {
268 if (PER_LINK_LOCKING_DISABLED)
269 {
270 return NVL_SUCCESS;
271 }
272
273 int i;
274 nvlink_link *link_prev = NULL;
275
276 // Check if array of links is already empty before attempting to release.
277 if ((NULL == links) || (numLinks == 0))
278 {
279 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
280 "%s: Could not release the link locks. Link array is empty !\n",
281 __FUNCTION__));
282
283 return NVL_ERR_GENERIC;
284 }
285
286 // Sort the link array in increasing order of (DBDF, link#)
287 _sort_links(links, numLinks, _compare);
288
289 for (i = 0; i < numLinks; i++)
290 {
291 //
292 // Don't acquire locks on loop back links twice since the current link is
293 // the same as the previous one
294 //
295 if (links[i] != link_prev)
296 {
297 nvlink_acquireLock(links[i]->linkLock);
298
299 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_INFO,
300 "%s: Acquire link lock for dom:%d bus:%d dev:%d fun:%d link:%d\n",
301 __FUNCTION__,
302
303 links[i]->dev->pciInfo.domain, links[i]->dev->pciInfo.bus,
304 links[i]->dev->pciInfo.device, links[i]->dev->pciInfo.function,
305 links[i]->linkNumber));
306 }
307
308 link_prev = links[i];
309 }
310
311 //
312 // ToDo: Check if the lock was acquired succesfully
313 // Currently the nvlink_acquireLock function doesn't report failures
314 //
315 return NVL_SUCCESS;
316 }
317
318 /*
319 * Loop over all the links and call nvlink_releaseLock(links[i]->linkLock).
320 * Return NVL_SUCCESS if all the link locks were released.
321 * Else if any link lock failed to be released return NVL_ERR_GENERIC.
322 */
323 NvlStatus
nvlink_lib_link_locks_release(nvlink_link ** links,int numLinks)324 nvlink_lib_link_locks_release
325 (
326 nvlink_link **links,
327 int numLinks
328 )
329 {
330 if (PER_LINK_LOCKING_DISABLED)
331 {
332 return NVL_SUCCESS;
333 }
334
335 int i;
336 nvlink_link *link_prev = NULL;
337
338 // Check if array of links is already empty before attempting to release.
339 if ((NULL == links) || (numLinks == 0))
340 {
341 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
342 "%s: Could not release the link locks. Link array is empty !\n",
343 __FUNCTION__));
344
345 return NVL_ERR_GENERIC;
346 }
347
348 // Sort the link array in increasing order of (DBDF, link#)
349 _sort_links(links, numLinks, _compare);
350
351 for (i = 0; i < numLinks; i++)
352 {
353 //
354 // Don't release locks on loop back links twice since the current link is
355 // the same as the previous one
356 //
357 if (links[i] != link_prev)
358 {
359 nvlink_releaseLock(links[i]->linkLock);
360
361 NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_INFO,
362 "%s: Release link lock for dom:%d bus:%d dev:%d fun:%d link:%d\n",
363 __FUNCTION__,
364
365 links[i]->dev->pciInfo.domain, links[i]->dev->pciInfo.bus,
366 links[i]->dev->pciInfo.device, links[i]->dev->pciInfo.function,
367 links[i]->linkNumber));
368 }
369
370 link_prev = links[i];
371 }
372
373 //
374 // ToDo: Check if the lock was released succesfully
375 // Currently the nvlink_releaseLock function doesn't report failures
376 //
377 return NVL_SUCCESS;
378 }
379
380 /*
381 * Sorts the links in the increasing order of DBDF, link#
382 */
383 static void
_sort_links(nvlink_link ** links,NvU32 numLinks,NvBool (* compare)(void *,void *))384 _sort_links
385 (
386 nvlink_link **links,
387 NvU32 numLinks,
388 NvBool (*compare)(void *, void *)
389 )
390 {
391 nvlink_link *temp = NULL;
392 NvU32 i, j;
393
394 for (i = 0; i < numLinks; i++)
395 {
396 for (j = i + 1; j < numLinks; j++)
397 {
398 if (_compare(links[j], links[i]))
399 {
400 temp = links[i];
401 links[i] = links[j];
402 links[j] = temp;
403 }
404 }
405 }
406 }
407
408 /*
409 * Compare function for _nvlink_sort - compares DBDF, link#
410 */
411 static NvBool
_compare(void * link1,void * link2)412 _compare
413 (
414 void *link1,
415 void *link2
416 )
417 {
418 nvlink_link *l1 = (nvlink_link *) link1;
419 nvlink_link *l2 = (nvlink_link *) link2;
420
421 // Compare link domains
422 if (l1->dev->pciInfo.domain < l2->dev->pciInfo.domain)
423 {
424 return NV_TRUE;
425 }
426
427 if (l1->dev->pciInfo.domain > l2->dev->pciInfo.domain)
428 {
429 return NV_FALSE;
430 }
431
432 // Domain is same for devices of links. Compare bus next
433
434 // Compare link buses
435 if (l1->dev->pciInfo.bus < l2->dev->pciInfo.bus)
436 {
437 return NV_TRUE;
438 }
439
440 if (l1->dev->pciInfo.bus > l2->dev->pciInfo.bus)
441 {
442 return NV_FALSE;
443 }
444
445 // Bus is same for devices of links. Compare device next
446
447 // Compare link devices
448 if (l1->dev->pciInfo.device < l2->dev->pciInfo.device)
449 {
450 return NV_TRUE;
451 }
452
453 if (l1->dev->pciInfo.device > l2->dev->pciInfo.device)
454 {
455 return NV_FALSE;
456 }
457
458 // Device is same for devices of links. Compare function next
459
460 // Compare link functions
461 if (l1->dev->pciInfo.function < l2->dev->pciInfo.function)
462 {
463 return NV_TRUE;
464 }
465
466 if (l1->dev->pciInfo.function > l2->dev->pciInfo.function)
467 {
468 return NV_FALSE;
469 }
470
471 // DBDF is same for both the links. Check the link#
472
473 // Compare link numbers
474 if (l1->linkNumber < l2->linkNumber)
475 {
476 return NV_TRUE;
477 }
478 else
479 {
480 return NV_FALSE;
481 }
482 }
483