1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include <linux/dma-buf.h>
4 #include <linux/shmem_fs.h>
5 #include <linux/vmalloc.h>
6 #include <drm/drm_prime.h>
7 
8 #include "vkms_drv.h"
9 
__vkms_gem_create(struct drm_device * dev,u64 size)10 static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev,
11 						 u64 size)
12 {
13 	struct vkms_gem_object *obj;
14 	int ret;
15 
16 	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
17 	if (!obj)
18 		return ERR_PTR(-ENOMEM);
19 
20 	size = roundup(size, PAGE_SIZE);
21 	ret = drm_gem_object_init(dev, &obj->gem, size);
22 	if (ret) {
23 		kfree(obj);
24 		return ERR_PTR(ret);
25 	}
26 
27 	mutex_init(&obj->pages_lock);
28 
29 	return obj;
30 }
31 
vkms_gem_free_object(struct drm_gem_object * obj)32 void vkms_gem_free_object(struct drm_gem_object *obj)
33 {
34 	struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object,
35 						   gem);
36 
37 	WARN_ON(gem->pages);
38 	WARN_ON(gem->vaddr);
39 
40 	mutex_destroy(&gem->pages_lock);
41 	drm_gem_object_release(obj);
42 	kfree(gem);
43 }
44 
vkms_gem_fault(struct vm_fault * vmf)45 vm_fault_t vkms_gem_fault(struct vm_fault *vmf)
46 {
47 	struct vm_area_struct *vma = vmf->vma;
48 	struct vkms_gem_object *obj = vma->vm_private_data;
49 	unsigned long vaddr = vmf->address;
50 	pgoff_t page_offset;
51 	loff_t num_pages;
52 	vm_fault_t ret = VM_FAULT_SIGBUS;
53 
54 	page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
55 	num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE);
56 
57 	if (page_offset > num_pages)
58 		return VM_FAULT_SIGBUS;
59 
60 	mutex_lock(&obj->pages_lock);
61 	if (obj->pages) {
62 		get_page(obj->pages[page_offset]);
63 		vmf->page = obj->pages[page_offset];
64 		ret = 0;
65 	}
66 	mutex_unlock(&obj->pages_lock);
67 	if (ret) {
68 		struct page *page;
69 		struct address_space *mapping;
70 
71 		mapping = file_inode(obj->gem.filp)->i_mapping;
72 		page = shmem_read_mapping_page(mapping, page_offset);
73 
74 		if (!IS_ERR(page)) {
75 			vmf->page = page;
76 			ret = 0;
77 		} else {
78 			switch (PTR_ERR(page)) {
79 			case -ENOSPC:
80 			case -ENOMEM:
81 				ret = VM_FAULT_OOM;
82 				break;
83 			case -EBUSY:
84 				ret = VM_FAULT_RETRY;
85 				break;
86 			case -EFAULT:
87 			case -EINVAL:
88 				ret = VM_FAULT_SIGBUS;
89 				break;
90 			default:
91 				WARN_ON(PTR_ERR(page));
92 				ret = VM_FAULT_SIGBUS;
93 				break;
94 			}
95 		}
96 	}
97 	return ret;
98 }
99 
vkms_gem_create(struct drm_device * dev,struct drm_file * file,u32 * handle,u64 size)100 static struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
101 					      struct drm_file *file,
102 					      u32 *handle,
103 					      u64 size)
104 {
105 	struct vkms_gem_object *obj;
106 	int ret;
107 
108 	if (!file || !dev || !handle)
109 		return ERR_PTR(-EINVAL);
110 
111 	obj = __vkms_gem_create(dev, size);
112 	if (IS_ERR(obj))
113 		return ERR_CAST(obj);
114 
115 	ret = drm_gem_handle_create(file, &obj->gem, handle);
116 	if (ret)
117 		return ERR_PTR(ret);
118 
119 	return &obj->gem;
120 }
121 
vkms_dumb_create(struct drm_file * file,struct drm_device * dev,struct drm_mode_create_dumb * args)122 int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
123 		     struct drm_mode_create_dumb *args)
124 {
125 	struct drm_gem_object *gem_obj;
126 	u64 pitch, size;
127 
128 	if (!args || !dev || !file)
129 		return -EINVAL;
130 
131 	pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
132 	size = pitch * args->height;
133 
134 	if (!size)
135 		return -EINVAL;
136 
137 	gem_obj = vkms_gem_create(dev, file, &args->handle, size);
138 	if (IS_ERR(gem_obj))
139 		return PTR_ERR(gem_obj);
140 
141 	args->size = gem_obj->size;
142 	args->pitch = pitch;
143 
144 	drm_gem_object_put(gem_obj);
145 
146 	DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
147 
148 	return 0;
149 }
150 
_get_pages(struct vkms_gem_object * vkms_obj)151 static struct page **_get_pages(struct vkms_gem_object *vkms_obj)
152 {
153 	struct drm_gem_object *gem_obj = &vkms_obj->gem;
154 
155 	if (!vkms_obj->pages) {
156 		struct page **pages = drm_gem_get_pages(gem_obj);
157 
158 		if (IS_ERR(pages))
159 			return pages;
160 
161 		if (cmpxchg(&vkms_obj->pages, NULL, pages))
162 			drm_gem_put_pages(gem_obj, pages, false, true);
163 	}
164 
165 	return vkms_obj->pages;
166 }
167 
vkms_gem_vunmap(struct drm_gem_object * obj)168 void vkms_gem_vunmap(struct drm_gem_object *obj)
169 {
170 	struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj);
171 
172 	mutex_lock(&vkms_obj->pages_lock);
173 	if (vkms_obj->vmap_count < 1) {
174 		WARN_ON(vkms_obj->vaddr);
175 		WARN_ON(vkms_obj->pages);
176 		mutex_unlock(&vkms_obj->pages_lock);
177 		return;
178 	}
179 
180 	vkms_obj->vmap_count--;
181 
182 	if (vkms_obj->vmap_count == 0) {
183 		vunmap(vkms_obj->vaddr);
184 		vkms_obj->vaddr = NULL;
185 		drm_gem_put_pages(obj, vkms_obj->pages, false, true);
186 		vkms_obj->pages = NULL;
187 	}
188 
189 	mutex_unlock(&vkms_obj->pages_lock);
190 }
191 
vkms_gem_vmap(struct drm_gem_object * obj)192 int vkms_gem_vmap(struct drm_gem_object *obj)
193 {
194 	struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj);
195 	int ret = 0;
196 
197 	mutex_lock(&vkms_obj->pages_lock);
198 
199 	if (!vkms_obj->vaddr) {
200 		unsigned int n_pages = obj->size >> PAGE_SHIFT;
201 		struct page **pages = _get_pages(vkms_obj);
202 
203 		if (IS_ERR(pages)) {
204 			ret = PTR_ERR(pages);
205 			goto out;
206 		}
207 
208 		vkms_obj->vaddr = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL);
209 		if (!vkms_obj->vaddr)
210 			goto err_vmap;
211 	}
212 
213 	vkms_obj->vmap_count++;
214 	goto out;
215 
216 err_vmap:
217 	ret = -ENOMEM;
218 	drm_gem_put_pages(obj, vkms_obj->pages, false, true);
219 	vkms_obj->pages = NULL;
220 out:
221 	mutex_unlock(&vkms_obj->pages_lock);
222 	return ret;
223 }
224 
225 struct drm_gem_object *
vkms_prime_import_sg_table(struct drm_device * dev,struct dma_buf_attachment * attach,struct sg_table * sg)226 vkms_prime_import_sg_table(struct drm_device *dev,
227 			   struct dma_buf_attachment *attach,
228 			   struct sg_table *sg)
229 {
230 	struct vkms_gem_object *obj;
231 	int npages;
232 
233 	obj = __vkms_gem_create(dev, attach->dmabuf->size);
234 	if (IS_ERR(obj))
235 		return ERR_CAST(obj);
236 
237 	npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE;
238 	DRM_DEBUG_PRIME("Importing %d pages\n", npages);
239 
240 	obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
241 	if (!obj->pages) {
242 		vkms_gem_free_object(&obj->gem);
243 		return ERR_PTR(-ENOMEM);
244 	}
245 
246 	drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages);
247 	return &obj->gem;
248 }
249