1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2018 Red Hat Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17*4882a593Smuzhiyun * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*4882a593Smuzhiyun * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*4882a593Smuzhiyun * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*4882a593Smuzhiyun * OTHER DEALINGS IN THE SOFTWARE.
21*4882a593Smuzhiyun */
22*4882a593Smuzhiyun #include "nouveau_svm.h"
23*4882a593Smuzhiyun #include "nouveau_drv.h"
24*4882a593Smuzhiyun #include "nouveau_chan.h"
25*4882a593Smuzhiyun #include "nouveau_dmem.h"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #include <nvif/notify.h>
28*4882a593Smuzhiyun #include <nvif/object.h>
29*4882a593Smuzhiyun #include <nvif/vmm.h>
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #include <nvif/class.h>
32*4882a593Smuzhiyun #include <nvif/clb069.h>
33*4882a593Smuzhiyun #include <nvif/ifc00d.h>
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #include <linux/sched/mm.h>
36*4882a593Smuzhiyun #include <linux/sort.h>
37*4882a593Smuzhiyun #include <linux/hmm.h>
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct nouveau_svm {
40*4882a593Smuzhiyun struct nouveau_drm *drm;
41*4882a593Smuzhiyun struct mutex mutex;
42*4882a593Smuzhiyun struct list_head inst;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun struct nouveau_svm_fault_buffer {
45*4882a593Smuzhiyun int id;
46*4882a593Smuzhiyun struct nvif_object object;
47*4882a593Smuzhiyun u32 entries;
48*4882a593Smuzhiyun u32 getaddr;
49*4882a593Smuzhiyun u32 putaddr;
50*4882a593Smuzhiyun u32 get;
51*4882a593Smuzhiyun u32 put;
52*4882a593Smuzhiyun struct nvif_notify notify;
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct nouveau_svm_fault {
55*4882a593Smuzhiyun u64 inst;
56*4882a593Smuzhiyun u64 addr;
57*4882a593Smuzhiyun u64 time;
58*4882a593Smuzhiyun u32 engine;
59*4882a593Smuzhiyun u8 gpc;
60*4882a593Smuzhiyun u8 hub;
61*4882a593Smuzhiyun u8 access;
62*4882a593Smuzhiyun u8 client;
63*4882a593Smuzhiyun u8 fault;
64*4882a593Smuzhiyun struct nouveau_svmm *svmm;
65*4882a593Smuzhiyun } **fault;
66*4882a593Smuzhiyun int fault_nr;
67*4882a593Smuzhiyun } buffer[1];
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun #define SVM_DBG(s,f,a...) NV_DEBUG((s)->drm, "svm: "f"\n", ##a)
71*4882a593Smuzhiyun #define SVM_ERR(s,f,a...) NV_WARN((s)->drm, "svm: "f"\n", ##a)
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct nouveau_pfnmap_args {
74*4882a593Smuzhiyun struct nvif_ioctl_v0 i;
75*4882a593Smuzhiyun struct nvif_ioctl_mthd_v0 m;
76*4882a593Smuzhiyun struct nvif_vmm_pfnmap_v0 p;
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun struct nouveau_ivmm {
80*4882a593Smuzhiyun struct nouveau_svmm *svmm;
81*4882a593Smuzhiyun u64 inst;
82*4882a593Smuzhiyun struct list_head head;
83*4882a593Smuzhiyun };
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun static struct nouveau_ivmm *
nouveau_ivmm_find(struct nouveau_svm * svm,u64 inst)86*4882a593Smuzhiyun nouveau_ivmm_find(struct nouveau_svm *svm, u64 inst)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct nouveau_ivmm *ivmm;
89*4882a593Smuzhiyun list_for_each_entry(ivmm, &svm->inst, head) {
90*4882a593Smuzhiyun if (ivmm->inst == inst)
91*4882a593Smuzhiyun return ivmm;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun return NULL;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun #define SVMM_DBG(s,f,a...) \
97*4882a593Smuzhiyun NV_DEBUG((s)->vmm->cli->drm, "svm-%p: "f"\n", (s), ##a)
98*4882a593Smuzhiyun #define SVMM_ERR(s,f,a...) \
99*4882a593Smuzhiyun NV_WARN((s)->vmm->cli->drm, "svm-%p: "f"\n", (s), ##a)
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun int
nouveau_svmm_bind(struct drm_device * dev,void * data,struct drm_file * file_priv)102*4882a593Smuzhiyun nouveau_svmm_bind(struct drm_device *dev, void *data,
103*4882a593Smuzhiyun struct drm_file *file_priv)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct nouveau_cli *cli = nouveau_cli(file_priv);
106*4882a593Smuzhiyun struct drm_nouveau_svm_bind *args = data;
107*4882a593Smuzhiyun unsigned target, cmd, priority;
108*4882a593Smuzhiyun unsigned long addr, end;
109*4882a593Smuzhiyun struct mm_struct *mm;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun args->va_start &= PAGE_MASK;
112*4882a593Smuzhiyun args->va_end = ALIGN(args->va_end, PAGE_SIZE);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* Sanity check arguments */
115*4882a593Smuzhiyun if (args->reserved0 || args->reserved1)
116*4882a593Smuzhiyun return -EINVAL;
117*4882a593Smuzhiyun if (args->header & (~NOUVEAU_SVM_BIND_VALID_MASK))
118*4882a593Smuzhiyun return -EINVAL;
119*4882a593Smuzhiyun if (args->va_start >= args->va_end)
120*4882a593Smuzhiyun return -EINVAL;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT;
123*4882a593Smuzhiyun cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK;
124*4882a593Smuzhiyun switch (cmd) {
125*4882a593Smuzhiyun case NOUVEAU_SVM_BIND_COMMAND__MIGRATE:
126*4882a593Smuzhiyun break;
127*4882a593Smuzhiyun default:
128*4882a593Smuzhiyun return -EINVAL;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun priority = args->header >> NOUVEAU_SVM_BIND_PRIORITY_SHIFT;
132*4882a593Smuzhiyun priority &= NOUVEAU_SVM_BIND_PRIORITY_MASK;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* FIXME support CPU target ie all target value < GPU_VRAM */
135*4882a593Smuzhiyun target = args->header >> NOUVEAU_SVM_BIND_TARGET_SHIFT;
136*4882a593Smuzhiyun target &= NOUVEAU_SVM_BIND_TARGET_MASK;
137*4882a593Smuzhiyun switch (target) {
138*4882a593Smuzhiyun case NOUVEAU_SVM_BIND_TARGET__GPU_VRAM:
139*4882a593Smuzhiyun break;
140*4882a593Smuzhiyun default:
141*4882a593Smuzhiyun return -EINVAL;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /*
145*4882a593Smuzhiyun * FIXME: For now refuse non 0 stride, we need to change the migrate
146*4882a593Smuzhiyun * kernel function to handle stride to avoid to create a mess within
147*4882a593Smuzhiyun * each device driver.
148*4882a593Smuzhiyun */
149*4882a593Smuzhiyun if (args->stride)
150*4882a593Smuzhiyun return -EINVAL;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /*
153*4882a593Smuzhiyun * Ok we are ask to do something sane, for now we only support migrate
154*4882a593Smuzhiyun * commands but we will add things like memory policy (what to do on
155*4882a593Smuzhiyun * page fault) and maybe some other commands.
156*4882a593Smuzhiyun */
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun mm = get_task_mm(current);
159*4882a593Smuzhiyun if (!mm) {
160*4882a593Smuzhiyun return -EINVAL;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun mmap_read_lock(mm);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (!cli->svm.svmm) {
165*4882a593Smuzhiyun mmap_read_unlock(mm);
166*4882a593Smuzhiyun mmput(mm);
167*4882a593Smuzhiyun return -EINVAL;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun for (addr = args->va_start, end = args->va_end; addr < end;) {
171*4882a593Smuzhiyun struct vm_area_struct *vma;
172*4882a593Smuzhiyun unsigned long next;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun vma = find_vma_intersection(mm, addr, end);
175*4882a593Smuzhiyun if (!vma)
176*4882a593Smuzhiyun break;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun addr = max(addr, vma->vm_start);
179*4882a593Smuzhiyun next = min(vma->vm_end, end);
180*4882a593Smuzhiyun /* This is a best effort so we ignore errors */
181*4882a593Smuzhiyun nouveau_dmem_migrate_vma(cli->drm, cli->svm.svmm, vma, addr,
182*4882a593Smuzhiyun next);
183*4882a593Smuzhiyun addr = next;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /*
187*4882a593Smuzhiyun * FIXME Return the number of page we have migrated, again we need to
188*4882a593Smuzhiyun * update the migrate API to return that information so that we can
189*4882a593Smuzhiyun * report it to user space.
190*4882a593Smuzhiyun */
191*4882a593Smuzhiyun args->result = 0;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun mmap_read_unlock(mm);
194*4882a593Smuzhiyun mmput(mm);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun return 0;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun /* Unlink channel instance from SVMM. */
200*4882a593Smuzhiyun void
nouveau_svmm_part(struct nouveau_svmm * svmm,u64 inst)201*4882a593Smuzhiyun nouveau_svmm_part(struct nouveau_svmm *svmm, u64 inst)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct nouveau_ivmm *ivmm;
204*4882a593Smuzhiyun if (svmm) {
205*4882a593Smuzhiyun mutex_lock(&svmm->vmm->cli->drm->svm->mutex);
206*4882a593Smuzhiyun ivmm = nouveau_ivmm_find(svmm->vmm->cli->drm->svm, inst);
207*4882a593Smuzhiyun if (ivmm) {
208*4882a593Smuzhiyun list_del(&ivmm->head);
209*4882a593Smuzhiyun kfree(ivmm);
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun mutex_unlock(&svmm->vmm->cli->drm->svm->mutex);
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun /* Link channel instance to SVMM. */
216*4882a593Smuzhiyun int
nouveau_svmm_join(struct nouveau_svmm * svmm,u64 inst)217*4882a593Smuzhiyun nouveau_svmm_join(struct nouveau_svmm *svmm, u64 inst)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun struct nouveau_ivmm *ivmm;
220*4882a593Smuzhiyun if (svmm) {
221*4882a593Smuzhiyun if (!(ivmm = kmalloc(sizeof(*ivmm), GFP_KERNEL)))
222*4882a593Smuzhiyun return -ENOMEM;
223*4882a593Smuzhiyun ivmm->svmm = svmm;
224*4882a593Smuzhiyun ivmm->inst = inst;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun mutex_lock(&svmm->vmm->cli->drm->svm->mutex);
227*4882a593Smuzhiyun list_add(&ivmm->head, &svmm->vmm->cli->drm->svm->inst);
228*4882a593Smuzhiyun mutex_unlock(&svmm->vmm->cli->drm->svm->mutex);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun return 0;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun /* Invalidate SVMM address-range on GPU. */
234*4882a593Smuzhiyun void
nouveau_svmm_invalidate(struct nouveau_svmm * svmm,u64 start,u64 limit)235*4882a593Smuzhiyun nouveau_svmm_invalidate(struct nouveau_svmm *svmm, u64 start, u64 limit)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun if (limit > start) {
238*4882a593Smuzhiyun bool super = svmm->vmm->vmm.object.client->super;
239*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = true;
240*4882a593Smuzhiyun nvif_object_mthd(&svmm->vmm->vmm.object, NVIF_VMM_V0_PFNCLR,
241*4882a593Smuzhiyun &(struct nvif_vmm_pfnclr_v0) {
242*4882a593Smuzhiyun .addr = start,
243*4882a593Smuzhiyun .size = limit - start,
244*4882a593Smuzhiyun }, sizeof(struct nvif_vmm_pfnclr_v0));
245*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = super;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun static int
nouveau_svmm_invalidate_range_start(struct mmu_notifier * mn,const struct mmu_notifier_range * update)250*4882a593Smuzhiyun nouveau_svmm_invalidate_range_start(struct mmu_notifier *mn,
251*4882a593Smuzhiyun const struct mmu_notifier_range *update)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun struct nouveau_svmm *svmm =
254*4882a593Smuzhiyun container_of(mn, struct nouveau_svmm, notifier);
255*4882a593Smuzhiyun unsigned long start = update->start;
256*4882a593Smuzhiyun unsigned long limit = update->end;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun if (!mmu_notifier_range_blockable(update))
259*4882a593Smuzhiyun return -EAGAIN;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun SVMM_DBG(svmm, "invalidate %016lx-%016lx", start, limit);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun mutex_lock(&svmm->mutex);
264*4882a593Smuzhiyun if (unlikely(!svmm->vmm))
265*4882a593Smuzhiyun goto out;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun /*
268*4882a593Smuzhiyun * Ignore invalidation callbacks for device private pages since
269*4882a593Smuzhiyun * the invalidation is handled as part of the migration process.
270*4882a593Smuzhiyun */
271*4882a593Smuzhiyun if (update->event == MMU_NOTIFY_MIGRATE &&
272*4882a593Smuzhiyun update->migrate_pgmap_owner == svmm->vmm->cli->drm->dev)
273*4882a593Smuzhiyun goto out;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun if (limit > svmm->unmanaged.start && start < svmm->unmanaged.limit) {
276*4882a593Smuzhiyun if (start < svmm->unmanaged.start) {
277*4882a593Smuzhiyun nouveau_svmm_invalidate(svmm, start,
278*4882a593Smuzhiyun svmm->unmanaged.limit);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun start = svmm->unmanaged.limit;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun nouveau_svmm_invalidate(svmm, start, limit);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun out:
286*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
287*4882a593Smuzhiyun return 0;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
nouveau_svmm_free_notifier(struct mmu_notifier * mn)290*4882a593Smuzhiyun static void nouveau_svmm_free_notifier(struct mmu_notifier *mn)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun kfree(container_of(mn, struct nouveau_svmm, notifier));
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun static const struct mmu_notifier_ops nouveau_mn_ops = {
296*4882a593Smuzhiyun .invalidate_range_start = nouveau_svmm_invalidate_range_start,
297*4882a593Smuzhiyun .free_notifier = nouveau_svmm_free_notifier,
298*4882a593Smuzhiyun };
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun void
nouveau_svmm_fini(struct nouveau_svmm ** psvmm)301*4882a593Smuzhiyun nouveau_svmm_fini(struct nouveau_svmm **psvmm)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun struct nouveau_svmm *svmm = *psvmm;
304*4882a593Smuzhiyun if (svmm) {
305*4882a593Smuzhiyun mutex_lock(&svmm->mutex);
306*4882a593Smuzhiyun svmm->vmm = NULL;
307*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
308*4882a593Smuzhiyun mmu_notifier_put(&svmm->notifier);
309*4882a593Smuzhiyun *psvmm = NULL;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun int
nouveau_svmm_init(struct drm_device * dev,void * data,struct drm_file * file_priv)314*4882a593Smuzhiyun nouveau_svmm_init(struct drm_device *dev, void *data,
315*4882a593Smuzhiyun struct drm_file *file_priv)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun struct nouveau_cli *cli = nouveau_cli(file_priv);
318*4882a593Smuzhiyun struct nouveau_svmm *svmm;
319*4882a593Smuzhiyun struct drm_nouveau_svm_init *args = data;
320*4882a593Smuzhiyun int ret;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun /* We need to fail if svm is disabled */
323*4882a593Smuzhiyun if (!cli->drm->svm)
324*4882a593Smuzhiyun return -ENOSYS;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun /* Allocate tracking for SVM-enabled VMM. */
327*4882a593Smuzhiyun if (!(svmm = kzalloc(sizeof(*svmm), GFP_KERNEL)))
328*4882a593Smuzhiyun return -ENOMEM;
329*4882a593Smuzhiyun svmm->vmm = &cli->svm;
330*4882a593Smuzhiyun svmm->unmanaged.start = args->unmanaged_addr;
331*4882a593Smuzhiyun svmm->unmanaged.limit = args->unmanaged_addr + args->unmanaged_size;
332*4882a593Smuzhiyun mutex_init(&svmm->mutex);
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun /* Check that SVM isn't already enabled for the client. */
335*4882a593Smuzhiyun mutex_lock(&cli->mutex);
336*4882a593Smuzhiyun if (cli->svm.cli) {
337*4882a593Smuzhiyun ret = -EBUSY;
338*4882a593Smuzhiyun goto out_free;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /* Allocate a new GPU VMM that can support SVM (managed by the
342*4882a593Smuzhiyun * client, with replayable faults enabled).
343*4882a593Smuzhiyun *
344*4882a593Smuzhiyun * All future channel/memory allocations will make use of this
345*4882a593Smuzhiyun * VMM instead of the standard one.
346*4882a593Smuzhiyun */
347*4882a593Smuzhiyun ret = nvif_vmm_ctor(&cli->mmu, "svmVmm",
348*4882a593Smuzhiyun cli->vmm.vmm.object.oclass, true,
349*4882a593Smuzhiyun args->unmanaged_addr, args->unmanaged_size,
350*4882a593Smuzhiyun &(struct gp100_vmm_v0) {
351*4882a593Smuzhiyun .fault_replay = true,
352*4882a593Smuzhiyun }, sizeof(struct gp100_vmm_v0), &cli->svm.vmm);
353*4882a593Smuzhiyun if (ret)
354*4882a593Smuzhiyun goto out_free;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun mmap_write_lock(current->mm);
357*4882a593Smuzhiyun svmm->notifier.ops = &nouveau_mn_ops;
358*4882a593Smuzhiyun ret = __mmu_notifier_register(&svmm->notifier, current->mm);
359*4882a593Smuzhiyun if (ret)
360*4882a593Smuzhiyun goto out_mm_unlock;
361*4882a593Smuzhiyun /* Note, ownership of svmm transfers to mmu_notifier */
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun cli->svm.svmm = svmm;
364*4882a593Smuzhiyun cli->svm.cli = cli;
365*4882a593Smuzhiyun mmap_write_unlock(current->mm);
366*4882a593Smuzhiyun mutex_unlock(&cli->mutex);
367*4882a593Smuzhiyun return 0;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun out_mm_unlock:
370*4882a593Smuzhiyun mmap_write_unlock(current->mm);
371*4882a593Smuzhiyun out_free:
372*4882a593Smuzhiyun mutex_unlock(&cli->mutex);
373*4882a593Smuzhiyun kfree(svmm);
374*4882a593Smuzhiyun return ret;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun /* Issue fault replay for GPU to retry accesses that faulted previously. */
378*4882a593Smuzhiyun static void
nouveau_svm_fault_replay(struct nouveau_svm * svm)379*4882a593Smuzhiyun nouveau_svm_fault_replay(struct nouveau_svm *svm)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun SVM_DBG(svm, "replay");
382*4882a593Smuzhiyun WARN_ON(nvif_object_mthd(&svm->drm->client.vmm.vmm.object,
383*4882a593Smuzhiyun GP100_VMM_VN_FAULT_REPLAY,
384*4882a593Smuzhiyun &(struct gp100_vmm_fault_replay_vn) {},
385*4882a593Smuzhiyun sizeof(struct gp100_vmm_fault_replay_vn)));
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun /* Cancel a replayable fault that could not be handled.
389*4882a593Smuzhiyun *
390*4882a593Smuzhiyun * Cancelling the fault will trigger recovery to reset the engine
391*4882a593Smuzhiyun * and kill the offending channel (ie. GPU SIGSEGV).
392*4882a593Smuzhiyun */
393*4882a593Smuzhiyun static void
nouveau_svm_fault_cancel(struct nouveau_svm * svm,u64 inst,u8 hub,u8 gpc,u8 client)394*4882a593Smuzhiyun nouveau_svm_fault_cancel(struct nouveau_svm *svm,
395*4882a593Smuzhiyun u64 inst, u8 hub, u8 gpc, u8 client)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun SVM_DBG(svm, "cancel %016llx %d %02x %02x", inst, hub, gpc, client);
398*4882a593Smuzhiyun WARN_ON(nvif_object_mthd(&svm->drm->client.vmm.vmm.object,
399*4882a593Smuzhiyun GP100_VMM_VN_FAULT_CANCEL,
400*4882a593Smuzhiyun &(struct gp100_vmm_fault_cancel_v0) {
401*4882a593Smuzhiyun .hub = hub,
402*4882a593Smuzhiyun .gpc = gpc,
403*4882a593Smuzhiyun .client = client,
404*4882a593Smuzhiyun .inst = inst,
405*4882a593Smuzhiyun }, sizeof(struct gp100_vmm_fault_cancel_v0)));
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun static void
nouveau_svm_fault_cancel_fault(struct nouveau_svm * svm,struct nouveau_svm_fault * fault)409*4882a593Smuzhiyun nouveau_svm_fault_cancel_fault(struct nouveau_svm *svm,
410*4882a593Smuzhiyun struct nouveau_svm_fault *fault)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun nouveau_svm_fault_cancel(svm, fault->inst,
413*4882a593Smuzhiyun fault->hub,
414*4882a593Smuzhiyun fault->gpc,
415*4882a593Smuzhiyun fault->client);
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun static int
nouveau_svm_fault_cmp(const void * a,const void * b)419*4882a593Smuzhiyun nouveau_svm_fault_cmp(const void *a, const void *b)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun const struct nouveau_svm_fault *fa = *(struct nouveau_svm_fault **)a;
422*4882a593Smuzhiyun const struct nouveau_svm_fault *fb = *(struct nouveau_svm_fault **)b;
423*4882a593Smuzhiyun int ret;
424*4882a593Smuzhiyun if ((ret = (s64)fa->inst - fb->inst))
425*4882a593Smuzhiyun return ret;
426*4882a593Smuzhiyun if ((ret = (s64)fa->addr - fb->addr))
427*4882a593Smuzhiyun return ret;
428*4882a593Smuzhiyun /*XXX: atomic? */
429*4882a593Smuzhiyun return (fa->access == 0 || fa->access == 3) -
430*4882a593Smuzhiyun (fb->access == 0 || fb->access == 3);
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun static void
nouveau_svm_fault_cache(struct nouveau_svm * svm,struct nouveau_svm_fault_buffer * buffer,u32 offset)434*4882a593Smuzhiyun nouveau_svm_fault_cache(struct nouveau_svm *svm,
435*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer, u32 offset)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun struct nvif_object *memory = &buffer->object;
438*4882a593Smuzhiyun const u32 instlo = nvif_rd32(memory, offset + 0x00);
439*4882a593Smuzhiyun const u32 insthi = nvif_rd32(memory, offset + 0x04);
440*4882a593Smuzhiyun const u32 addrlo = nvif_rd32(memory, offset + 0x08);
441*4882a593Smuzhiyun const u32 addrhi = nvif_rd32(memory, offset + 0x0c);
442*4882a593Smuzhiyun const u32 timelo = nvif_rd32(memory, offset + 0x10);
443*4882a593Smuzhiyun const u32 timehi = nvif_rd32(memory, offset + 0x14);
444*4882a593Smuzhiyun const u32 engine = nvif_rd32(memory, offset + 0x18);
445*4882a593Smuzhiyun const u32 info = nvif_rd32(memory, offset + 0x1c);
446*4882a593Smuzhiyun const u64 inst = (u64)insthi << 32 | instlo;
447*4882a593Smuzhiyun const u8 gpc = (info & 0x1f000000) >> 24;
448*4882a593Smuzhiyun const u8 hub = (info & 0x00100000) >> 20;
449*4882a593Smuzhiyun const u8 client = (info & 0x00007f00) >> 8;
450*4882a593Smuzhiyun struct nouveau_svm_fault *fault;
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun //XXX: i think we're supposed to spin waiting */
453*4882a593Smuzhiyun if (WARN_ON(!(info & 0x80000000)))
454*4882a593Smuzhiyun return;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun nvif_mask(memory, offset + 0x1c, 0x80000000, 0x00000000);
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun if (!buffer->fault[buffer->fault_nr]) {
459*4882a593Smuzhiyun fault = kmalloc(sizeof(*fault), GFP_KERNEL);
460*4882a593Smuzhiyun if (WARN_ON(!fault)) {
461*4882a593Smuzhiyun nouveau_svm_fault_cancel(svm, inst, hub, gpc, client);
462*4882a593Smuzhiyun return;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun buffer->fault[buffer->fault_nr] = fault;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun fault = buffer->fault[buffer->fault_nr++];
468*4882a593Smuzhiyun fault->inst = inst;
469*4882a593Smuzhiyun fault->addr = (u64)addrhi << 32 | addrlo;
470*4882a593Smuzhiyun fault->time = (u64)timehi << 32 | timelo;
471*4882a593Smuzhiyun fault->engine = engine;
472*4882a593Smuzhiyun fault->gpc = gpc;
473*4882a593Smuzhiyun fault->hub = hub;
474*4882a593Smuzhiyun fault->access = (info & 0x000f0000) >> 16;
475*4882a593Smuzhiyun fault->client = client;
476*4882a593Smuzhiyun fault->fault = (info & 0x0000001f);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun SVM_DBG(svm, "fault %016llx %016llx %02x",
479*4882a593Smuzhiyun fault->inst, fault->addr, fault->access);
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun struct svm_notifier {
483*4882a593Smuzhiyun struct mmu_interval_notifier notifier;
484*4882a593Smuzhiyun struct nouveau_svmm *svmm;
485*4882a593Smuzhiyun };
486*4882a593Smuzhiyun
nouveau_svm_range_invalidate(struct mmu_interval_notifier * mni,const struct mmu_notifier_range * range,unsigned long cur_seq)487*4882a593Smuzhiyun static bool nouveau_svm_range_invalidate(struct mmu_interval_notifier *mni,
488*4882a593Smuzhiyun const struct mmu_notifier_range *range,
489*4882a593Smuzhiyun unsigned long cur_seq)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun struct svm_notifier *sn =
492*4882a593Smuzhiyun container_of(mni, struct svm_notifier, notifier);
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun /*
495*4882a593Smuzhiyun * serializes the update to mni->invalidate_seq done by caller and
496*4882a593Smuzhiyun * prevents invalidation of the PTE from progressing while HW is being
497*4882a593Smuzhiyun * programmed. This is very hacky and only works because the normal
498*4882a593Smuzhiyun * notifier that does invalidation is always called after the range
499*4882a593Smuzhiyun * notifier.
500*4882a593Smuzhiyun */
501*4882a593Smuzhiyun if (mmu_notifier_range_blockable(range))
502*4882a593Smuzhiyun mutex_lock(&sn->svmm->mutex);
503*4882a593Smuzhiyun else if (!mutex_trylock(&sn->svmm->mutex))
504*4882a593Smuzhiyun return false;
505*4882a593Smuzhiyun mmu_interval_set_seq(mni, cur_seq);
506*4882a593Smuzhiyun mutex_unlock(&sn->svmm->mutex);
507*4882a593Smuzhiyun return true;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun static const struct mmu_interval_notifier_ops nouveau_svm_mni_ops = {
511*4882a593Smuzhiyun .invalidate = nouveau_svm_range_invalidate,
512*4882a593Smuzhiyun };
513*4882a593Smuzhiyun
nouveau_hmm_convert_pfn(struct nouveau_drm * drm,struct hmm_range * range,struct nouveau_pfnmap_args * args)514*4882a593Smuzhiyun static void nouveau_hmm_convert_pfn(struct nouveau_drm *drm,
515*4882a593Smuzhiyun struct hmm_range *range,
516*4882a593Smuzhiyun struct nouveau_pfnmap_args *args)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun struct page *page;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun /*
521*4882a593Smuzhiyun * The address prepared here is passed through nvif_object_ioctl()
522*4882a593Smuzhiyun * to an eventual DMA map in something like gp100_vmm_pgt_pfn()
523*4882a593Smuzhiyun *
524*4882a593Smuzhiyun * This is all just encoding the internal hmm representation into a
525*4882a593Smuzhiyun * different nouveau internal representation.
526*4882a593Smuzhiyun */
527*4882a593Smuzhiyun if (!(range->hmm_pfns[0] & HMM_PFN_VALID)) {
528*4882a593Smuzhiyun args->p.phys[0] = 0;
529*4882a593Smuzhiyun return;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun page = hmm_pfn_to_page(range->hmm_pfns[0]);
533*4882a593Smuzhiyun /*
534*4882a593Smuzhiyun * Only map compound pages to the GPU if the CPU is also mapping the
535*4882a593Smuzhiyun * page as a compound page. Otherwise, the PTE protections might not be
536*4882a593Smuzhiyun * consistent (e.g., CPU only maps part of a compound page).
537*4882a593Smuzhiyun * Note that the underlying page might still be larger than the
538*4882a593Smuzhiyun * CPU mapping (e.g., a PUD sized compound page partially mapped with
539*4882a593Smuzhiyun * a PMD sized page table entry).
540*4882a593Smuzhiyun */
541*4882a593Smuzhiyun if (hmm_pfn_to_map_order(range->hmm_pfns[0])) {
542*4882a593Smuzhiyun unsigned long addr = args->p.addr;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun args->p.page = hmm_pfn_to_map_order(range->hmm_pfns[0]) +
545*4882a593Smuzhiyun PAGE_SHIFT;
546*4882a593Smuzhiyun args->p.size = 1UL << args->p.page;
547*4882a593Smuzhiyun args->p.addr &= ~(args->p.size - 1);
548*4882a593Smuzhiyun page -= (addr - args->p.addr) >> PAGE_SHIFT;
549*4882a593Smuzhiyun }
550*4882a593Smuzhiyun if (is_device_private_page(page))
551*4882a593Smuzhiyun args->p.phys[0] = nouveau_dmem_page_addr(page) |
552*4882a593Smuzhiyun NVIF_VMM_PFNMAP_V0_V |
553*4882a593Smuzhiyun NVIF_VMM_PFNMAP_V0_VRAM;
554*4882a593Smuzhiyun else
555*4882a593Smuzhiyun args->p.phys[0] = page_to_phys(page) |
556*4882a593Smuzhiyun NVIF_VMM_PFNMAP_V0_V |
557*4882a593Smuzhiyun NVIF_VMM_PFNMAP_V0_HOST;
558*4882a593Smuzhiyun if (range->hmm_pfns[0] & HMM_PFN_WRITE)
559*4882a593Smuzhiyun args->p.phys[0] |= NVIF_VMM_PFNMAP_V0_W;
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
nouveau_range_fault(struct nouveau_svmm * svmm,struct nouveau_drm * drm,struct nouveau_pfnmap_args * args,u32 size,unsigned long hmm_flags,struct svm_notifier * notifier)562*4882a593Smuzhiyun static int nouveau_range_fault(struct nouveau_svmm *svmm,
563*4882a593Smuzhiyun struct nouveau_drm *drm,
564*4882a593Smuzhiyun struct nouveau_pfnmap_args *args, u32 size,
565*4882a593Smuzhiyun unsigned long hmm_flags,
566*4882a593Smuzhiyun struct svm_notifier *notifier)
567*4882a593Smuzhiyun {
568*4882a593Smuzhiyun unsigned long timeout =
569*4882a593Smuzhiyun jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
570*4882a593Smuzhiyun /* Have HMM fault pages within the fault window to the GPU. */
571*4882a593Smuzhiyun unsigned long hmm_pfns[1];
572*4882a593Smuzhiyun struct hmm_range range = {
573*4882a593Smuzhiyun .notifier = ¬ifier->notifier,
574*4882a593Smuzhiyun .start = notifier->notifier.interval_tree.start,
575*4882a593Smuzhiyun .end = notifier->notifier.interval_tree.last + 1,
576*4882a593Smuzhiyun .default_flags = hmm_flags,
577*4882a593Smuzhiyun .hmm_pfns = hmm_pfns,
578*4882a593Smuzhiyun .dev_private_owner = drm->dev,
579*4882a593Smuzhiyun };
580*4882a593Smuzhiyun struct mm_struct *mm = notifier->notifier.mm;
581*4882a593Smuzhiyun int ret;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun while (true) {
584*4882a593Smuzhiyun if (time_after(jiffies, timeout))
585*4882a593Smuzhiyun return -EBUSY;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun range.notifier_seq = mmu_interval_read_begin(range.notifier);
588*4882a593Smuzhiyun mmap_read_lock(mm);
589*4882a593Smuzhiyun ret = hmm_range_fault(&range);
590*4882a593Smuzhiyun mmap_read_unlock(mm);
591*4882a593Smuzhiyun if (ret) {
592*4882a593Smuzhiyun if (ret == -EBUSY)
593*4882a593Smuzhiyun continue;
594*4882a593Smuzhiyun return ret;
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun mutex_lock(&svmm->mutex);
598*4882a593Smuzhiyun if (mmu_interval_read_retry(range.notifier,
599*4882a593Smuzhiyun range.notifier_seq)) {
600*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
601*4882a593Smuzhiyun continue;
602*4882a593Smuzhiyun }
603*4882a593Smuzhiyun break;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun nouveau_hmm_convert_pfn(drm, &range, args);
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = true;
609*4882a593Smuzhiyun ret = nvif_object_ioctl(&svmm->vmm->vmm.object, args, size, NULL);
610*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = false;
611*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun return ret;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun static int
nouveau_svm_fault(struct nvif_notify * notify)617*4882a593Smuzhiyun nouveau_svm_fault(struct nvif_notify *notify)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer =
620*4882a593Smuzhiyun container_of(notify, typeof(*buffer), notify);
621*4882a593Smuzhiyun struct nouveau_svm *svm =
622*4882a593Smuzhiyun container_of(buffer, typeof(*svm), buffer[buffer->id]);
623*4882a593Smuzhiyun struct nvif_object *device = &svm->drm->client.device.object;
624*4882a593Smuzhiyun struct nouveau_svmm *svmm;
625*4882a593Smuzhiyun struct {
626*4882a593Smuzhiyun struct nouveau_pfnmap_args i;
627*4882a593Smuzhiyun u64 phys[1];
628*4882a593Smuzhiyun } args;
629*4882a593Smuzhiyun unsigned long hmm_flags;
630*4882a593Smuzhiyun u64 inst, start, limit;
631*4882a593Smuzhiyun int fi, fn;
632*4882a593Smuzhiyun int replay = 0, ret;
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun /* Parse available fault buffer entries into a cache, and update
635*4882a593Smuzhiyun * the GET pointer so HW can reuse the entries.
636*4882a593Smuzhiyun */
637*4882a593Smuzhiyun SVM_DBG(svm, "fault handler");
638*4882a593Smuzhiyun if (buffer->get == buffer->put) {
639*4882a593Smuzhiyun buffer->put = nvif_rd32(device, buffer->putaddr);
640*4882a593Smuzhiyun buffer->get = nvif_rd32(device, buffer->getaddr);
641*4882a593Smuzhiyun if (buffer->get == buffer->put)
642*4882a593Smuzhiyun return NVIF_NOTIFY_KEEP;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun buffer->fault_nr = 0;
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun SVM_DBG(svm, "get %08x put %08x", buffer->get, buffer->put);
647*4882a593Smuzhiyun while (buffer->get != buffer->put) {
648*4882a593Smuzhiyun nouveau_svm_fault_cache(svm, buffer, buffer->get * 0x20);
649*4882a593Smuzhiyun if (++buffer->get == buffer->entries)
650*4882a593Smuzhiyun buffer->get = 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun nvif_wr32(device, buffer->getaddr, buffer->get);
653*4882a593Smuzhiyun SVM_DBG(svm, "%d fault(s) pending", buffer->fault_nr);
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun /* Sort parsed faults by instance pointer to prevent unnecessary
656*4882a593Smuzhiyun * instance to SVMM translations, followed by address and access
657*4882a593Smuzhiyun * type to reduce the amount of work when handling the faults.
658*4882a593Smuzhiyun */
659*4882a593Smuzhiyun sort(buffer->fault, buffer->fault_nr, sizeof(*buffer->fault),
660*4882a593Smuzhiyun nouveau_svm_fault_cmp, NULL);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun /* Lookup SVMM structure for each unique instance pointer. */
663*4882a593Smuzhiyun mutex_lock(&svm->mutex);
664*4882a593Smuzhiyun for (fi = 0, svmm = NULL; fi < buffer->fault_nr; fi++) {
665*4882a593Smuzhiyun if (!svmm || buffer->fault[fi]->inst != inst) {
666*4882a593Smuzhiyun struct nouveau_ivmm *ivmm =
667*4882a593Smuzhiyun nouveau_ivmm_find(svm, buffer->fault[fi]->inst);
668*4882a593Smuzhiyun svmm = ivmm ? ivmm->svmm : NULL;
669*4882a593Smuzhiyun inst = buffer->fault[fi]->inst;
670*4882a593Smuzhiyun SVM_DBG(svm, "inst %016llx -> svm-%p", inst, svmm);
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun buffer->fault[fi]->svmm = svmm;
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun mutex_unlock(&svm->mutex);
675*4882a593Smuzhiyun
676*4882a593Smuzhiyun /* Process list of faults. */
677*4882a593Smuzhiyun args.i.i.version = 0;
678*4882a593Smuzhiyun args.i.i.type = NVIF_IOCTL_V0_MTHD;
679*4882a593Smuzhiyun args.i.m.version = 0;
680*4882a593Smuzhiyun args.i.m.method = NVIF_VMM_V0_PFNMAP;
681*4882a593Smuzhiyun args.i.p.version = 0;
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun for (fi = 0; fn = fi + 1, fi < buffer->fault_nr; fi = fn) {
684*4882a593Smuzhiyun struct svm_notifier notifier;
685*4882a593Smuzhiyun struct mm_struct *mm;
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun /* Cancel any faults from non-SVM channels. */
688*4882a593Smuzhiyun if (!(svmm = buffer->fault[fi]->svmm)) {
689*4882a593Smuzhiyun nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
690*4882a593Smuzhiyun continue;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun SVMM_DBG(svmm, "addr %016llx", buffer->fault[fi]->addr);
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun /* We try and group handling of faults within a small
695*4882a593Smuzhiyun * window into a single update.
696*4882a593Smuzhiyun */
697*4882a593Smuzhiyun start = buffer->fault[fi]->addr;
698*4882a593Smuzhiyun limit = start + PAGE_SIZE;
699*4882a593Smuzhiyun if (start < svmm->unmanaged.limit)
700*4882a593Smuzhiyun limit = min_t(u64, limit, svmm->unmanaged.start);
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun /*
703*4882a593Smuzhiyun * Prepare the GPU-side update of all pages within the
704*4882a593Smuzhiyun * fault window, determining required pages and access
705*4882a593Smuzhiyun * permissions based on pending faults.
706*4882a593Smuzhiyun */
707*4882a593Smuzhiyun args.i.p.addr = start;
708*4882a593Smuzhiyun args.i.p.page = PAGE_SHIFT;
709*4882a593Smuzhiyun args.i.p.size = PAGE_SIZE;
710*4882a593Smuzhiyun /*
711*4882a593Smuzhiyun * Determine required permissions based on GPU fault
712*4882a593Smuzhiyun * access flags.
713*4882a593Smuzhiyun * XXX: atomic?
714*4882a593Smuzhiyun */
715*4882a593Smuzhiyun switch (buffer->fault[fi]->access) {
716*4882a593Smuzhiyun case 0: /* READ. */
717*4882a593Smuzhiyun hmm_flags = HMM_PFN_REQ_FAULT;
718*4882a593Smuzhiyun break;
719*4882a593Smuzhiyun case 3: /* PREFETCH. */
720*4882a593Smuzhiyun hmm_flags = 0;
721*4882a593Smuzhiyun break;
722*4882a593Smuzhiyun default:
723*4882a593Smuzhiyun hmm_flags = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE;
724*4882a593Smuzhiyun break;
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun mm = svmm->notifier.mm;
728*4882a593Smuzhiyun if (!mmget_not_zero(mm)) {
729*4882a593Smuzhiyun nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]);
730*4882a593Smuzhiyun continue;
731*4882a593Smuzhiyun }
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun notifier.svmm = svmm;
734*4882a593Smuzhiyun ret = mmu_interval_notifier_insert(¬ifier.notifier, mm,
735*4882a593Smuzhiyun args.i.p.addr, args.i.p.size,
736*4882a593Smuzhiyun &nouveau_svm_mni_ops);
737*4882a593Smuzhiyun if (!ret) {
738*4882a593Smuzhiyun ret = nouveau_range_fault(svmm, svm->drm, &args.i,
739*4882a593Smuzhiyun sizeof(args), hmm_flags, ¬ifier);
740*4882a593Smuzhiyun mmu_interval_notifier_remove(¬ifier.notifier);
741*4882a593Smuzhiyun }
742*4882a593Smuzhiyun mmput(mm);
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun limit = args.i.p.addr + args.i.p.size;
745*4882a593Smuzhiyun for (fn = fi; ++fn < buffer->fault_nr; ) {
746*4882a593Smuzhiyun /* It's okay to skip over duplicate addresses from the
747*4882a593Smuzhiyun * same SVMM as faults are ordered by access type such
748*4882a593Smuzhiyun * that only the first one needs to be handled.
749*4882a593Smuzhiyun *
750*4882a593Smuzhiyun * ie. WRITE faults appear first, thus any handling of
751*4882a593Smuzhiyun * pending READ faults will already be satisfied.
752*4882a593Smuzhiyun * But if a large page is mapped, make sure subsequent
753*4882a593Smuzhiyun * fault addresses have sufficient access permission.
754*4882a593Smuzhiyun */
755*4882a593Smuzhiyun if (buffer->fault[fn]->svmm != svmm ||
756*4882a593Smuzhiyun buffer->fault[fn]->addr >= limit ||
757*4882a593Smuzhiyun (buffer->fault[fi]->access == 0 /* READ. */ &&
758*4882a593Smuzhiyun !(args.phys[0] & NVIF_VMM_PFNMAP_V0_V)) ||
759*4882a593Smuzhiyun (buffer->fault[fi]->access != 0 /* READ. */ &&
760*4882a593Smuzhiyun buffer->fault[fi]->access != 3 /* PREFETCH. */ &&
761*4882a593Smuzhiyun !(args.phys[0] & NVIF_VMM_PFNMAP_V0_W)))
762*4882a593Smuzhiyun break;
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun /* If handling failed completely, cancel all faults. */
766*4882a593Smuzhiyun if (ret) {
767*4882a593Smuzhiyun while (fi < fn) {
768*4882a593Smuzhiyun struct nouveau_svm_fault *fault =
769*4882a593Smuzhiyun buffer->fault[fi++];
770*4882a593Smuzhiyun
771*4882a593Smuzhiyun nouveau_svm_fault_cancel_fault(svm, fault);
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun } else
774*4882a593Smuzhiyun replay++;
775*4882a593Smuzhiyun }
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun /* Issue fault replay to the GPU. */
778*4882a593Smuzhiyun if (replay)
779*4882a593Smuzhiyun nouveau_svm_fault_replay(svm);
780*4882a593Smuzhiyun return NVIF_NOTIFY_KEEP;
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun static struct nouveau_pfnmap_args *
nouveau_pfns_to_args(void * pfns)784*4882a593Smuzhiyun nouveau_pfns_to_args(void *pfns)
785*4882a593Smuzhiyun {
786*4882a593Smuzhiyun return container_of(pfns, struct nouveau_pfnmap_args, p.phys);
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun u64 *
nouveau_pfns_alloc(unsigned long npages)790*4882a593Smuzhiyun nouveau_pfns_alloc(unsigned long npages)
791*4882a593Smuzhiyun {
792*4882a593Smuzhiyun struct nouveau_pfnmap_args *args;
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun args = kzalloc(struct_size(args, p.phys, npages), GFP_KERNEL);
795*4882a593Smuzhiyun if (!args)
796*4882a593Smuzhiyun return NULL;
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun args->i.type = NVIF_IOCTL_V0_MTHD;
799*4882a593Smuzhiyun args->m.method = NVIF_VMM_V0_PFNMAP;
800*4882a593Smuzhiyun args->p.page = PAGE_SHIFT;
801*4882a593Smuzhiyun
802*4882a593Smuzhiyun return args->p.phys;
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun void
nouveau_pfns_free(u64 * pfns)806*4882a593Smuzhiyun nouveau_pfns_free(u64 *pfns)
807*4882a593Smuzhiyun {
808*4882a593Smuzhiyun struct nouveau_pfnmap_args *args = nouveau_pfns_to_args(pfns);
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun kfree(args);
811*4882a593Smuzhiyun }
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun void
nouveau_pfns_map(struct nouveau_svmm * svmm,struct mm_struct * mm,unsigned long addr,u64 * pfns,unsigned long npages)814*4882a593Smuzhiyun nouveau_pfns_map(struct nouveau_svmm *svmm, struct mm_struct *mm,
815*4882a593Smuzhiyun unsigned long addr, u64 *pfns, unsigned long npages)
816*4882a593Smuzhiyun {
817*4882a593Smuzhiyun struct nouveau_pfnmap_args *args = nouveau_pfns_to_args(pfns);
818*4882a593Smuzhiyun int ret;
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun args->p.addr = addr;
821*4882a593Smuzhiyun args->p.size = npages << PAGE_SHIFT;
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun mutex_lock(&svmm->mutex);
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = true;
826*4882a593Smuzhiyun ret = nvif_object_ioctl(&svmm->vmm->vmm.object, args, sizeof(*args) +
827*4882a593Smuzhiyun npages * sizeof(args->p.phys[0]), NULL);
828*4882a593Smuzhiyun svmm->vmm->vmm.object.client->super = false;
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun static void
nouveau_svm_fault_buffer_fini(struct nouveau_svm * svm,int id)834*4882a593Smuzhiyun nouveau_svm_fault_buffer_fini(struct nouveau_svm *svm, int id)
835*4882a593Smuzhiyun {
836*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
837*4882a593Smuzhiyun nvif_notify_put(&buffer->notify);
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun static int
nouveau_svm_fault_buffer_init(struct nouveau_svm * svm,int id)841*4882a593Smuzhiyun nouveau_svm_fault_buffer_init(struct nouveau_svm *svm, int id)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
844*4882a593Smuzhiyun struct nvif_object *device = &svm->drm->client.device.object;
845*4882a593Smuzhiyun buffer->get = nvif_rd32(device, buffer->getaddr);
846*4882a593Smuzhiyun buffer->put = nvif_rd32(device, buffer->putaddr);
847*4882a593Smuzhiyun SVM_DBG(svm, "get %08x put %08x (init)", buffer->get, buffer->put);
848*4882a593Smuzhiyun return nvif_notify_get(&buffer->notify);
849*4882a593Smuzhiyun }
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun static void
nouveau_svm_fault_buffer_dtor(struct nouveau_svm * svm,int id)852*4882a593Smuzhiyun nouveau_svm_fault_buffer_dtor(struct nouveau_svm *svm, int id)
853*4882a593Smuzhiyun {
854*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
855*4882a593Smuzhiyun int i;
856*4882a593Smuzhiyun
857*4882a593Smuzhiyun if (buffer->fault) {
858*4882a593Smuzhiyun for (i = 0; buffer->fault[i] && i < buffer->entries; i++)
859*4882a593Smuzhiyun kfree(buffer->fault[i]);
860*4882a593Smuzhiyun kvfree(buffer->fault);
861*4882a593Smuzhiyun }
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun nouveau_svm_fault_buffer_fini(svm, id);
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun nvif_notify_dtor(&buffer->notify);
866*4882a593Smuzhiyun nvif_object_dtor(&buffer->object);
867*4882a593Smuzhiyun }
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun static int
nouveau_svm_fault_buffer_ctor(struct nouveau_svm * svm,s32 oclass,int id)870*4882a593Smuzhiyun nouveau_svm_fault_buffer_ctor(struct nouveau_svm *svm, s32 oclass, int id)
871*4882a593Smuzhiyun {
872*4882a593Smuzhiyun struct nouveau_svm_fault_buffer *buffer = &svm->buffer[id];
873*4882a593Smuzhiyun struct nouveau_drm *drm = svm->drm;
874*4882a593Smuzhiyun struct nvif_object *device = &drm->client.device.object;
875*4882a593Smuzhiyun struct nvif_clb069_v0 args = {};
876*4882a593Smuzhiyun int ret;
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun buffer->id = id;
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun ret = nvif_object_ctor(device, "svmFaultBuffer", 0, oclass, &args,
881*4882a593Smuzhiyun sizeof(args), &buffer->object);
882*4882a593Smuzhiyun if (ret < 0) {
883*4882a593Smuzhiyun SVM_ERR(svm, "Fault buffer allocation failed: %d", ret);
884*4882a593Smuzhiyun return ret;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun
887*4882a593Smuzhiyun nvif_object_map(&buffer->object, NULL, 0);
888*4882a593Smuzhiyun buffer->entries = args.entries;
889*4882a593Smuzhiyun buffer->getaddr = args.get;
890*4882a593Smuzhiyun buffer->putaddr = args.put;
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun ret = nvif_notify_ctor(&buffer->object, "svmFault", nouveau_svm_fault,
893*4882a593Smuzhiyun true, NVB069_V0_NTFY_FAULT, NULL, 0, 0,
894*4882a593Smuzhiyun &buffer->notify);
895*4882a593Smuzhiyun if (ret)
896*4882a593Smuzhiyun return ret;
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun buffer->fault = kvzalloc(sizeof(*buffer->fault) * buffer->entries, GFP_KERNEL);
899*4882a593Smuzhiyun if (!buffer->fault)
900*4882a593Smuzhiyun return -ENOMEM;
901*4882a593Smuzhiyun
902*4882a593Smuzhiyun return nouveau_svm_fault_buffer_init(svm, id);
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun void
nouveau_svm_resume(struct nouveau_drm * drm)906*4882a593Smuzhiyun nouveau_svm_resume(struct nouveau_drm *drm)
907*4882a593Smuzhiyun {
908*4882a593Smuzhiyun struct nouveau_svm *svm = drm->svm;
909*4882a593Smuzhiyun if (svm)
910*4882a593Smuzhiyun nouveau_svm_fault_buffer_init(svm, 0);
911*4882a593Smuzhiyun }
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun void
nouveau_svm_suspend(struct nouveau_drm * drm)914*4882a593Smuzhiyun nouveau_svm_suspend(struct nouveau_drm *drm)
915*4882a593Smuzhiyun {
916*4882a593Smuzhiyun struct nouveau_svm *svm = drm->svm;
917*4882a593Smuzhiyun if (svm)
918*4882a593Smuzhiyun nouveau_svm_fault_buffer_fini(svm, 0);
919*4882a593Smuzhiyun }
920*4882a593Smuzhiyun
921*4882a593Smuzhiyun void
nouveau_svm_fini(struct nouveau_drm * drm)922*4882a593Smuzhiyun nouveau_svm_fini(struct nouveau_drm *drm)
923*4882a593Smuzhiyun {
924*4882a593Smuzhiyun struct nouveau_svm *svm = drm->svm;
925*4882a593Smuzhiyun if (svm) {
926*4882a593Smuzhiyun nouveau_svm_fault_buffer_dtor(svm, 0);
927*4882a593Smuzhiyun kfree(drm->svm);
928*4882a593Smuzhiyun drm->svm = NULL;
929*4882a593Smuzhiyun }
930*4882a593Smuzhiyun }
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun void
nouveau_svm_init(struct nouveau_drm * drm)933*4882a593Smuzhiyun nouveau_svm_init(struct nouveau_drm *drm)
934*4882a593Smuzhiyun {
935*4882a593Smuzhiyun static const struct nvif_mclass buffers[] = {
936*4882a593Smuzhiyun { VOLTA_FAULT_BUFFER_A, 0 },
937*4882a593Smuzhiyun { MAXWELL_FAULT_BUFFER_A, 0 },
938*4882a593Smuzhiyun {}
939*4882a593Smuzhiyun };
940*4882a593Smuzhiyun struct nouveau_svm *svm;
941*4882a593Smuzhiyun int ret;
942*4882a593Smuzhiyun
943*4882a593Smuzhiyun /* Disable on Volta and newer until channel recovery is fixed,
944*4882a593Smuzhiyun * otherwise clients will have a trivial way to trash the GPU
945*4882a593Smuzhiyun * for everyone.
946*4882a593Smuzhiyun */
947*4882a593Smuzhiyun if (drm->client.device.info.family > NV_DEVICE_INFO_V0_PASCAL)
948*4882a593Smuzhiyun return;
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun if (!(drm->svm = svm = kzalloc(sizeof(*drm->svm), GFP_KERNEL)))
951*4882a593Smuzhiyun return;
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun drm->svm->drm = drm;
954*4882a593Smuzhiyun mutex_init(&drm->svm->mutex);
955*4882a593Smuzhiyun INIT_LIST_HEAD(&drm->svm->inst);
956*4882a593Smuzhiyun
957*4882a593Smuzhiyun ret = nvif_mclass(&drm->client.device.object, buffers);
958*4882a593Smuzhiyun if (ret < 0) {
959*4882a593Smuzhiyun SVM_DBG(svm, "No supported fault buffer class");
960*4882a593Smuzhiyun nouveau_svm_fini(drm);
961*4882a593Smuzhiyun return;
962*4882a593Smuzhiyun }
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun ret = nouveau_svm_fault_buffer_ctor(svm, buffers[ret].oclass, 0);
965*4882a593Smuzhiyun if (ret) {
966*4882a593Smuzhiyun nouveau_svm_fini(drm);
967*4882a593Smuzhiyun return;
968*4882a593Smuzhiyun }
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun SVM_DBG(svm, "Initialised");
971*4882a593Smuzhiyun }
972