xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/nouveau/nouveau_dmem.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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