1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2020 Intel Corporation
4 */
5
6 #include <linux/mm.h>
7 #include <linux/pagemap.h>
8 #include <linux/shmem_fs.h>
9
10 #include "gem/i915_gem_object.h"
11 #include "gem/i915_gem_lmem.h"
12 #include "shmem_utils.h"
13
shmem_create_from_data(const char * name,void * data,size_t len)14 struct file *shmem_create_from_data(const char *name, void *data, size_t len)
15 {
16 struct file *file;
17 int err;
18
19 file = shmem_file_setup(name, PAGE_ALIGN(len), VM_NORESERVE);
20 if (IS_ERR(file))
21 return file;
22
23 err = shmem_write(file, 0, data, len);
24 if (err) {
25 fput(file);
26 return ERR_PTR(err);
27 }
28
29 return file;
30 }
31
shmem_create_from_object(struct drm_i915_gem_object * obj)32 struct file *shmem_create_from_object(struct drm_i915_gem_object *obj)
33 {
34 struct file *file;
35 void *ptr;
36
37 if (i915_gem_object_is_shmem(obj)) {
38 file = obj->base.filp;
39 atomic_long_inc(&file->f_count);
40 return file;
41 }
42
43 ptr = i915_gem_object_pin_map_unlocked(obj, i915_gem_object_is_lmem(obj) ?
44 I915_MAP_WC : I915_MAP_WB);
45 if (IS_ERR(ptr))
46 return ERR_CAST(ptr);
47
48 file = shmem_create_from_data("", ptr, obj->base.size);
49 i915_gem_object_unpin_map(obj);
50
51 return file;
52 }
53
shmem_pin_map(struct file * file)54 void *shmem_pin_map(struct file *file)
55 {
56 struct page **pages;
57 size_t n_pages, i;
58 void *vaddr;
59
60 n_pages = file->f_mapping->host->i_size >> PAGE_SHIFT;
61 pages = kvmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
62 if (!pages)
63 return NULL;
64
65 for (i = 0; i < n_pages; i++) {
66 pages[i] = shmem_read_mapping_page_gfp(file->f_mapping, i,
67 GFP_KERNEL);
68 if (IS_ERR(pages[i]))
69 goto err_page;
70 }
71
72 vaddr = vmap(pages, n_pages, VM_MAP_PUT_PAGES, PAGE_KERNEL);
73 if (!vaddr)
74 goto err_page;
75 mapping_set_unevictable(file->f_mapping);
76 return vaddr;
77 err_page:
78 while (i--)
79 put_page(pages[i]);
80 kvfree(pages);
81 return NULL;
82 }
83
shmem_unpin_map(struct file * file,void * ptr)84 void shmem_unpin_map(struct file *file, void *ptr)
85 {
86 mapping_clear_unevictable(file->f_mapping);
87 vfree(ptr);
88 }
89
__shmem_rw(struct file * file,loff_t off,void * ptr,size_t len,bool write)90 static int __shmem_rw(struct file *file, loff_t off,
91 void *ptr, size_t len,
92 bool write)
93 {
94 unsigned long pfn;
95
96 for (pfn = off >> PAGE_SHIFT; len; pfn++) {
97 unsigned int this =
98 min_t(size_t, PAGE_SIZE - offset_in_page(off), len);
99 struct page *page;
100 void *vaddr;
101
102 page = shmem_read_mapping_page_gfp(file->f_mapping, pfn,
103 GFP_KERNEL);
104 if (IS_ERR(page))
105 return PTR_ERR(page);
106
107 vaddr = kmap(page);
108 if (write) {
109 memcpy(vaddr + offset_in_page(off), ptr, this);
110 set_page_dirty(page);
111 } else {
112 memcpy(ptr, vaddr + offset_in_page(off), this);
113 }
114 mark_page_accessed(page);
115 kunmap(page);
116 put_page(page);
117
118 len -= this;
119 ptr += this;
120 off = 0;
121 }
122
123 return 0;
124 }
125
shmem_read(struct file * file,loff_t off,void * dst,size_t len)126 int shmem_read(struct file *file, loff_t off, void *dst, size_t len)
127 {
128 return __shmem_rw(file, off, dst, len, false);
129 }
130
shmem_write(struct file * file,loff_t off,void * src,size_t len)131 int shmem_write(struct file *file, loff_t off, void *src, size_t len)
132 {
133 return __shmem_rw(file, off, src, len, true);
134 }
135
136 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
137 #include "st_shmem_utils.c"
138 #endif
139