xref: /open-nvidia-gpu/src/nvidia/src/kernel/gpu/uvm/uvm.c (revision eb5c7665)
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2012-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 "core/core.h"
25 #include "gpu/gpu.h"
26 #include "gpu/uvm/uvm.h"
27 #include "gpu/uvm/access_cntr_buffer.h"
28 #include "gpu/mem_sys/kern_mem_sys.h"
29 #include "nvRmReg.h"
30 #include "rmapi/control.h"
31 #include "rmapi/rmapi_utils.h"
32 #include "kernel/gpu/intr/engine_idx.h"
33 #include "kernel/gpu/mem_mgr/virt_mem_allocator_common.h"
34 
35 #include <ctrl/ctrl2080/ctrl2080internal.h>
36 
37 /**
38  * @brief Send the request to set up the buffer to physical RM.
39  */
40 static NV_STATUS
_uvmSetupAccessCntrBuffer(OBJGPU * pGpu,OBJUVM * pUvm,AccessCounterBuffer * pAccessCounterBuffer)41 _uvmSetupAccessCntrBuffer
42 (
43     OBJGPU              *pGpu,
44     OBJUVM              *pUvm,
45     AccessCounterBuffer *pAccessCounterBuffer
46 )
47 {
48     NvU32 bufferSize;
49     NvU32 numBufferPages;
50     NV2080_CTRL_INTERNAL_UVM_REGISTER_ACCESS_CNTR_BUFFER_PARAMS params = {0};
51     NV_STATUS status = NV_OK;
52 
53     // Buffer was not allocated, nothing to do
54     if (pAccessCounterBuffer->pUvmAccessCntrAllocMemDesc == NULL)
55     {
56         return NV_OK;
57     }
58 
59     bufferSize = pAccessCounterBuffer->pUvmAccessCntrAllocMemDesc->Size;
60     numBufferPages = NV_ROUNDUP(bufferSize, RM_PAGE_SIZE) / RM_PAGE_SIZE;
61 
62     if (numBufferPages > NV_ARRAY_ELEMENTS(params.bufferPteArray))
63     {
64         return NV_ERR_BUFFER_TOO_SMALL;
65     }
66 
67     memdescGetPhysAddrs(pAccessCounterBuffer->pUvmAccessCntrAllocMemDesc,
68                         AT_GPU, 0, RM_PAGE_SIZE,
69                         numBufferPages, params.bufferPteArray);
70 
71     params.bufferSize = bufferSize;
72     params.accessCounterIndex = pAccessCounterBuffer->accessCounterIndex;
73 
74     status = pUvm->pRmApi->Control(pUvm->pRmApi,
75                                    pUvm->hClient,
76                                    pUvm->hSubdevice,
77                                    NV2080_CTRL_CMD_INTERNAL_UVM_REGISTER_ACCESS_CNTR_BUFFER,
78                                    &params, sizeof(params));
79 
80     return status;
81 }
82 
83 /**
84  * @brief Send the request to unload access counter buffer to physical RM
85  *
86  * @param pGpu
87  * @param pUvm
88  */
89 static NV_STATUS
_uvmUnloadAccessCntrBuffer(OBJGPU * pGpu,OBJUVM * pUvm,AccessCounterBuffer * pAccessCounterBuffer)90 _uvmUnloadAccessCntrBuffer
91 (
92     OBJGPU              *pGpu,
93     OBJUVM              *pUvm,
94     AccessCounterBuffer *pAccessCounterBuffer
95 )
96 {
97     NV2080_CTRL_INTERNAL_UVM_UNREGISTER_ACCESS_CNTR_BUFFER_PARAMS params = {0};
98     NV_STATUS status;
99 
100     // Buffer was not allocated, nothing to do
101     if (pAccessCounterBuffer->pUvmAccessCntrAllocMemDesc == NULL)
102     {
103         return NV_OK;
104     }
105 
106     params.accessCounterIndex = pAccessCounterBuffer->accessCounterIndex;
107 
108     status = pUvm->pRmApi->Control(pUvm->pRmApi,
109                                    pUvm->hClient,
110                                    pUvm->hSubdevice,
111                                    NV2080_CTRL_CMD_INTERNAL_UVM_UNREGISTER_ACCESS_CNTR_BUFFER,
112                                    &params, sizeof(params));
113 
114     return status;
115 }
116 
117 /**
118  * @brief Initialize state for UVM
119  *
120  * @param pGpu
121  * @param pUvm
122  */
123 NV_STATUS
uvmStateInitUnlocked_IMPL(OBJGPU * pGpu,OBJUVM * pUvm)124 uvmStateInitUnlocked_IMPL(OBJGPU *pGpu, OBJUVM *pUvm)
125 {
126     NV_STATUS status = NV_OK;
127 
128     pUvm->pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL);
129     status = rmapiutilAllocClientAndDeviceHandles(pUvm->pRmApi, pGpu, &pUvm->hClient,
130                                                   NULL, &pUvm->hSubdevice);
131 
132     pUvm->accessCounterBufferCount = 1;
133 
134     pUvm->pAccessCounterBuffers = portMemAllocNonPaged(sizeof (*pUvm->pAccessCounterBuffers) * pUvm->accessCounterBufferCount);
135     NV_ASSERT_OR_RETURN(pUvm->pAccessCounterBuffers != NULL, NV_ERR_NO_MEMORY);
136     portMemSet(pUvm->pAccessCounterBuffers, 0, sizeof (*pUvm->pAccessCounterBuffers) * pUvm->accessCounterBufferCount);
137 
138     return status;
139 }
140 
141 /**
142  * @brief Destroy GPU gpu state for UVM
143  *
144  * @param pGpu
145  * @param pUvm
146  */
147 void
uvmStateDestroy_IMPL(OBJGPU * pGpu,OBJUVM * pUvm)148 uvmStateDestroy_IMPL(OBJGPU *pGpu, OBJUVM *pUvm)
149 {
150     portMemFree(pUvm->pAccessCounterBuffers);
151     rmapiutilFreeClientAndDeviceHandles(pUvm->pRmApi, &pUvm->hClient, NULL, &pUvm->hSubdevice);
152 }
153 
154 /**
155  * @brief Setup UVM access counters. Setup includes memory allocation, mapping
156  *        and writing mapped address to HW registers.
157  *
158  * @param pGpu
159  * @param pUvm
160  */
161 NV_STATUS
uvmInitializeAccessCntrBuffer_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,AccessCounterBuffer * pAccessCounterBuffer)162 uvmInitializeAccessCntrBuffer_IMPL
163 (
164     OBJGPU *pGpu,
165     OBJUVM *pUvm,
166     AccessCounterBuffer *pAccessCounterBuffer
167 )
168 {
169     NV_STATUS status;
170 
171     status = uvmInitAccessCntrBuffer_HAL(pGpu, pUvm, pAccessCounterBuffer);
172     if (status != NV_OK)
173     {
174         return status;
175     }
176 
177     status = _uvmSetupAccessCntrBuffer(pGpu, pUvm, pAccessCounterBuffer);
178     if (status != NV_OK)
179     {
180         (void) uvmDestroyAccessCntrBuffer_HAL(pGpu, pUvm, pAccessCounterBuffer);
181     }
182 
183     return status;
184 }
185 
186 /**
187  * @brief Destroy UVM access counters i.e. reciprocate above setup function.
188  *
189  * @param pGpu
190  * @param pUvm
191  */
192 NV_STATUS
uvmTerminateAccessCntrBuffer_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,AccessCounterBuffer * pAccessCounterBuffer)193 uvmTerminateAccessCntrBuffer_IMPL
194 (
195     OBJGPU              *pGpu,
196     OBJUVM              *pUvm,
197     AccessCounterBuffer *pAccessCounterBuffer
198 )
199 {
200     NV_STATUS status;
201 
202     status = _uvmUnloadAccessCntrBuffer(pGpu, pUvm, pAccessCounterBuffer);
203     if (status != NV_OK)
204     {
205         NV_PRINTF(LEVEL_ERROR,
206                   "Unloading UVM Access counters failed (status=0x%08x), proceeding...\n",
207                   status);
208     }
209 
210     status = uvmDestroyAccessCntrBuffer_HAL(pGpu, pUvm, pAccessCounterBuffer);
211     if (status != NV_OK)
212     {
213         NV_PRINTF(LEVEL_ERROR,
214                   "Freeing UVM Access counters failed (status=0x%08x), proceeding...\n",
215                   status);
216     }
217 
218     return NV_OK;
219 }
220 
221 /**
222  * @brief Re-create access counter memory descriptor and setup the buffer.
223  *
224  * @param pGpu
225  * @param pUvm
226  */
227 NV_STATUS
uvmAccessCntrBufferRegister_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,NvU32 accessCounterIndex,NvU32 bufferSize,NvU64 * pBufferPages)228 uvmAccessCntrBufferRegister_IMPL
229 (
230     OBJGPU *pGpu,
231     OBJUVM *pUvm,
232     NvU32   accessCounterIndex,
233     NvU32   bufferSize,
234     NvU64  *pBufferPages
235 )
236 {
237     NV_STATUS status;
238     MEMORY_DESCRIPTOR *pMemDesc;
239     NvU32 addrSpace = ADDR_SYSMEM;
240     NvU32 attr      = NV_MEMORY_CACHED;
241 
242     memdescOverrideInstLoc(DRF_VAL(_REG_STR_RM, _INST_LOC_4, _UVM_FAULT_BUFFER_REPLAYABLE, pGpu->instLocOverrides4),
243                            "UVM access counter", &addrSpace, &attr);
244 
245     status = memdescCreate(&pMemDesc, pGpu,
246                            bufferSize, 0, NV_FALSE,
247                            addrSpace, attr,
248                            (MEMDESC_FLAGS_GUEST_ALLOCATED |
249                             MEMDESC_FLAGS_EXT_PAGE_ARRAY_MEM |
250                             MEMDESC_FLAGS_LOST_ON_SUSPEND));
251     if (status != NV_OK)
252     {
253         return status;
254     }
255 
256     memdescFillPages(pMemDesc, 0, pBufferPages,
257                      NV_ROUNDUP(bufferSize, RM_PAGE_SIZE) / RM_PAGE_SIZE,
258                      RM_PAGE_SIZE);
259 
260     pUvm->pAccessCounterBuffers[accessCounterIndex].pUvmAccessCntrMemDesc = pMemDesc;
261 
262     status = uvmSetupAccessCntrBuffer_HAL(pGpu, pUvm, accessCounterIndex);
263     if (status != NV_OK)
264     {
265         NV_PRINTF(LEVEL_ERROR,
266                   "Setup of UVM Access counters failed (status=0x%08x)\n",
267                   status);
268 
269         memdescDestroy(pMemDesc);
270 
271         pUvm->pAccessCounterBuffers[accessCounterIndex].pUvmAccessCntrMemDesc = NULL;
272     }
273 
274     return status;
275 }
276 
277 /**
278  * @brief Disable access counter buffer and destroy the buffer
279  *        descriptor.
280  *
281  * @param pGpu
282  * @param pUvm
283  */
284 NV_STATUS
uvmAccessCntrBufferUnregister_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,NvU32 accessCounterIndex)285 uvmAccessCntrBufferUnregister_IMPL
286 (
287     OBJGPU *pGpu,
288     OBJUVM *pUvm,
289     NvU32   accessCounterIndex
290 )
291 {
292     NV_STATUS status;
293 
294     status = uvmUnloadAccessCntrBuffer_HAL(pGpu, pUvm, accessCounterIndex);
295     if (status != NV_OK)
296     {
297         NV_PRINTF(LEVEL_ERROR,
298                   "Unloading UVM Access counters failed (status=0x%08x), proceeding...\n",
299                   status);
300     }
301 
302     memdescDestroy(pUvm->pAccessCounterBuffers[accessCounterIndex].pUvmAccessCntrMemDesc);
303 
304     pUvm->pAccessCounterBuffers[accessCounterIndex].pUvmAccessCntrMemDesc = NULL;
305 
306     return NV_OK;
307 }
308 
309 /**
310  * @brief Provides an opportunity to register some IntrService during intrStateInit.
311  */
312 void
uvmRegisterIntrService_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,IntrServiceRecord pRecords[MC_ENGINE_IDX_MAX])313 uvmRegisterIntrService_IMPL
314 (
315     OBJGPU *pGpu,
316     OBJUVM *pUvm,
317     IntrServiceRecord pRecords[MC_ENGINE_IDX_MAX]
318 )
319 {
320     NvU32 engineIdx = MC_ENGINE_IDX_ACCESS_CNTR;
321     NV_ASSERT(pRecords[engineIdx].pInterruptService == NULL);
322     pRecords[engineIdx].pInterruptService = staticCast(pUvm, IntrService);
323 }
324 
325 /**
326  * @brief Service stall interrupts.
327  *
328  * @returns Zero, or any implementation-chosen nonzero value. If the same nonzero value is returned enough
329  *          times the interrupt is considered stuck.
330  */
331 NvU32
uvmServiceInterrupt_IMPL(OBJGPU * pGpu,OBJUVM * pUvm,IntrServiceServiceInterruptArguments * pParams)332 uvmServiceInterrupt_IMPL
333 (
334     OBJGPU *pGpu,
335     OBJUVM *pUvm,
336     IntrServiceServiceInterruptArguments *pParams
337 )
338 {
339     NV_ASSERT_OR_RETURN(pParams != NULL, 0);
340     NV_ASSERT_OR_RETURN(pParams->engineIdx == MC_ENGINE_IDX_ACCESS_CNTR, 0);
341 
342     NV_ASSERT_OK(uvmAccessCntrService_HAL(pGpu, pUvm));
343 
344     return 0;
345 }
346