xref: /linux/drivers/gpu/drm/i915/gt/shmem_utils.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2020 Intel Corporation
4  */
5 
6 #include <linux/iosys-map.h>
7 #include <linux/mm.h>
8 #include <linux/pagemap.h>
9 #include <linux/shmem_fs.h>
10 #include <linux/vmalloc.h>
11 
12 #include "i915_drv.h"
13 #include "gem/i915_gem_object.h"
14 #include "gem/i915_gem_lmem.h"
15 #include "shmem_utils.h"
16 
17 struct file *shmem_create_from_data(const char *name, void *data, size_t len)
18 {
19 	struct file *file;
20 	int err;
21 
22 	file = shmem_file_setup(name, PAGE_ALIGN(len),
23 				mk_vma_flags(VMA_NORESERVE_BIT));
24 	if (IS_ERR(file))
25 		return file;
26 
27 	err = shmem_write(file, 0, data, len);
28 	if (err) {
29 		fput(file);
30 		return ERR_PTR(err);
31 	}
32 
33 	return file;
34 }
35 
36 struct file *shmem_create_from_object(struct drm_i915_gem_object *obj)
37 {
38 	enum i915_map_type map_type;
39 	struct file *file;
40 	void *ptr;
41 
42 	if (i915_gem_object_is_shmem(obj)) {
43 		file = obj->base.filp;
44 		get_file(file);
45 		return file;
46 	}
47 
48 	map_type = i915_gem_object_is_lmem(obj) ? I915_MAP_WC : I915_MAP_WB;
49 	ptr = i915_gem_object_pin_map_unlocked(obj, map_type);
50 	if (IS_ERR(ptr))
51 		return ERR_CAST(ptr);
52 
53 	file = shmem_create_from_data("", ptr, obj->base.size);
54 	i915_gem_object_unpin_map(obj);
55 
56 	return file;
57 }
58 
59 void *shmem_pin_map(struct file *file)
60 {
61 	struct page **pages;
62 	size_t n_pages, i;
63 	void *vaddr;
64 
65 	n_pages = file->f_mapping->host->i_size >> PAGE_SHIFT;
66 	pages = kvmalloc_objs(*pages, n_pages);
67 	if (!pages)
68 		return NULL;
69 
70 	for (i = 0; i < n_pages; i++) {
71 		pages[i] = shmem_read_mapping_page_gfp(file->f_mapping, i,
72 						       GFP_KERNEL);
73 		if (IS_ERR(pages[i]))
74 			goto err_page;
75 	}
76 
77 	vaddr = vmap(pages, n_pages, VM_MAP_PUT_PAGES, PAGE_KERNEL);
78 	if (!vaddr)
79 		goto err_page;
80 	mapping_set_unevictable(file->f_mapping);
81 	return vaddr;
82 err_page:
83 	while (i--)
84 		put_page(pages[i]);
85 	kvfree(pages);
86 	return NULL;
87 }
88 
89 void shmem_unpin_map(struct file *file, void *ptr)
90 {
91 	mapping_clear_unevictable(file->f_mapping);
92 	vfree(ptr);
93 }
94 
95 static int __shmem_rw(struct file *file, loff_t off,
96 		      void *ptr, size_t len,
97 		      bool write)
98 {
99 	unsigned long pfn;
100 
101 	for (pfn = off >> PAGE_SHIFT; len; pfn++) {
102 		unsigned int this =
103 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
104 		struct page *page;
105 		void *vaddr;
106 
107 		page = shmem_read_mapping_page_gfp(file->f_mapping, pfn,
108 						   GFP_KERNEL);
109 		if (IS_ERR(page))
110 			return PTR_ERR(page);
111 
112 		vaddr = kmap_local_page(page);
113 		if (write) {
114 			memcpy(vaddr + offset_in_page(off), ptr, this);
115 			set_page_dirty(page);
116 		} else {
117 			memcpy(ptr, vaddr + offset_in_page(off), this);
118 		}
119 		mark_page_accessed(page);
120 		kunmap_local(vaddr);
121 		put_page(page);
122 
123 		len -= this;
124 		ptr += this;
125 		off = 0;
126 	}
127 
128 	return 0;
129 }
130 
131 int shmem_read_to_iosys_map(struct file *file, loff_t off,
132 			    struct iosys_map *map, size_t map_off, size_t len)
133 {
134 	unsigned long pfn;
135 
136 	for (pfn = off >> PAGE_SHIFT; len; pfn++) {
137 		unsigned int this =
138 			min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
139 		struct page *page;
140 		void *vaddr;
141 
142 		page = shmem_read_mapping_page_gfp(file->f_mapping, pfn,
143 						   GFP_KERNEL);
144 		if (IS_ERR(page))
145 			return PTR_ERR(page);
146 
147 		vaddr = kmap_local_page(page);
148 		iosys_map_memcpy_to(map, map_off, vaddr + offset_in_page(off),
149 				    this);
150 		mark_page_accessed(page);
151 		kunmap_local(vaddr);
152 		put_page(page);
153 
154 		len -= this;
155 		map_off += this;
156 		off = 0;
157 	}
158 
159 	return 0;
160 }
161 
162 int shmem_read(struct file *file, loff_t off, void *dst, size_t len)
163 {
164 	return __shmem_rw(file, off, dst, len, false);
165 }
166 
167 int shmem_write(struct file *file, loff_t off, void *src, size_t len)
168 {
169 	return __shmem_rw(file, off, src, len, true);
170 }
171 
172 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
173 #include "st_shmem_utils.c"
174 #endif
175