1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2022 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 "os/os.h"
25 #include "kernel/gpu/nvlink/kernel_nvlink.h"
26 #include "kernel/gpu/nvlink/kernel_ioctrl.h"
27 #include "gpu/gpu.h"
28 #include "gpu/mem_mgr/mem_mgr.h"
29 #include "nverror.h"
30 #include "objtmr.h"
31 
32 /*!
33  * @brief Check if ALI is supported for the given device
34  *
35  * @param[in]  pGpu           OBJGPU pointer
36  * @param[in]  pKernelNvlink  KernelNvlink pointer
37  */
38 NV_STATUS
39 knvlinkIsAliSupported_GH100
40 (
41     OBJGPU       *pGpu,
42     KernelNvlink *pKernelNvlink
43 )
44 {
45     NvU32 status = NV_OK;
46 
47     NV2080_CTRL_NVLINK_GET_ALI_ENABLED_PARAMS params;
48 
49     portMemSet(&params, 0, sizeof(params));
50 
51     // Initialize to default settings
52     params.bEnableAli = NV_FALSE;
53 
54     status = knvlinkExecGspRmRpc(pGpu, pKernelNvlink,
55                                  NV2080_CTRL_CMD_NVLINK_GET_ALI_ENABLED,
56                                  (void *)&params, sizeof(params));
57     if (status != NV_OK)
58     {
59         NV_PRINTF(LEVEL_ERROR, "Failed to get ALI enablement status!\n");
60         return status;
61     }
62 
63     pKernelNvlink->bEnableAli = params.bEnableAli;
64 
65     return status;
66 }
67 
68 /*!
69  * @brief   Validates fabric base address.
70  *
71  * @param[in]  pGpu           OBJGPU pointer
72  * @param[in]  pKernelNvlink  KernelNvlink pointer
73  * @param[in]  fabricBaseAddr Address to be validated
74  *
75  * @returns On success, NV_OK.
76  *          On failure, returns NV_ERR_XXX.
77  */
78 NV_STATUS
79 knvlinkValidateFabricBaseAddress_GH100
80 (
81     OBJGPU       *pGpu,
82     KernelNvlink *pKernelNvlink,
83     NvU64         fabricBaseAddr
84 )
85 {
86     MemoryManager *pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu);
87     NvU64          fbSizeBytes;
88 
89     fbSizeBytes = pMemoryManager->Ram.fbTotalMemSizeMb << 20;
90 
91     //
92     // Hopper SKUs will be paired with NVSwitches (Limerock-next) supporting 2K
93     // mapslots that can cover 512GB each. Make sure that the fabric base
94     // address being used is valid to cover whole frame buffer.
95     //
96 
97     // Check if fabric address is aligned to mapslot size.
98     if (fabricBaseAddr & (NVBIT64(39) - 1))
99     {
100         return NV_ERR_INVALID_ARGUMENT;
101     }
102 
103     // Align fbSize to mapslot size.
104     fbSizeBytes = RM_ALIGN_UP(fbSizeBytes, NVBIT64(39));
105 
106     return NV_OK;
107 }
108 
109 /*!
110  * @brief Do post setup on nvlink peers
111  *
112  * @param[in] pGpu           OBJGPU pointer
113  * @param[in] pKernelNvlink  KernelNvlink pointer
114  */
115 NV_STATUS
116 knvlinkPostSetupNvlinkPeer_GH100
117 (
118     OBJGPU       *pGpu,
119     KernelNvlink *pKernelNvlink
120 )
121 {
122     NvU32 status = NV_OK;
123     NV2080_CTRL_NVLINK_POST_SETUP_NVLINK_PEER_PARAMS postSetupNvlinkPeerParams;
124 
125     portMemSet(&postSetupNvlinkPeerParams, 0, sizeof(postSetupNvlinkPeerParams));
126 
127     postSetupNvlinkPeerParams.peerMask = (1 << NVLINK_MAX_PEERS_SW) - 1;
128 
129     status = knvlinkExecGspRmRpc(pGpu, pKernelNvlink,
130                                  NV2080_CTRL_CMD_NVLINK_POST_SETUP_NVLINK_PEER,
131                                  (void *)&postSetupNvlinkPeerParams,
132                                  sizeof(postSetupNvlinkPeerParams));
133     if (status != NV_OK)
134     {
135         NV_PRINTF(LEVEL_ERROR,
136                   "Failed to program post active settings and bufferready!\n");
137         return status;
138     }
139 
140     return status;
141 }
142 
143 /*!
144  * @brief Discover all links that are training or have been
145  *        trained on both GPUs
146  *
147  * @param[in] pGpu           OBJGPU pointer for local GPU
148  * @param[in] pKernelNvlink  KernelNvlink pointer
149  * @param[in] pPeerGpu       OBJGPU pointer for remote GPU
150  *
151  * @return  NV_OK if links are detected to be training
152  */
153 NV_STATUS
154 knvlinkDiscoverPostRxDetLinks_GH100
155 (
156     OBJGPU       *pGpu,
157     KernelNvlink *pKernelNvlink,
158     OBJGPU       *pPeerGpu
159 )
160 {
161     NV_STATUS status = NV_ERR_NOT_SUPPORTED;
162 
163 #if defined(INCLUDE_NVLINK_LIB)
164 
165     OBJGPU       *pGpu0          = pGpu;
166     OBJGPU       *pGpu1          = pPeerGpu;
167     KernelNvlink *pKernelNvlink0 = GPU_GET_KERNEL_NVLINK(pGpu0);
168     KernelNvlink *pKernelNvlink1 = NULL;
169 
170     if (pGpu1 == NULL)
171     {
172         NV_PRINTF(LEVEL_ERROR, "Invalid pPeerGpu.\n");
173 
174         return NV_ERR_INVALID_ARGUMENT;
175     }
176     else if ((pGpu0 == pGpu1) &&
177              (pGpu0->getProperty(pGpu0, PDB_PROP_GPU_NVLINK_P2P_LOOPBACK_DISABLED)))
178     {
179         // P2P over loopback links are disabled through regkey overrides
180         NV_PRINTF(LEVEL_INFO, "loopback P2P on GPU%u disabled by regkey\n",
181                   gpuGetInstance(pGpu0));
182 
183         return NV_ERR_NOT_SUPPORTED;
184     }
185     else
186     {
187         pKernelNvlink1 = GPU_GET_KERNEL_NVLINK(pGpu1);
188     }
189 
190     if (pKernelNvlink1 == NULL)
191     {
192         NV_PRINTF(LEVEL_ERROR,
193                   "Input mask contains a GPU on which NVLink is disabled.\n");
194 
195         return NV_ERR_INVALID_ARGUMENT;
196     }
197 
198     if ((IS_RTLSIM(pGpu) && !pKernelNvlink0->bForceEnableCoreLibRtlsims) ||
199         (pKernelNvlink0->pNvlinkDev == NULL)                             ||
200         !pKernelNvlink0->bEnableAli                                      ||
201         (pKernelNvlink1->pNvlinkDev == NULL)                             ||
202         !pKernelNvlink1->bEnableAli)
203     {
204         NV_PRINTF(LEVEL_INFO,
205                 "Not in ALI, checking PostRxDetLinks not supported.\n");
206         return NV_ERR_NOT_SUPPORTED;
207     }
208 
209     //
210     // Initialize Mask of links that have made it past RxDet to 0 then
211     // request to get all links from the given GPU that have gotted past RxDet
212     //
213     pKernelNvlink0->postRxDetLinkMask = 0;
214     status = knvlinkUpdatePostRxDetectLinkMask(pGpu0, pKernelNvlink0);
215     if(status != NV_OK)
216     {
217         NV_PRINTF(LEVEL_ERROR,
218                   "Getting peer0's postRxDetLinkMask failed!\n");
219         return NV_ERR_INVALID_STATE;
220     }
221 
222     // Only query if we are not in loopback
223     if (pKernelNvlink0 != pKernelNvlink1)
224     {
225         pKernelNvlink1->postRxDetLinkMask = 0;
226         status = knvlinkUpdatePostRxDetectLinkMask(pGpu1, pKernelNvlink1);
227         if(status != NV_OK)
228         {
229             NV_PRINTF(LEVEL_ERROR,
230                       "Getting peer1's postRxDetLinkMask failed!\n");
231             return NV_ERR_INVALID_STATE;
232         }
233     }
234 
235     //
236     // If the current gpu has no actively training or trained link OR
237     // if the peer gpu has no actively training or trained links then
238     // return an error. If either side has 0 links passed RxDet then
239     // there is no chance that we will find links connecting the devices
240     // further into discovery.
241     //
242     if(pKernelNvlink0->postRxDetLinkMask == 0 ||
243        pKernelNvlink1->postRxDetLinkMask == 0)
244     {
245         NV_PRINTF(LEVEL_ERROR, "Got 0 post RxDet Links!");
246         return NV_ERR_NOT_READY;
247     }
248 
249 #endif
250 
251     return status;
252 }
253 
254 NV_STATUS
255 ioctrlFaultUpTmrHandler
256 (
257     OBJGPU *pGpu,
258     OBJTMR *pTmr,
259     TMR_EVENT *pEvent
260 )
261 {
262     //NvU32 linkId = *(NvU32*)pData;
263     NV_STATUS    status = NV_OK;
264     KernelNvlink *pKernelNvlink = GPU_GET_KERNEL_NVLINK(pGpu);
265     NV2080_CTRL_NVLINK_POST_FAULT_UP_PARAMS *nvlinkPostFaultUpParams
266                  = portMemAllocNonPaged(sizeof(NV2080_CTRL_NVLINK_POST_FAULT_UP_PARAMS));
267     PNVLINK_ID   pFaultLink;
268     pFaultLink = listHead(&pKernelNvlink->faultUpLinks);
269 
270     nvlinkPostFaultUpParams->linkId = pFaultLink->linkId;
271     status = knvlinkExecGspRmRpc(pGpu, pKernelNvlink,
272                         NV2080_CTRL_CMD_NVLINK_POST_FAULT_UP,
273                         (void *)nvlinkPostFaultUpParams,
274                         sizeof(NV2080_CTRL_NVLINK_POST_FAULT_UP_PARAMS));
275 
276     if (status != NV_OK)
277     {
278         NV_PRINTF(LEVEL_ERROR, "Failed to send Faultup RPC\n");
279     }
280 
281     listRemove(&pKernelNvlink->faultUpLinks, pFaultLink);
282     portMemFree(nvlinkPostFaultUpParams);
283 
284     return status;
285 }
286 
287 NV_STATUS
288 knvlinkHandleFaultUpInterrupt_GH100
289 (
290     OBJGPU       *pGpu,
291     KernelNvlink *pKernelNvlink,
292     NvU32        linkId
293 )
294 {
295     OBJTMR    *pTmr = GPU_GET_TIMER(pGpu);
296     PNVLINK_ID pFaultLink;
297     NV_STATUS status = NV_OK;
298 
299     pFaultLink = listAppendNew(&pKernelNvlink->faultUpLinks);
300     NV_ASSERT_OR_RETURN(pFaultLink != NULL, NV_ERR_GENERIC);
301     pFaultLink->linkId = linkId;
302 
303     status = tmrEventScheduleRel(pTmr, pKernelNvlink->nvlinkLinks[linkId].pTmrEvent, NVLINK_RETRAIN_TIME);
304     if (status != NV_OK)
305     {
306         NV_PRINTF(LEVEL_ERROR, "GPU (ID: %d) tmrEventScheduleRel failed for linkid %d\n",
307                   gpuGetInstance(pGpu), linkId);
308         return NV_ERR_GENERIC;
309     }
310 
311     return status;
312 }
313 
314 NV_STATUS
315 knvlinkLogAliDebugMessages_GH100
316 (
317     OBJGPU       *pGpu,
318     KernelNvlink *pKernelNvlink
319 )
320 {
321     NV2080_CTRL_NVLINK_GET_ERR_INFO_PARAMS *nvlinkErrInfoParams = portMemAllocNonPaged(sizeof(NV2080_CTRL_NVLINK_GET_ERR_INFO_PARAMS));
322     portMemSet(nvlinkErrInfoParams, 0, sizeof(NV2080_CTRL_NVLINK_GET_ERR_INFO_PARAMS));
323     nvlinkErrInfoParams->ErrInfoFlags |= NV2080_CTRL_NVLINK_ERR_INFO_FLAGS_ALI_STATUS;
324     NvU32         i;
325     // This is a Physical, Hopper specific HAL for debug purposes.
326     NV_STATUS status = knvlinkExecGspRmRpc(pGpu, pKernelNvlink,
327                         NV2080_CTRL_CMD_NVLINK_GET_ERR_INFO,
328                         (void *)nvlinkErrInfoParams,
329                         sizeof(NV2080_CTRL_NVLINK_GET_ERR_INFO_PARAMS));
330     if (status != NV_OK)
331     {
332         NV_PRINTF(LEVEL_ERROR, "Error getting debug info for link training!\n");
333         portMemFree(nvlinkErrInfoParams);
334         return status;
335     }
336 
337     FOR_EACH_INDEX_IN_MASK(32, i, pKernelNvlink->postRxDetLinkMask)
338     {
339         nvErrorLog_va((void *)pGpu, ALI_TRAINING_FAIL,
340                 "NVLink: Link training failed for link %u",
341                 "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)",
342                 i,
343                 nvlinkErrInfoParams->linkErrInfo[i].NVLIPTLnkCtrlLinkStateRequest,
344                 nvlinkErrInfoParams->linkErrInfo[i].NVLDLRxSlsmErrCntl,
345                 nvlinkErrInfoParams->linkErrInfo[i].NVLDLTopLinkState,
346                 nvlinkErrInfoParams->linkErrInfo[i].NVLDLTopIntr,
347                 nvlinkErrInfoParams->linkErrInfo[i].DLStatMN00,
348                 nvlinkErrInfoParams->linkErrInfo[i].DLStatUC01,
349                 nvlinkErrInfoParams->linkErrInfo[i].MinionNvlinkLinkIntr);
350 
351         if (pKernelNvlink->bLinkTrainingDebugSpew)
352             NV_PRINTF(LEVEL_ERROR,"ALI Error for GPU %d::linkId %d:"
353                     "\nNVLIPT:\n\tCTRL_LINK_STATE_REQUEST_STATUS = %X\n"
354                     "\nNVLDL :\n\tNV_NVLDL_RXSLSM_ERR_CNTL = %X\n"
355                     "\n\tNV_NVLDL_TOP_LINK_STATE = %X\n"
356                     "\n\tNV_NVLDL_TOP_INTR = %X\n"
357                     "\nMINION DLSTAT:\n\tDLSTAT MN00 = %X\n"
358                     "\n\tDLSTAT UC01 = %X\n"
359                     "\n\tNV_MINION_NVLINK_LINK_INTR = %X\n",
360                     pGpu->gpuInstance, i,
361                     nvlinkErrInfoParams->linkErrInfo[i].NVLIPTLnkCtrlLinkStateRequest,
362                     nvlinkErrInfoParams->linkErrInfo[i].NVLDLRxSlsmErrCntl,
363                     nvlinkErrInfoParams->linkErrInfo[i].NVLDLTopLinkState,
364                     nvlinkErrInfoParams->linkErrInfo[i].NVLDLTopIntr,
365                     nvlinkErrInfoParams->linkErrInfo[i].DLStatMN00,
366                     nvlinkErrInfoParams->linkErrInfo[i].DLStatUC01,
367                     nvlinkErrInfoParams->linkErrInfo[i].MinionNvlinkLinkIntr);
368     }
369     FOR_EACH_INDEX_IN_MASK_END;
370     portMemFree(nvlinkErrInfoParams);
371     return NV_OK;
372 }
373 
374 /*!
375  * @brief   Set unique fabric address for NVSwitch enabled systems.
376  *
377  * @param[in] pGpu           OBJGPU pointer
378  * @param[in] pKernelNvlink  KernelNvlink pointer
379  * @param[in] fabricBaseAddr Fabric Address to set
380  *
381  * @returns On success, sets unique fabric address and returns NV_OK.
382  *          On failure, returns NV_ERR_XXX.
383  */
384 NV_STATUS
385 knvlinkSetUniqueFabricBaseAddress_GH100
386 (
387     OBJGPU       *pGpu,
388     KernelNvlink *pKernelNvlink,
389     NvU64         fabricBaseAddr
390 )
391 {
392     NV_STATUS status = NV_OK;
393 
394     status = knvlinkValidateFabricBaseAddress_HAL(pGpu, pKernelNvlink,
395                                                   fabricBaseAddr);
396     if (status != NV_OK)
397     {
398         NV_PRINTF(LEVEL_ERROR, "Fabric addr validation failed for GPU %x\n",
399                   pGpu->gpuInstance);
400         return status;
401     }
402 
403     if (IsSLIEnabled(pGpu))
404     {
405         NV_PRINTF(LEVEL_ERROR,
406                   "Operation is unsupported on SLI enabled GPU %x\n",
407                   pGpu->gpuInstance);
408         return NV_ERR_NOT_SUPPORTED;
409     }
410 
411     if (pKernelNvlink->fabricBaseAddr == fabricBaseAddr)
412     {
413         NV_PRINTF(LEVEL_INFO,
414                   "The same fabric addr is being re-assigned to GPU %x\n",
415                   pGpu->gpuInstance);
416         return NV_OK;
417     }
418 
419     if (pKernelNvlink->fabricBaseAddr != NVLINK_INVALID_FABRIC_ADDR)
420     {
421         NV_PRINTF(LEVEL_ERROR, "Fabric addr is already assigned to GPU %x\n",
422                   pGpu->gpuInstance);
423         return NV_ERR_STATE_IN_USE;
424     }
425 
426     pKernelNvlink->fabricBaseAddr = fabricBaseAddr;
427 
428     NV_PRINTF(LEVEL_INFO, "Fabric base addr %llx is assigned to GPU %x\n",
429               pKernelNvlink->fabricBaseAddr, pGpu->gpuInstance);
430 
431     return NV_OK;
432 }
433 
434 /*!
435  * @brief  Check if floorsweeping is needed for this particular chip
436  *
437  * @param[in]  pGpu            OBJGPU pointer
438  * @param[in]  pKernelNvlink   KernelNvlink pointer
439  *
440  * @returns On success, sets unique fabric address and returns NV_OK.
441  *          On failure, returns NV_ERR_XXX.
442  */
443 NvBool
444 knvlinkIsFloorSweepingNeeded_GH100
445 (
446     OBJGPU       *pGpu,
447     KernelNvlink *pKernelNvlink,
448     NvU32         numActiveLinksPerIoctrl,
449     NvU32         numLinksPerIoctrl
450 )
451 {
452 
453     //
454     // Only floorsweep down the given GPU if the following conditions are met:
455     // 1. The number of active links allowed for the IOCTRL is less then the
456     //    total number of links for the IOCTRL. No reason to spend time in code
457     //    if the exectution of it will be a NOP
458     //
459     // 2. If the GPU has never been floorswept. An optimization to make sure RM
460     //    doesn't burn cycles repeatedly running running code that will be a NOP
461     //
462     // 3. (temporary) Run only on Silicon chips. Fmodel currently doesn't support
463     //    this feature
464     //
465 
466     if (numActiveLinksPerIoctrl < numLinksPerIoctrl &&
467         !pKernelNvlink->bFloorSwept                 &&
468         IS_SILICON(pGpu))
469     {
470         return NV_TRUE;
471     }
472 
473     return NV_FALSE;
474 }
475 
476 /*!
477  * @brief   Check if system has enough active NVLinks and
478  *          enough NVLink bridges
479  *
480  * @param[in] pGpu           OBJGPU pointer
481  * @param[in] pKernelNvlink  KernelNvlink pointer
482  *
483  */
484 void
485 knvlinkDirectConnectCheck_GH100
486 (
487     OBJGPU       *pGpu,
488     KernelNvlink *pKernelNvlink
489 )
490 {
491     NV2080_CTRL_NVLINK_DIRECT_CONNECT_CHECK_PARAMS params = {0};
492 
493     knvlinkExecGspRmRpc(pGpu, pKernelNvlink,
494                         NV2080_CTRL_CMD_NVLINK_DIRECT_CONNECT_CHECK,
495                         (void *)&params,
496                         sizeof(params));
497 }
498