xref: /openbsd-src/sys/dev/pci/drm/i915/gt/shmem_utils.c (revision 0a503ede77f395ac19eba8770e42c9e0665a730d)
1ad8b1aafSjsg // SPDX-License-Identifier: MIT
2ad8b1aafSjsg /*
3ad8b1aafSjsg  * Copyright © 2020 Intel Corporation
4ad8b1aafSjsg  */
5ad8b1aafSjsg 
61bb76ff1Sjsg #include <linux/iosys-map.h>
7ad8b1aafSjsg #include <linux/mm.h>
8ad8b1aafSjsg #include <linux/pagemap.h>
9ad8b1aafSjsg #include <linux/shmem_fs.h>
10ad8b1aafSjsg 
11f005ef32Sjsg #include "i915_drv.h"
12ad8b1aafSjsg #include "gem/i915_gem_object.h"
135ca02815Sjsg #include "gem/i915_gem_lmem.h"
14ad8b1aafSjsg #include "shmem_utils.h"
15ad8b1aafSjsg 
16ad8b1aafSjsg #ifdef __linux__
17ad8b1aafSjsg 
shmem_create_from_data(const char * name,void * data,size_t len)18ad8b1aafSjsg struct file *shmem_create_from_data(const char *name, void *data, size_t len)
19ad8b1aafSjsg {
20ad8b1aafSjsg 	struct file *file;
21ad8b1aafSjsg 	int err;
22ad8b1aafSjsg 
23ad8b1aafSjsg 	file = shmem_file_setup(name, PAGE_ALIGN(len), VM_NORESERVE);
24ad8b1aafSjsg 	if (IS_ERR(file))
25ad8b1aafSjsg 		return file;
26ad8b1aafSjsg 
27ad8b1aafSjsg 	err = shmem_write(file, 0, data, len);
28ad8b1aafSjsg 	if (err) {
29ad8b1aafSjsg 		fput(file);
30ad8b1aafSjsg 		return ERR_PTR(err);
31ad8b1aafSjsg 	}
32ad8b1aafSjsg 
33ad8b1aafSjsg 	return file;
34ad8b1aafSjsg }
35ad8b1aafSjsg 
shmem_create_from_object(struct drm_i915_gem_object * obj)36ad8b1aafSjsg struct file *shmem_create_from_object(struct drm_i915_gem_object *obj)
37ad8b1aafSjsg {
38f005ef32Sjsg 	enum i915_map_type map_type;
39ad8b1aafSjsg 	struct file *file;
40ad8b1aafSjsg 	void *ptr;
41ad8b1aafSjsg 
425ca02815Sjsg 	if (i915_gem_object_is_shmem(obj)) {
43ad8b1aafSjsg 		file = obj->base.filp;
44ad8b1aafSjsg 		atomic_long_inc(&file->f_count);
45ad8b1aafSjsg 		return file;
46ad8b1aafSjsg 	}
47ad8b1aafSjsg 
48f005ef32Sjsg 	map_type = i915_gem_object_is_lmem(obj) ? I915_MAP_WC : I915_MAP_WB;
49f005ef32Sjsg 	ptr = i915_gem_object_pin_map_unlocked(obj, map_type);
50ad8b1aafSjsg 	if (IS_ERR(ptr))
51ad8b1aafSjsg 		return ERR_CAST(ptr);
52ad8b1aafSjsg 
53ad8b1aafSjsg 	file = shmem_create_from_data("", ptr, obj->base.size);
54ad8b1aafSjsg 	i915_gem_object_unpin_map(obj);
55ad8b1aafSjsg 
56ad8b1aafSjsg 	return file;
57ad8b1aafSjsg }
58ad8b1aafSjsg 
shmem_pin_map(struct file * file)59ad8b1aafSjsg void *shmem_pin_map(struct file *file)
60ad8b1aafSjsg {
61ad8b1aafSjsg 	struct page **pages;
62ad8b1aafSjsg 	size_t n_pages, i;
63ad8b1aafSjsg 	void *vaddr;
64ad8b1aafSjsg 
65ad8b1aafSjsg 	n_pages = file->f_mapping->host->i_size >> PAGE_SHIFT;
66ad8b1aafSjsg 	pages = kvmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
67ad8b1aafSjsg 	if (!pages)
68ad8b1aafSjsg 		return NULL;
69ad8b1aafSjsg 
70ad8b1aafSjsg 	for (i = 0; i < n_pages; i++) {
71ad8b1aafSjsg 		pages[i] = shmem_read_mapping_page_gfp(file->f_mapping, i,
72ad8b1aafSjsg 						       GFP_KERNEL);
73ad8b1aafSjsg 		if (IS_ERR(pages[i]))
74ad8b1aafSjsg 			goto err_page;
75ad8b1aafSjsg 	}
76ad8b1aafSjsg 
77ad8b1aafSjsg 	vaddr = vmap(pages, n_pages, VM_MAP_PUT_PAGES, PAGE_KERNEL);
78ad8b1aafSjsg 	if (!vaddr)
79ad8b1aafSjsg 		goto err_page;
80ad8b1aafSjsg 	mapping_set_unevictable(file->f_mapping);
81ad8b1aafSjsg 	return vaddr;
82ad8b1aafSjsg err_page:
83ad8b1aafSjsg 	while (i--)
84ad8b1aafSjsg 		put_page(pages[i]);
85ad8b1aafSjsg 	kvfree(pages);
86ad8b1aafSjsg 	return NULL;
87ad8b1aafSjsg }
88ad8b1aafSjsg 
shmem_unpin_map(struct file * file,void * ptr)89ad8b1aafSjsg void shmem_unpin_map(struct file *file, void *ptr)
90ad8b1aafSjsg {
91ad8b1aafSjsg 	mapping_clear_unevictable(file->f_mapping);
92ad8b1aafSjsg 	vfree(ptr);
93ad8b1aafSjsg }
94ad8b1aafSjsg 
__shmem_rw(struct file * file,loff_t off,void * ptr,size_t len,bool write)95ad8b1aafSjsg static int __shmem_rw(struct file *file, loff_t off,
96ad8b1aafSjsg 		      void *ptr, size_t len,
97ad8b1aafSjsg 		      bool write)
98ad8b1aafSjsg {
99ad8b1aafSjsg 	unsigned long pfn;
100ad8b1aafSjsg 
101ad8b1aafSjsg 	for (pfn = off >> PAGE_SHIFT; len; pfn++) {
102ad8b1aafSjsg 		unsigned int this =
103ad8b1aafSjsg 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
104ad8b1aafSjsg 		struct page *page;
105ad8b1aafSjsg 		void *vaddr;
106ad8b1aafSjsg 
107ad8b1aafSjsg 		page = shmem_read_mapping_page_gfp(file->f_mapping, pfn,
108ad8b1aafSjsg 						   GFP_KERNEL);
109ad8b1aafSjsg 		if (IS_ERR(page))
110ad8b1aafSjsg 			return PTR_ERR(page);
111ad8b1aafSjsg 
112ad8b1aafSjsg 		vaddr = kmap(page);
113ad8b1aafSjsg 		if (write) {
114ad8b1aafSjsg 			memcpy(vaddr + offset_in_page(off), ptr, this);
115ad8b1aafSjsg 			set_page_dirty(page);
116ad8b1aafSjsg 		} else {
117ad8b1aafSjsg 			memcpy(ptr, vaddr + offset_in_page(off), this);
118ad8b1aafSjsg 		}
119ad8b1aafSjsg 		mark_page_accessed(page);
120ad8b1aafSjsg 		kunmap(page);
121ad8b1aafSjsg 		put_page(page);
122ad8b1aafSjsg 
123ad8b1aafSjsg 		len -= this;
124ad8b1aafSjsg 		ptr += this;
125ad8b1aafSjsg 		off = 0;
126ad8b1aafSjsg 	}
127ad8b1aafSjsg 
128ad8b1aafSjsg 	return 0;
129ad8b1aafSjsg }
130ad8b1aafSjsg 
shmem_read_to_iosys_map(struct file * file,loff_t off,struct iosys_map * map,size_t map_off,size_t len)1311bb76ff1Sjsg int shmem_read_to_iosys_map(struct file *file, loff_t off,
1321bb76ff1Sjsg 			    struct iosys_map *map, size_t map_off, size_t len)
1331bb76ff1Sjsg {
1341bb76ff1Sjsg 	unsigned long pfn;
1351bb76ff1Sjsg 
1361bb76ff1Sjsg 	for (pfn = off >> PAGE_SHIFT; len; pfn++) {
1371bb76ff1Sjsg 		unsigned int this =
1381bb76ff1Sjsg 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
1391bb76ff1Sjsg 		struct page *page;
1401bb76ff1Sjsg 		void *vaddr;
1411bb76ff1Sjsg 
1421bb76ff1Sjsg 		page = shmem_read_mapping_page_gfp(file->f_mapping, pfn,
1431bb76ff1Sjsg 						   GFP_KERNEL);
1441bb76ff1Sjsg 		if (IS_ERR(page))
1451bb76ff1Sjsg 			return PTR_ERR(page);
1461bb76ff1Sjsg 
1471bb76ff1Sjsg 		vaddr = kmap(page);
1481bb76ff1Sjsg 		iosys_map_memcpy_to(map, map_off, vaddr + offset_in_page(off),
1491bb76ff1Sjsg 				    this);
1501bb76ff1Sjsg 		mark_page_accessed(page);
1511bb76ff1Sjsg 		kunmap(page);
1521bb76ff1Sjsg 		put_page(page);
1531bb76ff1Sjsg 
1541bb76ff1Sjsg 		len -= this;
1551bb76ff1Sjsg 		map_off += this;
1561bb76ff1Sjsg 		off = 0;
1571bb76ff1Sjsg 	}
1581bb76ff1Sjsg 
1591bb76ff1Sjsg 	return 0;
1601bb76ff1Sjsg }
1611bb76ff1Sjsg 
shmem_read(struct file * file,loff_t off,void * dst,size_t len)162ad8b1aafSjsg int shmem_read(struct file *file, loff_t off, void *dst, size_t len)
163ad8b1aafSjsg {
164ad8b1aafSjsg 	return __shmem_rw(file, off, dst, len, false);
165ad8b1aafSjsg }
166ad8b1aafSjsg 
shmem_write(struct file * file,loff_t off,void * src,size_t len)167ad8b1aafSjsg int shmem_write(struct file *file, loff_t off, void *src, size_t len)
168ad8b1aafSjsg {
169ad8b1aafSjsg 	return __shmem_rw(file, off, src, len, true);
170ad8b1aafSjsg }
171ad8b1aafSjsg 
172ad8b1aafSjsg #endif /* __linux__ */
173ad8b1aafSjsg 
174ad8b1aafSjsg struct uvm_object *
uao_create_from_data(void * data,size_t len)175ad8b1aafSjsg uao_create_from_data(void *data, size_t len)
176ad8b1aafSjsg {
177ad8b1aafSjsg 	struct uvm_object *uao;
178ad8b1aafSjsg 	int err;
179ad8b1aafSjsg 
180ad8b1aafSjsg 	uao = uao_create(PAGE_ALIGN(len), 0);
181ad8b1aafSjsg 	if (uao == NULL) {
182ad8b1aafSjsg 		return ERR_PTR(-ENOMEM);
183ad8b1aafSjsg 	}
184ad8b1aafSjsg 
185ad8b1aafSjsg 	err = uao_write(uao, 0, data, len);
186ad8b1aafSjsg 	if (err) {
187ad8b1aafSjsg 		uao_detach(uao);
188ad8b1aafSjsg 		return ERR_PTR(err);
189ad8b1aafSjsg 	}
190ad8b1aafSjsg 
191ad8b1aafSjsg 	return uao;
192ad8b1aafSjsg }
193ad8b1aafSjsg 
194ad8b1aafSjsg struct uvm_object *
uao_create_from_object(struct drm_i915_gem_object * obj)195ad8b1aafSjsg uao_create_from_object(struct drm_i915_gem_object *obj)
196ad8b1aafSjsg {
197ad8b1aafSjsg 	struct uvm_object *uao;
198ad8b1aafSjsg 	void *ptr;
199ad8b1aafSjsg 
2009abd394aSjsg 	if (i915_gem_object_is_shmem(obj)) {
201ad8b1aafSjsg 		uao_reference(obj->base.uao);
202ad8b1aafSjsg 		return obj->base.uao;
203ad8b1aafSjsg 	}
204ad8b1aafSjsg 
2059abd394aSjsg 	ptr = i915_gem_object_pin_map_unlocked(obj, i915_gem_object_is_lmem(obj) ?
2069abd394aSjsg 						I915_MAP_WC : I915_MAP_WB);
207ad8b1aafSjsg 	if (IS_ERR(ptr))
208ad8b1aafSjsg 		return ERR_CAST(ptr);
209ad8b1aafSjsg 
210ad8b1aafSjsg 	uao = uao_create_from_data(ptr, obj->base.size);
211ad8b1aafSjsg 	i915_gem_object_unpin_map(obj);
212ad8b1aafSjsg 
213ad8b1aafSjsg 	return uao;
214ad8b1aafSjsg }
215ad8b1aafSjsg 
__uao_rw(struct uvm_object * uao,loff_t off,void * ptr,size_t len,bool write)216ad8b1aafSjsg static int __uao_rw(struct uvm_object *uao, loff_t off,
217ad8b1aafSjsg 		      void *ptr, size_t len,
218ad8b1aafSjsg 		      bool write)
219ad8b1aafSjsg {
220ad8b1aafSjsg 	struct pglist plist;
221ad8b1aafSjsg 	struct vm_page *page;
222ad8b1aafSjsg 	vaddr_t pgoff = trunc_page(off);
223ad8b1aafSjsg 	size_t olen = round_page(len);
224ad8b1aafSjsg 
225ad8b1aafSjsg 	TAILQ_INIT(&plist);
226ad8b1aafSjsg 	if (uvm_obj_wire(uao, pgoff, olen, &plist))
227ad8b1aafSjsg 		return -ENOMEM;
228ad8b1aafSjsg 
229ad8b1aafSjsg 	TAILQ_FOREACH(page, &plist, pageq) {
230ad8b1aafSjsg 		unsigned int this =
231ad8b1aafSjsg 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
232ad8b1aafSjsg 		void *vaddr = kmap(page);
233ad8b1aafSjsg 
234ad8b1aafSjsg 		if (write) {
235ad8b1aafSjsg 			memcpy(vaddr + offset_in_page(off), ptr, this);
236ad8b1aafSjsg 			set_page_dirty(page);
237ad8b1aafSjsg 		} else {
238ad8b1aafSjsg 			memcpy(ptr, vaddr + offset_in_page(off), this);
239ad8b1aafSjsg 		}
240ad8b1aafSjsg 
241ad8b1aafSjsg 		kunmap_va(vaddr);
242ad8b1aafSjsg 		len -= this;
243ad8b1aafSjsg 		ptr += this;
244ad8b1aafSjsg 		off = 0;
245ad8b1aafSjsg 	}
246ad8b1aafSjsg 
247ad8b1aafSjsg 	uvm_obj_unwire(uao, pgoff, olen);
248ad8b1aafSjsg 
249ad8b1aafSjsg 	return 0;
250ad8b1aafSjsg }
251ad8b1aafSjsg 
uao_read_to_iosys_map(struct uvm_object * uao,loff_t off,struct iosys_map * map,size_t map_off,size_t len)252*0a503edeSjsg int uao_read_to_iosys_map(struct uvm_object *uao, loff_t off,
253*0a503edeSjsg 			    struct iosys_map *map, size_t map_off, size_t len)
254*0a503edeSjsg {
255*0a503edeSjsg 	struct pglist plist;
256*0a503edeSjsg 	struct vm_page *page;
257*0a503edeSjsg 	vaddr_t pgoff = trunc_page(off);
258*0a503edeSjsg 	size_t olen = round_page(len);
259*0a503edeSjsg 
260*0a503edeSjsg 	TAILQ_INIT(&plist);
261*0a503edeSjsg 	if (uvm_obj_wire(uao, pgoff, olen, &plist))
262*0a503edeSjsg 		return -ENOMEM;
263*0a503edeSjsg 
264*0a503edeSjsg 	TAILQ_FOREACH(page, &plist, pageq) {
265*0a503edeSjsg 		unsigned int this =
266*0a503edeSjsg 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
267*0a503edeSjsg 		void *vaddr;
268*0a503edeSjsg 
269*0a503edeSjsg 		vaddr = kmap(page);
270*0a503edeSjsg 		iosys_map_memcpy_to(map, map_off, vaddr + offset_in_page(off),
271*0a503edeSjsg 				    this);
272*0a503edeSjsg 		kunmap_va(vaddr);
273*0a503edeSjsg 
274*0a503edeSjsg 		len -= this;
275*0a503edeSjsg 		map_off += this;
276*0a503edeSjsg 		off = 0;
277*0a503edeSjsg 	}
278*0a503edeSjsg 
279*0a503edeSjsg 	uvm_obj_unwire(uao, pgoff, olen);
280*0a503edeSjsg 
281*0a503edeSjsg 	return 0;
282*0a503edeSjsg }
283*0a503edeSjsg 
uao_read(struct uvm_object * uao,loff_t off,void * dst,size_t len)284ad8b1aafSjsg int uao_read(struct uvm_object *uao, loff_t off, void *dst, size_t len)
285ad8b1aafSjsg {
286ad8b1aafSjsg 	return __uao_rw(uao, off, dst, len, false);
287ad8b1aafSjsg }
288ad8b1aafSjsg 
uao_write(struct uvm_object * uao,loff_t off,void * src,size_t len)289ad8b1aafSjsg int uao_write(struct uvm_object *uao, loff_t off, void *src, size_t len)
290ad8b1aafSjsg {
291ad8b1aafSjsg 	return __uao_rw(uao, off, src, len, true);
292ad8b1aafSjsg }
293ad8b1aafSjsg 
294ad8b1aafSjsg #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
295ad8b1aafSjsg #include "st_shmem_utils.c"
296ad8b1aafSjsg #endif
297