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 struct drm_device *dev = obj->base.dev; 93 loff_t num_pages; 94 pgoff_t page_offset; 95 int ret; 96 97 /* We don't use vmf->pgoff since that has the fake offset */ 98 page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> 99 PAGE_SHIFT; 100 101 num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE); 102 103 if (page_offset > num_pages) 104 return VM_FAULT_SIGBUS; 105 106 mutex_lock(&dev->struct_mutex); 107 108 ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, 109 obj->pages[page_offset]); 110 111 mutex_unlock(&dev->struct_mutex); 112 switch (ret) { 113 case 0: 114 return VM_FAULT_NOPAGE; 115 case -ENOMEM: 116 return VM_FAULT_OOM; 117 case -EBUSY: 118 return VM_FAULT_RETRY; 119 case -EFAULT: 120 case -EINVAL: 121 return VM_FAULT_SIGBUS; 122 default: 123 WARN_ON(1); 124 return VM_FAULT_SIGBUS; 125 } 126 } 127 128 static const struct vm_operations_struct vgem_gem_vm_ops = { 129 .fault = vgem_gem_fault, 130 .open = drm_gem_vm_open, 131 .close = drm_gem_vm_close, 132 }; 133 134 /* ioctls */ 135 136 static struct drm_gem_object *vgem_gem_create(struct drm_device *dev, 137 struct drm_file *file, 138 unsigned int *handle, 139 unsigned long size) 140 { 141 struct drm_vgem_gem_object *obj; 142 struct drm_gem_object *gem_object; 143 int err; 144 145 size = roundup(size, PAGE_SIZE); 146 147 obj = kzalloc(sizeof(*obj), GFP_KERNEL); 148 if (!obj) 149 return ERR_PTR(-ENOMEM); 150 151 gem_object = &obj->base; 152 153 err = drm_gem_object_init(dev, gem_object, size); 154 if (err) 155 goto out; 156 157 err = drm_gem_handle_create(file, gem_object, handle); 158 if (err) 159 goto handle_out; 160 161 drm_gem_object_unreference_unlocked(gem_object); 162 163 return gem_object; 164 165 handle_out: 166 drm_gem_object_release(gem_object); 167 out: 168 kfree(obj); 169 return ERR_PTR(err); 170 } 171 172 static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev, 173 struct drm_mode_create_dumb *args) 174 { 175 struct drm_gem_object *gem_object; 176 uint64_t size; 177 uint64_t pitch = args->width * DIV_ROUND_UP(args->bpp, 8); 178 179 size = args->height * pitch; 180 if (size == 0) 181 return -EINVAL; 182 183 gem_object = vgem_gem_create(dev, file, &args->handle, size); 184 185 if (IS_ERR(gem_object)) { 186 DRM_DEBUG_DRIVER("object creation failed\n"); 187 return PTR_ERR(gem_object); 188 } 189 190 args->size = gem_object->size; 191 args->pitch = pitch; 192 193 DRM_DEBUG_DRIVER("Created object of size %lld\n", size); 194 195 return 0; 196 } 197 198 int vgem_gem_dumb_map(struct drm_file *file, struct drm_device *dev, 199 uint32_t handle, uint64_t *offset) 200 { 201 int ret = 0; 202 struct drm_gem_object *obj; 203 204 mutex_lock(&dev->struct_mutex); 205 obj = drm_gem_object_lookup(dev, file, handle); 206 if (!obj) { 207 ret = -ENOENT; 208 goto unlock; 209 } 210 211 if (!drm_vma_node_has_offset(&obj->vma_node)) { 212 ret = drm_gem_create_mmap_offset(obj); 213 if (ret) 214 goto unref; 215 } 216 217 BUG_ON(!obj->filp); 218 219 obj->filp->private_data = obj; 220 221 ret = vgem_gem_get_pages(to_vgem_bo(obj)); 222 if (ret) 223 goto fail_get_pages; 224 225 *offset = drm_vma_node_offset_addr(&obj->vma_node); 226 227 goto unref; 228 229 fail_get_pages: 230 drm_gem_free_mmap_offset(obj); 231 unref: 232 drm_gem_object_unreference(obj); 233 unlock: 234 mutex_unlock(&dev->struct_mutex); 235 return ret; 236 } 237 238 static struct drm_ioctl_desc vgem_ioctls[] = { 239 }; 240 241 static const struct file_operations vgem_driver_fops = { 242 .owner = THIS_MODULE, 243 .open = drm_open, 244 .mmap = drm_gem_mmap, 245 .poll = drm_poll, 246 .read = drm_read, 247 .unlocked_ioctl = drm_ioctl, 248 .release = drm_release, 249 }; 250 251 static struct drm_driver vgem_driver = { 252 .driver_features = DRIVER_GEM, 253 .gem_free_object = vgem_gem_free_object, 254 .gem_vm_ops = &vgem_gem_vm_ops, 255 .ioctls = vgem_ioctls, 256 .fops = &vgem_driver_fops, 257 .dumb_create = vgem_gem_dumb_create, 258 .dumb_map_offset = vgem_gem_dumb_map, 259 .name = DRIVER_NAME, 260 .desc = DRIVER_DESC, 261 .date = DRIVER_DATE, 262 .major = DRIVER_MAJOR, 263 .minor = DRIVER_MINOR, 264 }; 265 266 struct drm_device *vgem_device; 267 268 static int __init vgem_init(void) 269 { 270 int ret; 271 272 vgem_device = drm_dev_alloc(&vgem_driver, NULL); 273 if (!vgem_device) { 274 ret = -ENOMEM; 275 goto out; 276 } 277 278 drm_dev_set_unique(vgem_device, "vgem"); 279 280 ret = drm_dev_register(vgem_device, 0); 281 282 if (ret) 283 goto out_unref; 284 285 return 0; 286 287 out_unref: 288 drm_dev_unref(vgem_device); 289 out: 290 return ret; 291 } 292 293 static void __exit vgem_exit(void) 294 { 295 drm_dev_unregister(vgem_device); 296 drm_dev_unref(vgem_device); 297 } 298 299 module_init(vgem_init); 300 module_exit(vgem_exit); 301 302 MODULE_AUTHOR("Red Hat, Inc."); 303 MODULE_DESCRIPTION(DRIVER_DESC); 304 MODULE_LICENSE("GPL and additional rights"); 305