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_dmem.h"
23*4882a593Smuzhiyun #include "nouveau_drv.h"
24*4882a593Smuzhiyun #include "nouveau_chan.h"
25*4882a593Smuzhiyun #include "nouveau_dma.h"
26*4882a593Smuzhiyun #include "nouveau_mem.h"
27*4882a593Smuzhiyun #include "nouveau_bo.h"
28*4882a593Smuzhiyun #include "nouveau_svm.h"
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <nvif/class.h>
31*4882a593Smuzhiyun #include <nvif/object.h>
32*4882a593Smuzhiyun #include <nvif/push906f.h>
33*4882a593Smuzhiyun #include <nvif/if000c.h>
34*4882a593Smuzhiyun #include <nvif/if500b.h>
35*4882a593Smuzhiyun #include <nvif/if900b.h>
36*4882a593Smuzhiyun #include <nvif/if000c.h>
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #include <nvhw/class/cla0b5.h>
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #include <linux/sched/mm.h>
41*4882a593Smuzhiyun #include <linux/hmm.h>
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /*
44*4882a593Smuzhiyun * FIXME: this is ugly right now we are using TTM to allocate vram and we pin
45*4882a593Smuzhiyun * it in vram while in use. We likely want to overhaul memory management for
46*4882a593Smuzhiyun * nouveau to be more page like (not necessarily with system page size but a
47*4882a593Smuzhiyun * bigger page size) at lowest level and have some shim layer on top that would
48*4882a593Smuzhiyun * provide the same functionality as TTM.
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun #define DMEM_CHUNK_SIZE (2UL << 20)
51*4882a593Smuzhiyun #define DMEM_CHUNK_NPAGES (DMEM_CHUNK_SIZE >> PAGE_SHIFT)
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun enum nouveau_aper {
54*4882a593Smuzhiyun NOUVEAU_APER_VIRT,
55*4882a593Smuzhiyun NOUVEAU_APER_VRAM,
56*4882a593Smuzhiyun NOUVEAU_APER_HOST,
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun typedef int (*nouveau_migrate_copy_t)(struct nouveau_drm *drm, u64 npages,
60*4882a593Smuzhiyun enum nouveau_aper, u64 dst_addr,
61*4882a593Smuzhiyun enum nouveau_aper, u64 src_addr);
62*4882a593Smuzhiyun typedef int (*nouveau_clear_page_t)(struct nouveau_drm *drm, u32 length,
63*4882a593Smuzhiyun enum nouveau_aper, u64 dst_addr);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun struct nouveau_dmem_chunk {
66*4882a593Smuzhiyun struct list_head list;
67*4882a593Smuzhiyun struct nouveau_bo *bo;
68*4882a593Smuzhiyun struct nouveau_drm *drm;
69*4882a593Smuzhiyun unsigned long callocated;
70*4882a593Smuzhiyun struct dev_pagemap pagemap;
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct nouveau_dmem_migrate {
74*4882a593Smuzhiyun nouveau_migrate_copy_t copy_func;
75*4882a593Smuzhiyun nouveau_clear_page_t clear_func;
76*4882a593Smuzhiyun struct nouveau_channel *chan;
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun struct nouveau_dmem {
80*4882a593Smuzhiyun struct nouveau_drm *drm;
81*4882a593Smuzhiyun struct nouveau_dmem_migrate migrate;
82*4882a593Smuzhiyun struct list_head chunks;
83*4882a593Smuzhiyun struct mutex mutex;
84*4882a593Smuzhiyun struct page *free_pages;
85*4882a593Smuzhiyun spinlock_t lock;
86*4882a593Smuzhiyun };
87*4882a593Smuzhiyun
nouveau_page_to_chunk(struct page * page)88*4882a593Smuzhiyun static struct nouveau_dmem_chunk *nouveau_page_to_chunk(struct page *page)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun return container_of(page->pgmap, struct nouveau_dmem_chunk, pagemap);
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
page_to_drm(struct page * page)93*4882a593Smuzhiyun static struct nouveau_drm *page_to_drm(struct page *page)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk = nouveau_page_to_chunk(page);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun return chunk->drm;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
nouveau_dmem_page_addr(struct page * page)100*4882a593Smuzhiyun unsigned long nouveau_dmem_page_addr(struct page *page)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk = nouveau_page_to_chunk(page);
103*4882a593Smuzhiyun unsigned long off = (page_to_pfn(page) << PAGE_SHIFT) -
104*4882a593Smuzhiyun chunk->pagemap.range.start;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun return chunk->bo->offset + off;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
nouveau_dmem_page_free(struct page * page)109*4882a593Smuzhiyun static void nouveau_dmem_page_free(struct page *page)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk = nouveau_page_to_chunk(page);
112*4882a593Smuzhiyun struct nouveau_dmem *dmem = chunk->drm->dmem;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun spin_lock(&dmem->lock);
115*4882a593Smuzhiyun page->zone_device_data = dmem->free_pages;
116*4882a593Smuzhiyun dmem->free_pages = page;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun WARN_ON(!chunk->callocated);
119*4882a593Smuzhiyun chunk->callocated--;
120*4882a593Smuzhiyun /*
121*4882a593Smuzhiyun * FIXME when chunk->callocated reach 0 we should add the chunk to
122*4882a593Smuzhiyun * a reclaim list so that it can be freed in case of memory pressure.
123*4882a593Smuzhiyun */
124*4882a593Smuzhiyun spin_unlock(&dmem->lock);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
nouveau_dmem_fence_done(struct nouveau_fence ** fence)127*4882a593Smuzhiyun static void nouveau_dmem_fence_done(struct nouveau_fence **fence)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun if (fence) {
130*4882a593Smuzhiyun nouveau_fence_wait(*fence, true, false);
131*4882a593Smuzhiyun nouveau_fence_unref(fence);
132*4882a593Smuzhiyun } else {
133*4882a593Smuzhiyun /*
134*4882a593Smuzhiyun * FIXME wait for channel to be IDLE before calling finalizing
135*4882a593Smuzhiyun * the hmem object.
136*4882a593Smuzhiyun */
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
nouveau_dmem_fault_copy_one(struct nouveau_drm * drm,struct vm_fault * vmf,struct migrate_vma * args,dma_addr_t * dma_addr)140*4882a593Smuzhiyun static vm_fault_t nouveau_dmem_fault_copy_one(struct nouveau_drm *drm,
141*4882a593Smuzhiyun struct vm_fault *vmf, struct migrate_vma *args,
142*4882a593Smuzhiyun dma_addr_t *dma_addr)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct device *dev = drm->dev->dev;
145*4882a593Smuzhiyun struct page *dpage, *spage;
146*4882a593Smuzhiyun struct nouveau_svmm *svmm;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun spage = migrate_pfn_to_page(args->src[0]);
149*4882a593Smuzhiyun if (!spage || !(args->src[0] & MIGRATE_PFN_MIGRATE))
150*4882a593Smuzhiyun return 0;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun dpage = alloc_page_vma(GFP_HIGHUSER, vmf->vma, vmf->address);
153*4882a593Smuzhiyun if (!dpage)
154*4882a593Smuzhiyun return VM_FAULT_SIGBUS;
155*4882a593Smuzhiyun lock_page(dpage);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun *dma_addr = dma_map_page(dev, dpage, 0, PAGE_SIZE, DMA_BIDIRECTIONAL);
158*4882a593Smuzhiyun if (dma_mapping_error(dev, *dma_addr))
159*4882a593Smuzhiyun goto error_free_page;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun svmm = spage->zone_device_data;
162*4882a593Smuzhiyun mutex_lock(&svmm->mutex);
163*4882a593Smuzhiyun nouveau_svmm_invalidate(svmm, args->start, args->end);
164*4882a593Smuzhiyun if (drm->dmem->migrate.copy_func(drm, 1, NOUVEAU_APER_HOST, *dma_addr,
165*4882a593Smuzhiyun NOUVEAU_APER_VRAM, nouveau_dmem_page_addr(spage)))
166*4882a593Smuzhiyun goto error_dma_unmap;
167*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun args->dst[0] = migrate_pfn(page_to_pfn(dpage)) | MIGRATE_PFN_LOCKED;
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun error_dma_unmap:
173*4882a593Smuzhiyun mutex_unlock(&svmm->mutex);
174*4882a593Smuzhiyun dma_unmap_page(dev, *dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
175*4882a593Smuzhiyun error_free_page:
176*4882a593Smuzhiyun __free_page(dpage);
177*4882a593Smuzhiyun return VM_FAULT_SIGBUS;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
nouveau_dmem_migrate_to_ram(struct vm_fault * vmf)180*4882a593Smuzhiyun static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun struct nouveau_drm *drm = page_to_drm(vmf->page);
183*4882a593Smuzhiyun struct nouveau_dmem *dmem = drm->dmem;
184*4882a593Smuzhiyun struct nouveau_fence *fence;
185*4882a593Smuzhiyun unsigned long src = 0, dst = 0;
186*4882a593Smuzhiyun dma_addr_t dma_addr = 0;
187*4882a593Smuzhiyun vm_fault_t ret;
188*4882a593Smuzhiyun struct migrate_vma args = {
189*4882a593Smuzhiyun .vma = vmf->vma,
190*4882a593Smuzhiyun .start = vmf->address,
191*4882a593Smuzhiyun .end = vmf->address + PAGE_SIZE,
192*4882a593Smuzhiyun .src = &src,
193*4882a593Smuzhiyun .dst = &dst,
194*4882a593Smuzhiyun .pgmap_owner = drm->dev,
195*4882a593Smuzhiyun .flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE,
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /*
199*4882a593Smuzhiyun * FIXME what we really want is to find some heuristic to migrate more
200*4882a593Smuzhiyun * than just one page on CPU fault. When such fault happens it is very
201*4882a593Smuzhiyun * likely that more surrounding page will CPU fault too.
202*4882a593Smuzhiyun */
203*4882a593Smuzhiyun if (migrate_vma_setup(&args) < 0)
204*4882a593Smuzhiyun return VM_FAULT_SIGBUS;
205*4882a593Smuzhiyun if (!args.cpages)
206*4882a593Smuzhiyun return 0;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun ret = nouveau_dmem_fault_copy_one(drm, vmf, &args, &dma_addr);
209*4882a593Smuzhiyun if (ret || dst == 0)
210*4882a593Smuzhiyun goto done;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun nouveau_fence_new(dmem->migrate.chan, false, &fence);
213*4882a593Smuzhiyun migrate_vma_pages(&args);
214*4882a593Smuzhiyun nouveau_dmem_fence_done(&fence);
215*4882a593Smuzhiyun dma_unmap_page(drm->dev->dev, dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
216*4882a593Smuzhiyun done:
217*4882a593Smuzhiyun migrate_vma_finalize(&args);
218*4882a593Smuzhiyun return ret;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = {
222*4882a593Smuzhiyun .page_free = nouveau_dmem_page_free,
223*4882a593Smuzhiyun .migrate_to_ram = nouveau_dmem_migrate_to_ram,
224*4882a593Smuzhiyun };
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun static int
nouveau_dmem_chunk_alloc(struct nouveau_drm * drm,struct page ** ppage)227*4882a593Smuzhiyun nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk;
230*4882a593Smuzhiyun struct resource *res;
231*4882a593Smuzhiyun struct page *page;
232*4882a593Smuzhiyun void *ptr;
233*4882a593Smuzhiyun unsigned long i, pfn_first;
234*4882a593Smuzhiyun int ret;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun chunk = kzalloc(sizeof(*chunk), GFP_KERNEL);
237*4882a593Smuzhiyun if (chunk == NULL) {
238*4882a593Smuzhiyun ret = -ENOMEM;
239*4882a593Smuzhiyun goto out;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun /* Allocate unused physical address space for device private pages. */
243*4882a593Smuzhiyun res = request_free_mem_region(&iomem_resource, DMEM_CHUNK_SIZE,
244*4882a593Smuzhiyun "nouveau_dmem");
245*4882a593Smuzhiyun if (IS_ERR(res)) {
246*4882a593Smuzhiyun ret = PTR_ERR(res);
247*4882a593Smuzhiyun goto out_free;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun chunk->drm = drm;
251*4882a593Smuzhiyun chunk->pagemap.type = MEMORY_DEVICE_PRIVATE;
252*4882a593Smuzhiyun chunk->pagemap.range.start = res->start;
253*4882a593Smuzhiyun chunk->pagemap.range.end = res->end;
254*4882a593Smuzhiyun chunk->pagemap.nr_range = 1;
255*4882a593Smuzhiyun chunk->pagemap.ops = &nouveau_dmem_pagemap_ops;
256*4882a593Smuzhiyun chunk->pagemap.owner = drm->dev;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun ret = nouveau_bo_new(&drm->client, DMEM_CHUNK_SIZE, 0,
259*4882a593Smuzhiyun NOUVEAU_GEM_DOMAIN_VRAM, 0, 0, NULL, NULL,
260*4882a593Smuzhiyun &chunk->bo);
261*4882a593Smuzhiyun if (ret)
262*4882a593Smuzhiyun goto out_release;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun ret = nouveau_bo_pin(chunk->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
265*4882a593Smuzhiyun if (ret)
266*4882a593Smuzhiyun goto out_bo_free;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun ptr = memremap_pages(&chunk->pagemap, numa_node_id());
269*4882a593Smuzhiyun if (IS_ERR(ptr)) {
270*4882a593Smuzhiyun ret = PTR_ERR(ptr);
271*4882a593Smuzhiyun goto out_bo_unpin;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun mutex_lock(&drm->dmem->mutex);
275*4882a593Smuzhiyun list_add(&chunk->list, &drm->dmem->chunks);
276*4882a593Smuzhiyun mutex_unlock(&drm->dmem->mutex);
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun pfn_first = chunk->pagemap.range.start >> PAGE_SHIFT;
279*4882a593Smuzhiyun page = pfn_to_page(pfn_first);
280*4882a593Smuzhiyun spin_lock(&drm->dmem->lock);
281*4882a593Smuzhiyun for (i = 0; i < DMEM_CHUNK_NPAGES - 1; ++i, ++page) {
282*4882a593Smuzhiyun page->zone_device_data = drm->dmem->free_pages;
283*4882a593Smuzhiyun drm->dmem->free_pages = page;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun *ppage = page;
286*4882a593Smuzhiyun chunk->callocated++;
287*4882a593Smuzhiyun spin_unlock(&drm->dmem->lock);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun NV_INFO(drm, "DMEM: registered %ldMB of device memory\n",
290*4882a593Smuzhiyun DMEM_CHUNK_SIZE >> 20);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun return 0;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun out_bo_unpin:
295*4882a593Smuzhiyun nouveau_bo_unpin(chunk->bo);
296*4882a593Smuzhiyun out_bo_free:
297*4882a593Smuzhiyun nouveau_bo_ref(NULL, &chunk->bo);
298*4882a593Smuzhiyun out_release:
299*4882a593Smuzhiyun release_mem_region(chunk->pagemap.range.start, range_len(&chunk->pagemap.range));
300*4882a593Smuzhiyun out_free:
301*4882a593Smuzhiyun kfree(chunk);
302*4882a593Smuzhiyun out:
303*4882a593Smuzhiyun return ret;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static struct page *
nouveau_dmem_page_alloc_locked(struct nouveau_drm * drm)307*4882a593Smuzhiyun nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk;
310*4882a593Smuzhiyun struct page *page = NULL;
311*4882a593Smuzhiyun int ret;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun spin_lock(&drm->dmem->lock);
314*4882a593Smuzhiyun if (drm->dmem->free_pages) {
315*4882a593Smuzhiyun page = drm->dmem->free_pages;
316*4882a593Smuzhiyun drm->dmem->free_pages = page->zone_device_data;
317*4882a593Smuzhiyun chunk = nouveau_page_to_chunk(page);
318*4882a593Smuzhiyun chunk->callocated++;
319*4882a593Smuzhiyun spin_unlock(&drm->dmem->lock);
320*4882a593Smuzhiyun } else {
321*4882a593Smuzhiyun spin_unlock(&drm->dmem->lock);
322*4882a593Smuzhiyun ret = nouveau_dmem_chunk_alloc(drm, &page);
323*4882a593Smuzhiyun if (ret)
324*4882a593Smuzhiyun return NULL;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun get_page(page);
328*4882a593Smuzhiyun lock_page(page);
329*4882a593Smuzhiyun return page;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun static void
nouveau_dmem_page_free_locked(struct nouveau_drm * drm,struct page * page)333*4882a593Smuzhiyun nouveau_dmem_page_free_locked(struct nouveau_drm *drm, struct page *page)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun unlock_page(page);
336*4882a593Smuzhiyun put_page(page);
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun void
nouveau_dmem_resume(struct nouveau_drm * drm)340*4882a593Smuzhiyun nouveau_dmem_resume(struct nouveau_drm *drm)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk;
343*4882a593Smuzhiyun int ret;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun if (drm->dmem == NULL)
346*4882a593Smuzhiyun return;
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun mutex_lock(&drm->dmem->mutex);
349*4882a593Smuzhiyun list_for_each_entry(chunk, &drm->dmem->chunks, list) {
350*4882a593Smuzhiyun ret = nouveau_bo_pin(chunk->bo, NOUVEAU_GEM_DOMAIN_VRAM, false);
351*4882a593Smuzhiyun /* FIXME handle pin failure */
352*4882a593Smuzhiyun WARN_ON(ret);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun mutex_unlock(&drm->dmem->mutex);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun void
nouveau_dmem_suspend(struct nouveau_drm * drm)358*4882a593Smuzhiyun nouveau_dmem_suspend(struct nouveau_drm *drm)
359*4882a593Smuzhiyun {
360*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun if (drm->dmem == NULL)
363*4882a593Smuzhiyun return;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun mutex_lock(&drm->dmem->mutex);
366*4882a593Smuzhiyun list_for_each_entry(chunk, &drm->dmem->chunks, list)
367*4882a593Smuzhiyun nouveau_bo_unpin(chunk->bo);
368*4882a593Smuzhiyun mutex_unlock(&drm->dmem->mutex);
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun void
nouveau_dmem_fini(struct nouveau_drm * drm)372*4882a593Smuzhiyun nouveau_dmem_fini(struct nouveau_drm *drm)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun struct nouveau_dmem_chunk *chunk, *tmp;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun if (drm->dmem == NULL)
377*4882a593Smuzhiyun return;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun mutex_lock(&drm->dmem->mutex);
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun list_for_each_entry_safe(chunk, tmp, &drm->dmem->chunks, list) {
382*4882a593Smuzhiyun nouveau_bo_unpin(chunk->bo);
383*4882a593Smuzhiyun nouveau_bo_ref(NULL, &chunk->bo);
384*4882a593Smuzhiyun list_del(&chunk->list);
385*4882a593Smuzhiyun memunmap_pages(&chunk->pagemap);
386*4882a593Smuzhiyun release_mem_region(chunk->pagemap.range.start,
387*4882a593Smuzhiyun range_len(&chunk->pagemap.range));
388*4882a593Smuzhiyun kfree(chunk);
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun mutex_unlock(&drm->dmem->mutex);
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun static int
nvc0b5_migrate_copy(struct nouveau_drm * drm,u64 npages,enum nouveau_aper dst_aper,u64 dst_addr,enum nouveau_aper src_aper,u64 src_addr)395*4882a593Smuzhiyun nvc0b5_migrate_copy(struct nouveau_drm *drm, u64 npages,
396*4882a593Smuzhiyun enum nouveau_aper dst_aper, u64 dst_addr,
397*4882a593Smuzhiyun enum nouveau_aper src_aper, u64 src_addr)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun struct nvif_push *push = drm->dmem->migrate.chan->chan.push;
400*4882a593Smuzhiyun u32 launch_dma = 0;
401*4882a593Smuzhiyun int ret;
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun ret = PUSH_WAIT(push, 13);
404*4882a593Smuzhiyun if (ret)
405*4882a593Smuzhiyun return ret;
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun if (src_aper != NOUVEAU_APER_VIRT) {
408*4882a593Smuzhiyun switch (src_aper) {
409*4882a593Smuzhiyun case NOUVEAU_APER_VRAM:
410*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_SRC_PHYS_MODE,
411*4882a593Smuzhiyun NVDEF(NVA0B5, SET_SRC_PHYS_MODE, TARGET, LOCAL_FB));
412*4882a593Smuzhiyun break;
413*4882a593Smuzhiyun case NOUVEAU_APER_HOST:
414*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_SRC_PHYS_MODE,
415*4882a593Smuzhiyun NVDEF(NVA0B5, SET_SRC_PHYS_MODE, TARGET, COHERENT_SYSMEM));
416*4882a593Smuzhiyun break;
417*4882a593Smuzhiyun default:
418*4882a593Smuzhiyun return -EINVAL;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun launch_dma |= NVDEF(NVA0B5, LAUNCH_DMA, SRC_TYPE, PHYSICAL);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun if (dst_aper != NOUVEAU_APER_VIRT) {
425*4882a593Smuzhiyun switch (dst_aper) {
426*4882a593Smuzhiyun case NOUVEAU_APER_VRAM:
427*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_DST_PHYS_MODE,
428*4882a593Smuzhiyun NVDEF(NVA0B5, SET_DST_PHYS_MODE, TARGET, LOCAL_FB));
429*4882a593Smuzhiyun break;
430*4882a593Smuzhiyun case NOUVEAU_APER_HOST:
431*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_DST_PHYS_MODE,
432*4882a593Smuzhiyun NVDEF(NVA0B5, SET_DST_PHYS_MODE, TARGET, COHERENT_SYSMEM));
433*4882a593Smuzhiyun break;
434*4882a593Smuzhiyun default:
435*4882a593Smuzhiyun return -EINVAL;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun launch_dma |= NVDEF(NVA0B5, LAUNCH_DMA, DST_TYPE, PHYSICAL);
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, OFFSET_IN_UPPER,
442*4882a593Smuzhiyun NVVAL(NVA0B5, OFFSET_IN_UPPER, UPPER, upper_32_bits(src_addr)),
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun OFFSET_IN_LOWER, lower_32_bits(src_addr),
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun OFFSET_OUT_UPPER,
447*4882a593Smuzhiyun NVVAL(NVA0B5, OFFSET_OUT_UPPER, UPPER, upper_32_bits(dst_addr)),
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun OFFSET_OUT_LOWER, lower_32_bits(dst_addr),
450*4882a593Smuzhiyun PITCH_IN, PAGE_SIZE,
451*4882a593Smuzhiyun PITCH_OUT, PAGE_SIZE,
452*4882a593Smuzhiyun LINE_LENGTH_IN, PAGE_SIZE,
453*4882a593Smuzhiyun LINE_COUNT, npages);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, LAUNCH_DMA, launch_dma |
456*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, DATA_TRANSFER_TYPE, NON_PIPELINED) |
457*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, FLUSH_ENABLE, TRUE) |
458*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, SEMAPHORE_TYPE, NONE) |
459*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, INTERRUPT_TYPE, NONE) |
460*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, SRC_MEMORY_LAYOUT, PITCH) |
461*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, DST_MEMORY_LAYOUT, PITCH) |
462*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, MULTI_LINE_ENABLE, TRUE) |
463*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, REMAP_ENABLE, FALSE) |
464*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, BYPASS_L2, USE_PTE_SETTING));
465*4882a593Smuzhiyun return 0;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun static int
nvc0b5_migrate_clear(struct nouveau_drm * drm,u32 length,enum nouveau_aper dst_aper,u64 dst_addr)469*4882a593Smuzhiyun nvc0b5_migrate_clear(struct nouveau_drm *drm, u32 length,
470*4882a593Smuzhiyun enum nouveau_aper dst_aper, u64 dst_addr)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun struct nvif_push *push = drm->dmem->migrate.chan->chan.push;
473*4882a593Smuzhiyun u32 launch_dma = 0;
474*4882a593Smuzhiyun int ret;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun ret = PUSH_WAIT(push, 12);
477*4882a593Smuzhiyun if (ret)
478*4882a593Smuzhiyun return ret;
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun switch (dst_aper) {
481*4882a593Smuzhiyun case NOUVEAU_APER_VRAM:
482*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_DST_PHYS_MODE,
483*4882a593Smuzhiyun NVDEF(NVA0B5, SET_DST_PHYS_MODE, TARGET, LOCAL_FB));
484*4882a593Smuzhiyun break;
485*4882a593Smuzhiyun case NOUVEAU_APER_HOST:
486*4882a593Smuzhiyun PUSH_IMMD(push, NVA0B5, SET_DST_PHYS_MODE,
487*4882a593Smuzhiyun NVDEF(NVA0B5, SET_DST_PHYS_MODE, TARGET, COHERENT_SYSMEM));
488*4882a593Smuzhiyun break;
489*4882a593Smuzhiyun default:
490*4882a593Smuzhiyun return -EINVAL;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun launch_dma |= NVDEF(NVA0B5, LAUNCH_DMA, DST_TYPE, PHYSICAL);
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, SET_REMAP_CONST_A, 0,
496*4882a593Smuzhiyun SET_REMAP_CONST_B, 0,
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun SET_REMAP_COMPONENTS,
499*4882a593Smuzhiyun NVDEF(NVA0B5, SET_REMAP_COMPONENTS, DST_X, CONST_A) |
500*4882a593Smuzhiyun NVDEF(NVA0B5, SET_REMAP_COMPONENTS, DST_Y, CONST_B) |
501*4882a593Smuzhiyun NVDEF(NVA0B5, SET_REMAP_COMPONENTS, COMPONENT_SIZE, FOUR) |
502*4882a593Smuzhiyun NVDEF(NVA0B5, SET_REMAP_COMPONENTS, NUM_DST_COMPONENTS, TWO));
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, OFFSET_OUT_UPPER,
505*4882a593Smuzhiyun NVVAL(NVA0B5, OFFSET_OUT_UPPER, UPPER, upper_32_bits(dst_addr)),
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun OFFSET_OUT_LOWER, lower_32_bits(dst_addr));
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, LINE_LENGTH_IN, length >> 3);
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun PUSH_MTHD(push, NVA0B5, LAUNCH_DMA, launch_dma |
512*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, DATA_TRANSFER_TYPE, NON_PIPELINED) |
513*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, FLUSH_ENABLE, TRUE) |
514*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, SEMAPHORE_TYPE, NONE) |
515*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, INTERRUPT_TYPE, NONE) |
516*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, SRC_MEMORY_LAYOUT, PITCH) |
517*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, DST_MEMORY_LAYOUT, PITCH) |
518*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, MULTI_LINE_ENABLE, FALSE) |
519*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, REMAP_ENABLE, TRUE) |
520*4882a593Smuzhiyun NVDEF(NVA0B5, LAUNCH_DMA, BYPASS_L2, USE_PTE_SETTING));
521*4882a593Smuzhiyun return 0;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun static int
nouveau_dmem_migrate_init(struct nouveau_drm * drm)525*4882a593Smuzhiyun nouveau_dmem_migrate_init(struct nouveau_drm *drm)
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun switch (drm->ttm.copy.oclass) {
528*4882a593Smuzhiyun case PASCAL_DMA_COPY_A:
529*4882a593Smuzhiyun case PASCAL_DMA_COPY_B:
530*4882a593Smuzhiyun case VOLTA_DMA_COPY_A:
531*4882a593Smuzhiyun case TURING_DMA_COPY_A:
532*4882a593Smuzhiyun drm->dmem->migrate.copy_func = nvc0b5_migrate_copy;
533*4882a593Smuzhiyun drm->dmem->migrate.clear_func = nvc0b5_migrate_clear;
534*4882a593Smuzhiyun drm->dmem->migrate.chan = drm->ttm.chan;
535*4882a593Smuzhiyun return 0;
536*4882a593Smuzhiyun default:
537*4882a593Smuzhiyun break;
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun return -ENODEV;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun void
nouveau_dmem_init(struct nouveau_drm * drm)543*4882a593Smuzhiyun nouveau_dmem_init(struct nouveau_drm *drm)
544*4882a593Smuzhiyun {
545*4882a593Smuzhiyun int ret;
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun /* This only make sense on PASCAL or newer */
548*4882a593Smuzhiyun if (drm->client.device.info.family < NV_DEVICE_INFO_V0_PASCAL)
549*4882a593Smuzhiyun return;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun if (!(drm->dmem = kzalloc(sizeof(*drm->dmem), GFP_KERNEL)))
552*4882a593Smuzhiyun return;
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun drm->dmem->drm = drm;
555*4882a593Smuzhiyun mutex_init(&drm->dmem->mutex);
556*4882a593Smuzhiyun INIT_LIST_HEAD(&drm->dmem->chunks);
557*4882a593Smuzhiyun mutex_init(&drm->dmem->mutex);
558*4882a593Smuzhiyun spin_lock_init(&drm->dmem->lock);
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun /* Initialize migration dma helpers before registering memory */
561*4882a593Smuzhiyun ret = nouveau_dmem_migrate_init(drm);
562*4882a593Smuzhiyun if (ret) {
563*4882a593Smuzhiyun kfree(drm->dmem);
564*4882a593Smuzhiyun drm->dmem = NULL;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun
nouveau_dmem_migrate_copy_one(struct nouveau_drm * drm,struct nouveau_svmm * svmm,unsigned long src,dma_addr_t * dma_addr,u64 * pfn)568*4882a593Smuzhiyun static unsigned long nouveau_dmem_migrate_copy_one(struct nouveau_drm *drm,
569*4882a593Smuzhiyun struct nouveau_svmm *svmm, unsigned long src,
570*4882a593Smuzhiyun dma_addr_t *dma_addr, u64 *pfn)
571*4882a593Smuzhiyun {
572*4882a593Smuzhiyun struct device *dev = drm->dev->dev;
573*4882a593Smuzhiyun struct page *dpage, *spage;
574*4882a593Smuzhiyun unsigned long paddr;
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun spage = migrate_pfn_to_page(src);
577*4882a593Smuzhiyun if (!(src & MIGRATE_PFN_MIGRATE))
578*4882a593Smuzhiyun goto out;
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun dpage = nouveau_dmem_page_alloc_locked(drm);
581*4882a593Smuzhiyun if (!dpage)
582*4882a593Smuzhiyun goto out;
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun paddr = nouveau_dmem_page_addr(dpage);
585*4882a593Smuzhiyun if (spage) {
586*4882a593Smuzhiyun *dma_addr = dma_map_page(dev, spage, 0, page_size(spage),
587*4882a593Smuzhiyun DMA_BIDIRECTIONAL);
588*4882a593Smuzhiyun if (dma_mapping_error(dev, *dma_addr))
589*4882a593Smuzhiyun goto out_free_page;
590*4882a593Smuzhiyun if (drm->dmem->migrate.copy_func(drm, 1,
591*4882a593Smuzhiyun NOUVEAU_APER_VRAM, paddr, NOUVEAU_APER_HOST, *dma_addr))
592*4882a593Smuzhiyun goto out_dma_unmap;
593*4882a593Smuzhiyun } else {
594*4882a593Smuzhiyun *dma_addr = DMA_MAPPING_ERROR;
595*4882a593Smuzhiyun if (drm->dmem->migrate.clear_func(drm, page_size(dpage),
596*4882a593Smuzhiyun NOUVEAU_APER_VRAM, paddr))
597*4882a593Smuzhiyun goto out_free_page;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun dpage->zone_device_data = svmm;
601*4882a593Smuzhiyun *pfn = NVIF_VMM_PFNMAP_V0_V | NVIF_VMM_PFNMAP_V0_VRAM |
602*4882a593Smuzhiyun ((paddr >> PAGE_SHIFT) << NVIF_VMM_PFNMAP_V0_ADDR_SHIFT);
603*4882a593Smuzhiyun if (src & MIGRATE_PFN_WRITE)
604*4882a593Smuzhiyun *pfn |= NVIF_VMM_PFNMAP_V0_W;
605*4882a593Smuzhiyun return migrate_pfn(page_to_pfn(dpage)) | MIGRATE_PFN_LOCKED;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun out_dma_unmap:
608*4882a593Smuzhiyun dma_unmap_page(dev, *dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
609*4882a593Smuzhiyun out_free_page:
610*4882a593Smuzhiyun nouveau_dmem_page_free_locked(drm, dpage);
611*4882a593Smuzhiyun out:
612*4882a593Smuzhiyun *pfn = NVIF_VMM_PFNMAP_V0_NONE;
613*4882a593Smuzhiyun return 0;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
nouveau_dmem_migrate_chunk(struct nouveau_drm * drm,struct nouveau_svmm * svmm,struct migrate_vma * args,dma_addr_t * dma_addrs,u64 * pfns)616*4882a593Smuzhiyun static void nouveau_dmem_migrate_chunk(struct nouveau_drm *drm,
617*4882a593Smuzhiyun struct nouveau_svmm *svmm, struct migrate_vma *args,
618*4882a593Smuzhiyun dma_addr_t *dma_addrs, u64 *pfns)
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun struct nouveau_fence *fence;
621*4882a593Smuzhiyun unsigned long addr = args->start, nr_dma = 0, i;
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun for (i = 0; addr < args->end; i++) {
624*4882a593Smuzhiyun args->dst[i] = nouveau_dmem_migrate_copy_one(drm, svmm,
625*4882a593Smuzhiyun args->src[i], dma_addrs + nr_dma, pfns + i);
626*4882a593Smuzhiyun if (!dma_mapping_error(drm->dev->dev, dma_addrs[nr_dma]))
627*4882a593Smuzhiyun nr_dma++;
628*4882a593Smuzhiyun addr += PAGE_SIZE;
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun nouveau_fence_new(drm->dmem->migrate.chan, false, &fence);
632*4882a593Smuzhiyun migrate_vma_pages(args);
633*4882a593Smuzhiyun nouveau_dmem_fence_done(&fence);
634*4882a593Smuzhiyun nouveau_pfns_map(svmm, args->vma->vm_mm, args->start, pfns, i);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun while (nr_dma--) {
637*4882a593Smuzhiyun dma_unmap_page(drm->dev->dev, dma_addrs[nr_dma], PAGE_SIZE,
638*4882a593Smuzhiyun DMA_BIDIRECTIONAL);
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun migrate_vma_finalize(args);
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun int
nouveau_dmem_migrate_vma(struct nouveau_drm * drm,struct nouveau_svmm * svmm,struct vm_area_struct * vma,unsigned long start,unsigned long end)644*4882a593Smuzhiyun nouveau_dmem_migrate_vma(struct nouveau_drm *drm,
645*4882a593Smuzhiyun struct nouveau_svmm *svmm,
646*4882a593Smuzhiyun struct vm_area_struct *vma,
647*4882a593Smuzhiyun unsigned long start,
648*4882a593Smuzhiyun unsigned long end)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun unsigned long npages = (end - start) >> PAGE_SHIFT;
651*4882a593Smuzhiyun unsigned long max = min(SG_MAX_SINGLE_ALLOC, npages);
652*4882a593Smuzhiyun dma_addr_t *dma_addrs;
653*4882a593Smuzhiyun struct migrate_vma args = {
654*4882a593Smuzhiyun .vma = vma,
655*4882a593Smuzhiyun .start = start,
656*4882a593Smuzhiyun .pgmap_owner = drm->dev,
657*4882a593Smuzhiyun .flags = MIGRATE_VMA_SELECT_SYSTEM,
658*4882a593Smuzhiyun };
659*4882a593Smuzhiyun unsigned long i;
660*4882a593Smuzhiyun u64 *pfns;
661*4882a593Smuzhiyun int ret = -ENOMEM;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun if (drm->dmem == NULL)
664*4882a593Smuzhiyun return -ENODEV;
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun args.src = kcalloc(max, sizeof(*args.src), GFP_KERNEL);
667*4882a593Smuzhiyun if (!args.src)
668*4882a593Smuzhiyun goto out;
669*4882a593Smuzhiyun args.dst = kcalloc(max, sizeof(*args.dst), GFP_KERNEL);
670*4882a593Smuzhiyun if (!args.dst)
671*4882a593Smuzhiyun goto out_free_src;
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun dma_addrs = kmalloc_array(max, sizeof(*dma_addrs), GFP_KERNEL);
674*4882a593Smuzhiyun if (!dma_addrs)
675*4882a593Smuzhiyun goto out_free_dst;
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun pfns = nouveau_pfns_alloc(max);
678*4882a593Smuzhiyun if (!pfns)
679*4882a593Smuzhiyun goto out_free_dma;
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun for (i = 0; i < npages; i += max) {
682*4882a593Smuzhiyun if (args.start + (max << PAGE_SHIFT) > end)
683*4882a593Smuzhiyun args.end = end;
684*4882a593Smuzhiyun else
685*4882a593Smuzhiyun args.end = args.start + (max << PAGE_SHIFT);
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun ret = migrate_vma_setup(&args);
688*4882a593Smuzhiyun if (ret)
689*4882a593Smuzhiyun goto out_free_pfns;
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun if (args.cpages)
692*4882a593Smuzhiyun nouveau_dmem_migrate_chunk(drm, svmm, &args, dma_addrs,
693*4882a593Smuzhiyun pfns);
694*4882a593Smuzhiyun args.start = args.end;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun ret = 0;
698*4882a593Smuzhiyun out_free_pfns:
699*4882a593Smuzhiyun nouveau_pfns_free(pfns);
700*4882a593Smuzhiyun out_free_dma:
701*4882a593Smuzhiyun kfree(dma_addrs);
702*4882a593Smuzhiyun out_free_dst:
703*4882a593Smuzhiyun kfree(args.dst);
704*4882a593Smuzhiyun out_free_src:
705*4882a593Smuzhiyun kfree(args.src);
706*4882a593Smuzhiyun out:
707*4882a593Smuzhiyun return ret;
708*4882a593Smuzhiyun }
709