linux/drivers/gpu/drm/vgem/vgem_drv.c
<<
>>
Prefs
   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
  45void 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
  51static 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
  72int 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
  89static 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
 128static 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
 136static 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
 165handle_out:
 166        drm_gem_object_release(gem_object);
 167out:
 168        kfree(obj);
 169        return ERR_PTR(err);
 170}
 171
 172static 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
 198int 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
 229fail_get_pages:
 230        drm_gem_free_mmap_offset(obj);
 231unref:
 232        drm_gem_object_unreference(obj);
 233unlock:
 234        mutex_unlock(&dev->struct_mutex);
 235        return ret;
 236}
 237
 238static struct drm_ioctl_desc vgem_ioctls[] = {
 239};
 240
 241static 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
 251static 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
 266struct drm_device *vgem_device;
 267
 268static 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
 287out_unref:
 288        drm_dev_unref(vgem_device);
 289out:
 290        return ret;
 291}
 292
 293static void __exit vgem_exit(void)
 294{
 295        drm_dev_unregister(vgem_device);
 296        drm_dev_unref(vgem_device);
 297}
 298
 299module_init(vgem_init);
 300module_exit(vgem_exit);
 301
 302MODULE_AUTHOR("Red Hat, Inc.");
 303MODULE_DESCRIPTION(DRIVER_DESC);
 304MODULE_LICENSE("GPL and additional rights");
 305