xref: /openbsd-src/sys/dev/pci/drm/i915/gem/i915_gem_phys.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * SPDX-License-Identifier: MIT
3c349dbc7Sjsg  *
4c349dbc7Sjsg  * Copyright © 2014-2016 Intel Corporation
5c349dbc7Sjsg  */
6c349dbc7Sjsg 
7c349dbc7Sjsg #include <linux/highmem.h>
8c349dbc7Sjsg #include <linux/shmem_fs.h>
9c349dbc7Sjsg #include <linux/swap.h>
10c349dbc7Sjsg 
11c349dbc7Sjsg #include <drm/drm_cache.h>
125ca02815Sjsg #include <drm/drm_legacy.h>	/* for drm_dmamem_alloc() */
13c349dbc7Sjsg 
14c349dbc7Sjsg #include "gt/intel_gt.h"
15c349dbc7Sjsg #include "i915_drv.h"
16c349dbc7Sjsg #include "i915_gem_object.h"
17c349dbc7Sjsg #include "i915_gem_region.h"
181bb76ff1Sjsg #include "i915_gem_tiling.h"
19c349dbc7Sjsg #include "i915_scatterlist.h"
20c349dbc7Sjsg 
i915_gem_object_get_pages_phys(struct drm_i915_gem_object * obj)21c349dbc7Sjsg static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
22c349dbc7Sjsg {
23c349dbc7Sjsg #ifdef __linux__
24c349dbc7Sjsg 	struct address_space *mapping = obj->base.filp->f_mapping;
25c349dbc7Sjsg #else
265ca02815Sjsg 	struct drm_dmamem *dmah;
275ca02815Sjsg 	int flags = 0;
28c349dbc7Sjsg #endif
291bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
30c349dbc7Sjsg 	struct scatterlist *sg;
31c349dbc7Sjsg 	struct sg_table *st;
32c349dbc7Sjsg 	dma_addr_t dma;
33c349dbc7Sjsg 	void *vaddr;
34c349dbc7Sjsg 	void *dst;
35c349dbc7Sjsg 	int i;
36c349dbc7Sjsg 
37*f005ef32Sjsg 	/* Contiguous chunk, with a single scatterlist element */
38*f005ef32Sjsg 	if (overflows_type(obj->base.size, sg->length))
39*f005ef32Sjsg 		return -E2BIG;
40*f005ef32Sjsg 
41ad8b1aafSjsg 	if (GEM_WARN_ON(i915_gem_object_needs_bit17_swizzle(obj)))
42c349dbc7Sjsg 		return -EINVAL;
43c349dbc7Sjsg 
44c349dbc7Sjsg 	/*
45c349dbc7Sjsg 	 * Always aligning to the object size, allows a single allocation
46c349dbc7Sjsg 	 * to handle all possible callers, and given typical object sizes,
47c349dbc7Sjsg 	 * the alignment of the buddy allocation will naturally match.
48c349dbc7Sjsg 	 */
49c349dbc7Sjsg #ifdef __linux__
505ca02815Sjsg 	vaddr = dma_alloc_coherent(obj->base.dev->dev,
51c349dbc7Sjsg 				   roundup_pow_of_two(obj->base.size),
52c349dbc7Sjsg 				   &dma, GFP_KERNEL);
53c349dbc7Sjsg 	if (!vaddr)
54c349dbc7Sjsg 		return -ENOMEM;
55c349dbc7Sjsg #else
565ca02815Sjsg 	dmah = drm_dmamem_alloc(i915->dmat,
57c349dbc7Sjsg 	    roundup_pow_of_two(obj->base.size),
585ca02815Sjsg 	    PAGE_SIZE, 1,
595ca02815Sjsg 	    roundup_pow_of_two(obj->base.size), flags, 0);
605ca02815Sjsg 	if (dmah == NULL)
61c349dbc7Sjsg 		return -ENOMEM;
625ca02815Sjsg 	dma = dmah->map->dm_segs[0].ds_addr;
635ca02815Sjsg 	vaddr = dmah->kva;
64c349dbc7Sjsg #endif
65c349dbc7Sjsg 
66c349dbc7Sjsg 	st = kmalloc(sizeof(*st), GFP_KERNEL);
67c349dbc7Sjsg 	if (!st)
68c349dbc7Sjsg 		goto err_pci;
69c349dbc7Sjsg 
70c349dbc7Sjsg 	if (sg_alloc_table(st, 1, GFP_KERNEL))
71c349dbc7Sjsg 		goto err_st;
72c349dbc7Sjsg 
73c349dbc7Sjsg 	sg = st->sgl;
74c349dbc7Sjsg 	sg->offset = 0;
75c349dbc7Sjsg 	sg->length = obj->base.size;
76c349dbc7Sjsg 
77c349dbc7Sjsg #ifdef __linux__
78c349dbc7Sjsg 	sg_assign_page(sg, (struct page *)vaddr);
79c349dbc7Sjsg #else
805ca02815Sjsg 	sg_assign_page(sg, (struct vm_page *)dmah);
81c349dbc7Sjsg #endif
82c349dbc7Sjsg 	sg_dma_address(sg) = dma;
83c349dbc7Sjsg 	sg_dma_len(sg) = obj->base.size;
84c349dbc7Sjsg 
85c349dbc7Sjsg 	dst = vaddr;
86c349dbc7Sjsg 	for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
87c349dbc7Sjsg 		struct vm_page *page;
88c349dbc7Sjsg 		void *src;
89c349dbc7Sjsg 
90c349dbc7Sjsg #ifdef  __linux__
91c349dbc7Sjsg 		page = shmem_read_mapping_page(mapping, i);
92c349dbc7Sjsg 		if (IS_ERR(page))
93c349dbc7Sjsg 			goto err_st;
94c349dbc7Sjsg #else
95c349dbc7Sjsg 		struct pglist plist;
96c349dbc7Sjsg 		TAILQ_INIT(&plist);
97da3d0110Smpi 		if (uvm_obj_wire(obj->base.uao, i * PAGE_SIZE,
98c349dbc7Sjsg 				(i + 1) * PAGE_SIZE, &plist))
99c349dbc7Sjsg 			goto err_st;
100c349dbc7Sjsg 		page = TAILQ_FIRST(&plist);
101c349dbc7Sjsg #endif
102c349dbc7Sjsg 
103c349dbc7Sjsg 		src = kmap_atomic(page);
104c349dbc7Sjsg 		memcpy(dst, src, PAGE_SIZE);
105c349dbc7Sjsg 		drm_clflush_virt_range(dst, PAGE_SIZE);
106c349dbc7Sjsg 		kunmap_atomic(src);
107c349dbc7Sjsg 
108c349dbc7Sjsg #ifdef __linux__
109c349dbc7Sjsg 		put_page(page);
110c349dbc7Sjsg #else
111da3d0110Smpi 		uvm_obj_unwire(obj->base.uao, i * PAGE_SIZE,
112c349dbc7Sjsg 			      (i + 1) * PAGE_SIZE);
113c349dbc7Sjsg #endif
114c349dbc7Sjsg 		dst += PAGE_SIZE;
115c349dbc7Sjsg 	}
116c349dbc7Sjsg 
1171bb76ff1Sjsg 	intel_gt_chipset_flush(to_gt(i915));
118c349dbc7Sjsg 
1195ca02815Sjsg 	/* We're no longer struct page backed */
1205ca02815Sjsg 	obj->mem_flags &= ~I915_BO_FLAG_STRUCT_PAGE;
121*f005ef32Sjsg 	__i915_gem_object_set_pages(obj, st);
122c349dbc7Sjsg 
123c349dbc7Sjsg 	return 0;
124c349dbc7Sjsg 
125c349dbc7Sjsg err_st:
126c349dbc7Sjsg 	kfree(st);
127c349dbc7Sjsg err_pci:
128c349dbc7Sjsg #ifdef __linux__
1295ca02815Sjsg 	dma_free_coherent(obj->base.dev->dev,
130c349dbc7Sjsg 			  roundup_pow_of_two(obj->base.size),
131c349dbc7Sjsg 			  vaddr, dma);
132c349dbc7Sjsg #else
1335ca02815Sjsg 	drm_dmamem_free(i915->dmat, dmah);
134c349dbc7Sjsg #endif
135c349dbc7Sjsg 	return -ENOMEM;
136c349dbc7Sjsg }
137c349dbc7Sjsg 
1385ca02815Sjsg void
i915_gem_object_put_pages_phys(struct drm_i915_gem_object * obj,struct sg_table * pages)139c349dbc7Sjsg i915_gem_object_put_pages_phys(struct drm_i915_gem_object *obj,
140c349dbc7Sjsg 			       struct sg_table *pages)
141c349dbc7Sjsg {
142c349dbc7Sjsg 	dma_addr_t dma = sg_dma_address(pages->sgl);
143c349dbc7Sjsg #ifdef __linux__
144c349dbc7Sjsg 	void *vaddr = sg_page(pages->sgl);
145c349dbc7Sjsg #else
1465ca02815Sjsg 	struct drm_dmamem *dmah = (void *)sg_page(pages->sgl);
1475ca02815Sjsg 	void *vaddr = dmah->kva;
1485ca02815Sjsg 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
149c349dbc7Sjsg #endif
150c349dbc7Sjsg 
151c349dbc7Sjsg 	__i915_gem_object_release_shmem(obj, pages, false);
152c349dbc7Sjsg 
153c349dbc7Sjsg 	if (obj->mm.dirty) {
154c349dbc7Sjsg #ifdef __linux__
155c349dbc7Sjsg 		struct address_space *mapping = obj->base.filp->f_mapping;
156c349dbc7Sjsg #endif
157c349dbc7Sjsg 		void *src = vaddr;
158c349dbc7Sjsg 		int i;
159c349dbc7Sjsg 
160c349dbc7Sjsg 		for (i = 0; i < obj->base.size / PAGE_SIZE; i++) {
161c349dbc7Sjsg 			struct vm_page *page;
162c349dbc7Sjsg 			char *dst;
163c349dbc7Sjsg 
164c349dbc7Sjsg #ifdef __linux__
165c349dbc7Sjsg 			page = shmem_read_mapping_page(mapping, i);
166c349dbc7Sjsg 			if (IS_ERR(page))
167c349dbc7Sjsg 				continue;
168c349dbc7Sjsg #else
169c349dbc7Sjsg 			struct pglist plist;
170c349dbc7Sjsg 			TAILQ_INIT(&plist);
171da3d0110Smpi 			if (uvm_obj_wire(obj->base.uao, i * PAGE_SIZE,
172c349dbc7Sjsg 					(i + 1) * PAGE_SIZE, &plist))
173c349dbc7Sjsg 				continue;
174c349dbc7Sjsg 			page = TAILQ_FIRST(&plist);
175c349dbc7Sjsg #endif
176c349dbc7Sjsg 
177c349dbc7Sjsg 			dst = kmap_atomic(page);
178c349dbc7Sjsg 			drm_clflush_virt_range(src, PAGE_SIZE);
179c349dbc7Sjsg 			memcpy(dst, src, PAGE_SIZE);
180c349dbc7Sjsg 			kunmap_atomic(dst);
181c349dbc7Sjsg 
182c349dbc7Sjsg 			set_page_dirty(page);
183c349dbc7Sjsg #ifdef __linux__
184c349dbc7Sjsg 			if (obj->mm.madv == I915_MADV_WILLNEED)
185c349dbc7Sjsg 				mark_page_accessed(page);
186c349dbc7Sjsg 			put_page(page);
187c349dbc7Sjsg #else
188da3d0110Smpi 			uvm_obj_unwire(obj->base.uao, i * PAGE_SIZE,
189c349dbc7Sjsg 				      (i + 1) * PAGE_SIZE);
190c349dbc7Sjsg #endif
191c349dbc7Sjsg 
192c349dbc7Sjsg 			src += PAGE_SIZE;
193c349dbc7Sjsg 		}
194c349dbc7Sjsg 		obj->mm.dirty = false;
195c349dbc7Sjsg 	}
196c349dbc7Sjsg 
197c349dbc7Sjsg 	sg_free_table(pages);
198c349dbc7Sjsg 	kfree(pages);
199c349dbc7Sjsg 
200c349dbc7Sjsg #ifdef __linux__
2015ca02815Sjsg 	dma_free_coherent(obj->base.dev->dev,
202c349dbc7Sjsg 			  roundup_pow_of_two(obj->base.size),
203c349dbc7Sjsg 			  vaddr, dma);
204c349dbc7Sjsg #else
2055ca02815Sjsg 	drm_dmamem_free(i915->dmat, dmah);
206c349dbc7Sjsg #endif
207c349dbc7Sjsg }
208c349dbc7Sjsg 
i915_gem_object_pwrite_phys(struct drm_i915_gem_object * obj,const struct drm_i915_gem_pwrite * args)2095ca02815Sjsg int i915_gem_object_pwrite_phys(struct drm_i915_gem_object *obj,
210ad8b1aafSjsg 				const struct drm_i915_gem_pwrite *args)
211ad8b1aafSjsg {
2120db1f4c5Sjsg #ifdef __linux__
213ad8b1aafSjsg 	void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
2140db1f4c5Sjsg #else
2150db1f4c5Sjsg 	struct drm_dmamem *dmah = (void *)sg_page(obj->mm.pages->sgl);
2160db1f4c5Sjsg 	void *vaddr = dmah->kva + args->offset;
2170db1f4c5Sjsg #endif
218ad8b1aafSjsg 	char __user *user_data = u64_to_user_ptr(args->data_ptr);
2191bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
220ad8b1aafSjsg 	int err;
221ad8b1aafSjsg 
222ad8b1aafSjsg 	err = i915_gem_object_wait(obj,
223ad8b1aafSjsg 				   I915_WAIT_INTERRUPTIBLE |
224ad8b1aafSjsg 				   I915_WAIT_ALL,
225ad8b1aafSjsg 				   MAX_SCHEDULE_TIMEOUT);
226ad8b1aafSjsg 	if (err)
227ad8b1aafSjsg 		return err;
228ad8b1aafSjsg 
229ad8b1aafSjsg 	/*
230ad8b1aafSjsg 	 * We manually control the domain here and pretend that it
231ad8b1aafSjsg 	 * remains coherent i.e. in the GTT domain, like shmem_pwrite.
232ad8b1aafSjsg 	 */
233ad8b1aafSjsg 	i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);
234ad8b1aafSjsg 
235ad8b1aafSjsg 	if (copy_from_user(vaddr, user_data, args->size))
236ad8b1aafSjsg 		return -EFAULT;
237ad8b1aafSjsg 
238ad8b1aafSjsg 	drm_clflush_virt_range(vaddr, args->size);
2391bb76ff1Sjsg 	intel_gt_chipset_flush(to_gt(i915));
240ad8b1aafSjsg 
241ad8b1aafSjsg 	i915_gem_object_flush_frontbuffer(obj, ORIGIN_CPU);
242ad8b1aafSjsg 	return 0;
243ad8b1aafSjsg }
244ad8b1aafSjsg 
i915_gem_object_pread_phys(struct drm_i915_gem_object * obj,const struct drm_i915_gem_pread * args)2455ca02815Sjsg int i915_gem_object_pread_phys(struct drm_i915_gem_object *obj,
246ad8b1aafSjsg 			       const struct drm_i915_gem_pread *args)
247ad8b1aafSjsg {
2480db1f4c5Sjsg #ifdef __linux__
249ad8b1aafSjsg 	void *vaddr = sg_page(obj->mm.pages->sgl) + args->offset;
2500db1f4c5Sjsg #else
2510db1f4c5Sjsg 	struct drm_dmamem *dmah = (void *)sg_page(obj->mm.pages->sgl);
2520db1f4c5Sjsg 	void *vaddr = dmah->kva + args->offset;
2530db1f4c5Sjsg #endif
254ad8b1aafSjsg 	char __user *user_data = u64_to_user_ptr(args->data_ptr);
255ad8b1aafSjsg 	int err;
256ad8b1aafSjsg 
257ad8b1aafSjsg 	err = i915_gem_object_wait(obj,
258ad8b1aafSjsg 				   I915_WAIT_INTERRUPTIBLE,
259ad8b1aafSjsg 				   MAX_SCHEDULE_TIMEOUT);
260ad8b1aafSjsg 	if (err)
261ad8b1aafSjsg 		return err;
262ad8b1aafSjsg 
263ad8b1aafSjsg 	drm_clflush_virt_range(vaddr, args->size);
264ad8b1aafSjsg 	if (copy_to_user(user_data, vaddr, args->size))
265ad8b1aafSjsg 		return -EFAULT;
266ad8b1aafSjsg 
267ad8b1aafSjsg 	return 0;
268ad8b1aafSjsg }
269ad8b1aafSjsg 
i915_gem_object_shmem_to_phys(struct drm_i915_gem_object * obj)2705ca02815Sjsg static int i915_gem_object_shmem_to_phys(struct drm_i915_gem_object *obj)
271c349dbc7Sjsg {
272c349dbc7Sjsg 	struct sg_table *pages;
273c349dbc7Sjsg 	int err;
274c349dbc7Sjsg 
275c349dbc7Sjsg 	pages = __i915_gem_object_unset_pages(obj);
276c349dbc7Sjsg 
2775ca02815Sjsg 	err = i915_gem_object_get_pages_phys(obj);
278c349dbc7Sjsg 	if (err)
279c349dbc7Sjsg 		goto err_xfer;
280c349dbc7Sjsg 
281c349dbc7Sjsg 	/* Perma-pin (until release) the physical set of pages */
282c349dbc7Sjsg 	__i915_gem_object_pin_pages(obj);
283c349dbc7Sjsg 
284c349dbc7Sjsg 	if (!IS_ERR_OR_NULL(pages))
2855ca02815Sjsg 		i915_gem_object_put_pages_shmem(obj, pages);
286c349dbc7Sjsg 
287c349dbc7Sjsg 	i915_gem_object_release_memory_region(obj);
288c349dbc7Sjsg 	return 0;
289c349dbc7Sjsg 
290c349dbc7Sjsg err_xfer:
291*f005ef32Sjsg 	if (!IS_ERR_OR_NULL(pages))
292*f005ef32Sjsg 		__i915_gem_object_set_pages(obj, pages);
293c349dbc7Sjsg 	return err;
294c349dbc7Sjsg }
295c349dbc7Sjsg 
i915_gem_object_attach_phys(struct drm_i915_gem_object * obj,int align)2965ca02815Sjsg int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
2975ca02815Sjsg {
2985ca02815Sjsg 	int err;
2995ca02815Sjsg 
3005ca02815Sjsg 	assert_object_held(obj);
3015ca02815Sjsg 
3025ca02815Sjsg 	if (align > obj->base.size)
3035ca02815Sjsg 		return -EINVAL;
3045ca02815Sjsg 
3055ca02815Sjsg 	if (!i915_gem_object_is_shmem(obj))
3065ca02815Sjsg 		return -EINVAL;
3075ca02815Sjsg 
3085ca02815Sjsg 	if (!i915_gem_object_has_struct_page(obj))
3095ca02815Sjsg 		return 0;
3105ca02815Sjsg 
3115ca02815Sjsg 	err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
3125ca02815Sjsg 	if (err)
3135ca02815Sjsg 		return err;
3145ca02815Sjsg 
3155ca02815Sjsg 	if (obj->mm.madv != I915_MADV_WILLNEED)
3165ca02815Sjsg 		return -EFAULT;
3175ca02815Sjsg 
3185ca02815Sjsg 	if (i915_gem_object_has_tiling_quirk(obj))
3195ca02815Sjsg 		return -EFAULT;
3205ca02815Sjsg 
3215ca02815Sjsg 	if (obj->mm.mapping || i915_gem_object_has_pinned_pages(obj))
3225ca02815Sjsg 		return -EBUSY;
3235ca02815Sjsg 
3245ca02815Sjsg 	if (unlikely(obj->mm.madv != I915_MADV_WILLNEED)) {
3255ca02815Sjsg 		drm_dbg(obj->base.dev,
3265ca02815Sjsg 			"Attempting to obtain a purgeable object\n");
3275ca02815Sjsg 		return -EFAULT;
3285ca02815Sjsg 	}
3295ca02815Sjsg 
3305ca02815Sjsg 	return i915_gem_object_shmem_to_phys(obj);
3315ca02815Sjsg }
3325ca02815Sjsg 
333c349dbc7Sjsg #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
334c349dbc7Sjsg #include "selftests/i915_gem_phys.c"
335c349dbc7Sjsg #endif
336