vgem_drv.c revision 1.2 1 /* $NetBSD: vgem_drv.c,v 1.2 2018/08/27 04:58:37 riastradh Exp $ */
2
3 /*
4 * Copyright 2011 Red Hat, Inc.
5 * Copyright 2014 The Chromium OS Authors
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software")
9 * to deal in the software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * them Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
22 * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Adam Jackson <ajax (at) redhat.com>
27 * Ben Widawsky <ben (at) bwidawsk.net>
28 */
29
30 /**
31 * This is vgem, a (non-hardware-backed) GEM service. This is used by Mesa's
32 * software renderer and the X server for efficient buffer sharing.
33 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: vgem_drv.c,v 1.2 2018/08/27 04:58:37 riastradh Exp $");
37
38 #include <linux/module.h>
39 #include <linux/ramfs.h>
40 #include <linux/shmem_fs.h>
41 #include <linux/dma-buf.h>
42 #include "vgem_drv.h"
43
44 #define DRIVER_NAME "vgem"
45 #define DRIVER_DESC "Virtual GEM provider"
46 #define DRIVER_DATE "20120112"
47 #define DRIVER_MAJOR 1
48 #define DRIVER_MINOR 0
49
50 void vgem_gem_put_pages(struct drm_vgem_gem_object *obj)
51 {
52 drm_gem_put_pages(&obj->base, obj->pages, false, false);
53 obj->pages = NULL;
54 }
55
56 static void vgem_gem_free_object(struct drm_gem_object *obj)
57 {
58 struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
59
60 drm_gem_free_mmap_offset(obj);
61
62 if (vgem_obj->use_dma_buf && obj->dma_buf) {
63 dma_buf_put(obj->dma_buf);
64 obj->dma_buf = NULL;
65 }
66
67 drm_gem_object_release(obj);
68
69 if (vgem_obj->pages)
70 vgem_gem_put_pages(vgem_obj);
71
72 vgem_obj->pages = NULL;
73
74 kfree(vgem_obj);
75 }
76
77 int vgem_gem_get_pages(struct drm_vgem_gem_object *obj)
78 {
79 struct page **pages;
80
81 if (obj->pages || obj->use_dma_buf)
82 return 0;
83
84 pages = drm_gem_get_pages(&obj->base);
85 if (IS_ERR(pages)) {
86 return PTR_ERR(pages);
87 }
88
89 obj->pages = pages;
90
91 return 0;
92 }
93
94 static int vgem_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
95 {
96 struct drm_vgem_gem_object *obj = vma->vm_private_data;
97 struct drm_device *dev = obj->base.dev;
98 loff_t num_pages;
99 pgoff_t page_offset;
100 int ret;
101
102 /* We don't use vmf->pgoff since that has the fake offset */
103 page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
104 PAGE_SHIFT;
105
106 num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE);
107
108 if (page_offset > num_pages)
109 return VM_FAULT_SIGBUS;
110
111 mutex_lock(&dev->struct_mutex);
112
113 ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address,
114 obj->pages[page_offset]);
115
116 mutex_unlock(&dev->struct_mutex);
117 switch (ret) {
118 case 0:
119 return VM_FAULT_NOPAGE;
120 case -ENOMEM:
121 return VM_FAULT_OOM;
122 case -EBUSY:
123 return VM_FAULT_RETRY;
124 case -EFAULT:
125 case -EINVAL:
126 return VM_FAULT_SIGBUS;
127 default:
128 WARN_ON(1);
129 return VM_FAULT_SIGBUS;
130 }
131 }
132
133 static const struct vm_operations_struct vgem_gem_vm_ops = {
134 .fault = vgem_gem_fault,
135 .open = drm_gem_vm_open,
136 .close = drm_gem_vm_close,
137 };
138
139 /* ioctls */
140
141 static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
142 struct drm_file *file,
143 unsigned int *handle,
144 unsigned long size)
145 {
146 struct drm_vgem_gem_object *obj;
147 struct drm_gem_object *gem_object;
148 int err;
149
150 size = roundup(size, PAGE_SIZE);
151
152 obj = kzalloc(sizeof(*obj), GFP_KERNEL);
153 if (!obj)
154 return ERR_PTR(-ENOMEM);
155
156 gem_object = &obj->base;
157
158 err = drm_gem_object_init(dev, gem_object, size);
159 if (err)
160 goto out;
161
162 err = drm_gem_handle_create(file, gem_object, handle);
163 if (err)
164 goto handle_out;
165
166 drm_gem_object_unreference_unlocked(gem_object);
167
168 return gem_object;
169
170 handle_out:
171 drm_gem_object_release(gem_object);
172 out:
173 kfree(obj);
174 return ERR_PTR(err);
175 }
176
177 static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
178 struct drm_mode_create_dumb *args)
179 {
180 struct drm_gem_object *gem_object;
181 uint64_t size;
182 uint64_t pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
183
184 size = args->height * pitch;
185 if (size == 0)
186 return -EINVAL;
187
188 gem_object = vgem_gem_create(dev, file, &args->handle, size);
189
190 if (IS_ERR(gem_object)) {
191 DRM_DEBUG_DRIVER("object creation failed\n");
192 return PTR_ERR(gem_object);
193 }
194
195 args->size = gem_object->size;
196 args->pitch = pitch;
197
198 DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
199
200 return 0;
201 }
202
203 int vgem_gem_dumb_map(struct drm_file *file, struct drm_device *dev,
204 uint32_t handle, uint64_t *offset)
205 {
206 int ret = 0;
207 struct drm_gem_object *obj;
208
209 mutex_lock(&dev->struct_mutex);
210 obj = drm_gem_object_lookup(dev, file, handle);
211 if (!obj) {
212 ret = -ENOENT;
213 goto unlock;
214 }
215
216 if (!drm_vma_node_has_offset(&obj->vma_node)) {
217 ret = drm_gem_create_mmap_offset(obj);
218 if (ret)
219 goto unref;
220 }
221
222 BUG_ON(!obj->filp);
223
224 obj->filp->private_data = obj;
225
226 ret = vgem_gem_get_pages(to_vgem_bo(obj));
227 if (ret)
228 goto fail_get_pages;
229
230 *offset = drm_vma_node_offset_addr(&obj->vma_node);
231
232 goto unref;
233
234 fail_get_pages:
235 drm_gem_free_mmap_offset(obj);
236 unref:
237 drm_gem_object_unreference(obj);
238 unlock:
239 mutex_unlock(&dev->struct_mutex);
240 return ret;
241 }
242
243 static struct drm_ioctl_desc vgem_ioctls[] = {
244 };
245
246 static const struct file_operations vgem_driver_fops = {
247 .owner = THIS_MODULE,
248 .open = drm_open,
249 .mmap = drm_gem_mmap,
250 .poll = drm_poll,
251 .read = drm_read,
252 .unlocked_ioctl = drm_ioctl,
253 .release = drm_release,
254 };
255
256 static struct drm_driver vgem_driver = {
257 .driver_features = DRIVER_GEM,
258 .gem_free_object = vgem_gem_free_object,
259 .gem_vm_ops = &vgem_gem_vm_ops,
260 .ioctls = vgem_ioctls,
261 .fops = &vgem_driver_fops,
262 .dumb_create = vgem_gem_dumb_create,
263 .dumb_map_offset = vgem_gem_dumb_map,
264 .name = DRIVER_NAME,
265 .desc = DRIVER_DESC,
266 .date = DRIVER_DATE,
267 .major = DRIVER_MAJOR,
268 .minor = DRIVER_MINOR,
269 };
270
271 struct drm_device *vgem_device;
272
273 static int __init vgem_init(void)
274 {
275 int ret;
276
277 vgem_device = drm_dev_alloc(&vgem_driver, NULL);
278 if (!vgem_device) {
279 ret = -ENOMEM;
280 goto out;
281 }
282
283 drm_dev_set_unique(vgem_device, "vgem");
284
285 ret = drm_dev_register(vgem_device, 0);
286
287 if (ret)
288 goto out_unref;
289
290 return 0;
291
292 out_unref:
293 drm_dev_unref(vgem_device);
294 out:
295 return ret;
296 }
297
298 static void __exit vgem_exit(void)
299 {
300 drm_dev_unregister(vgem_device);
301 drm_dev_unref(vgem_device);
302 }
303
304 module_init(vgem_init);
305 module_exit(vgem_exit);
306
307 MODULE_AUTHOR("Red Hat, Inc.");
308 MODULE_DESCRIPTION(DRIVER_DESC);
309 MODULE_LICENSE("GPL and additional rights");
310