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 ¶ms, 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 ¶ms, 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