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