Home | History | Annotate | Line # | Download | only in vgem
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