1 /*******************************************************************************
2     Copyright (c) 2019-2023 NVidia Corporation
3 
4     Permission is hereby granted, free of charge, to any person obtaining a copy
5     of this software and associated documentation files (the "Software"), to
6     deal in the Software without restriction, including without limitation the
7     rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8     sell copies of the Software, and to permit persons to whom the Software is
9     furnished to do so, subject to the following conditions:
10 
11     The above copyright notice and this permission notice shall be
12     included in all copies or substantial portions of the Software.
13 
14     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20     DEALINGS IN THE SOFTWARE.
21 *******************************************************************************/
22 
23 #include "nvlink.h"
24 #include "nvlink_export.h"
25 #include "nvlink_os.h"
26 #include "nvlink_ctx.h"
27 #include "nvlink_helper.h"
28 
29 #include "nvlink_lock.h"
30 
31 nvlink_lib_context nvlinkLibCtx = {0};
32 
33 /*
34  * Initialize the nvlink core library
35  *
36  * return NVL_SUCCESS if the library is initialized successfully
37  */
38 NvlStatus
39 nvlink_lib_initialize(void)
40 {
41     NvlStatus lock_status = NVL_SUCCESS;
42 
43     if (nvlinkLibCtx.nv_devicelist_head.initialized == 0)
44     {
45         // Allocate top-level lock
46         lock_status = nvlink_lib_top_lock_alloc();
47         if (lock_status != NVL_SUCCESS)
48         {
49             NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
50                 "%s: Failed to allocate top-level lock\n",
51                 __FUNCTION__));
52 
53             return lock_status;
54          }
55 
56         // Acquire top-level lock
57         lock_status = nvlink_lib_top_lock_acquire();
58         if (lock_status != NVL_SUCCESS)
59         {
60             NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
61                 "%s: Failed to acquire top-level lock\n",
62                 __FUNCTION__));
63 
64             return lock_status;
65          }
66 
67         // Top-level lock is now acquired
68 
69         // Initialize the device list head
70         nvListInit(&nvlinkLibCtx.nv_devicelist_head.link_list);
71         nvListInit(&nvlinkLibCtx.nv_devicelist_head.node);
72         nvlinkLibCtx.nv_devicelist_head.initialized = 1;
73 
74         // Initialize the intranode connection list head
75         nvListInit(&nvlinkLibCtx.nv_intraconn_head.node);
76 
77         // Initialize the internode connection list head
78         nvListInit(&nvlinkLibCtx.nv_interconn_head.node);
79 
80         // Initialize registered and connected links to 0
81         nvlinkLibCtx.registeredEndpoints   = 0;
82         nvlinkLibCtx.connectedEndpoints    = 0;
83         nvlinkLibCtx.notConnectedEndpoints = 0;
84 
85         //
86         // Initialize fabric node id to max value until set
87         // by ioctl interface
88         //
89         nvlinkLibCtx.nodeId = NV_U16_MAX ;
90 
91         // Release top-level lock
92         nvlink_lib_top_lock_release();
93     }
94 
95     return NVL_SUCCESS;
96 }
97 
98 /*
99  * Unload the nvlink core library
100  *
101  * return NVL_SUCCESS if the library is unloaded successfully
102  */
103 NvlStatus
104 nvlink_lib_unload(void)
105 {
106     NvlStatus lock_status = NVL_SUCCESS;
107 
108     if (nvlink_lib_is_initialized())
109     {
110         // Acquire top-level lock
111         lock_status = nvlink_lib_top_lock_acquire();
112         if (lock_status != NVL_SUCCESS)
113         {
114             NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
115                 "%s: Failed to acquire top-level lock\n",
116                 __FUNCTION__));
117 
118             return lock_status;
119          }
120 
121         // Top-level lock is now acquired
122 
123         // Check if there are no devices registered
124         if (nvlink_lib_is_device_list_empty())
125         {
126             nvlinkLibCtx.nv_devicelist_head.initialized = 0;
127         }
128 
129         // Release and free top-level lock
130         nvlink_lib_top_lock_release();
131         nvlink_lib_top_lock_free();
132     }
133 
134     return NVL_SUCCESS;
135 }
136 
137 /*
138  * Check if the nvlink core library is initialized
139  *
140  * return NV_TRUE if the core library is already initialized
141  */
142 NvBool
143 nvlink_lib_is_initialized(void)
144 {
145     return nvlinkLibCtx.nv_devicelist_head.initialized;
146 }
147 
148 /*
149  * Check if there are any devices registered
150  *
151  * return NV_TRUE if there are devices registered in the core library
152  */
153 NvBool
154 nvlink_lib_is_device_list_empty(void)
155 {
156     NvBool isEmpty = NV_TRUE;
157 
158     isEmpty = nvListIsEmpty(&nvlinkLibCtx.nv_devicelist_head.node);
159 
160     return isEmpty;
161 }
162 
163 /*
164  * Get if a device registerd to the nvlink corelib has a reduced nvlink config
165  *
166  * return NV_TRUE if there is a device registered to the core library that is a reduced
167  * nvlink config device
168  */
169 NvBool
170 nvlink_lib_is_registerd_device_with_reduced_config(void)
171 {
172     NvlStatus lock_status = NVL_SUCCESS;
173     nvlink_device *dev    = NULL;
174 
175     // Acquire top-level lock
176     lock_status = nvlink_lib_top_lock_acquire();
177     if (lock_status != NVL_SUCCESS)
178     {
179         NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
180             "%s: Failed to acquire top-level lock\n",
181             __FUNCTION__));
182 
183         return NV_FALSE;
184      }
185 
186     FOR_EACH_DEVICE_REGISTERED(dev, nvlinkLibCtx.nv_devicelist_head, node)
187     {
188         if (dev->bReducedNvlinkConfig == NV_TRUE)
189         {
190             return NV_TRUE;
191         }
192     }
193 
194     // Release and free top-level lock
195     nvlink_lib_top_lock_release();
196     nvlink_lib_top_lock_free();
197 
198     return NV_FALSE;
199 }
200 
201 /*
202 * Get the number of devices that have the device type deviceType
203 */
204 NvlStatus
205 nvlink_lib_return_device_count_by_type
206 (
207     NvU32 deviceType,
208     NvU32 *numDevices
209 )
210 {
211     NvlStatus lock_status = NVL_SUCCESS;
212     nvlink_device *dev = NULL;
213     NvU32 device_count = 0;
214 
215     if (nvlink_lib_is_initialized())
216     {
217         // Acquire top-level lock
218         lock_status = nvlink_lib_top_lock_acquire();
219         if (lock_status != NVL_SUCCESS)
220         {
221             NVLINK_PRINT((DBG_MODULE_NVLINK_CORE, NVLINK_DBG_LEVEL_ERRORS,
222                 "%s: Failed to acquire top-level lock\n",
223                 __FUNCTION__));
224 
225             return lock_status;
226          }
227 
228         // Top-level lock is now acquired
229 
230         // Loop through device list
231         FOR_EACH_DEVICE_REGISTERED(dev, nvlinkLibCtx.nv_devicelist_head, node)
232         {
233             if (dev->type == deviceType)
234             {
235                 device_count++;
236             }
237         }
238 
239         // Release top-level lock
240         nvlink_lib_top_lock_release();
241     }
242     *numDevices = device_count;
243     return NVL_SUCCESS;
244 }
245