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 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 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 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 84 void shmem_unpin_map(struct file *file, void *ptr) 85 { 86 mapping_clear_unevictable(file->f_mapping); 87 vfree(ptr); 88 } 89 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 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 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