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