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