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
shmem_create_from_data(const char * name,void * data,size_t len)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
shmem_create_from_object(struct drm_i915_gem_object * obj)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
shmem_pin_map(struct file * file)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
shmem_unpin_map(struct file * file,void * ptr)89 void shmem_unpin_map(struct file *file, void *ptr)
90 {
91 mapping_clear_unevictable(file->f_mapping);
92 vfree(ptr);
93 }
94
__shmem_rw(struct file * file,loff_t off,void * ptr,size_t len,bool write)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
shmem_read_to_iosys_map(struct file * file,loff_t off,struct iosys_map * map,size_t map_off,size_t len)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
shmem_read(struct file * file,loff_t off,void * dst,size_t len)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
shmem_write(struct file * file,loff_t off,void * src,size_t len)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