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