diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2013-06-07 20:46:16 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2013-06-07 20:46:16 +0000 |
commit | b5eb995ea27dc816484505244c7aef178f6ac040 (patch) | |
tree | cf5013ccd0dbe0ee67adab36592b1573ac2891e4 /sys | |
parent | 4d536be368ceb6ab24e88330ed0aecc489be7945 (diff) |
Add proper mmap(2) support for drm(4)/inteldrm(4). This changes the
DRM_I915_GEM_MMAP and DRM_I915_GEM_MMAP_GTT ioctls to be compatible with
Linux. This also is the first step that moves us away from accessing all
graphics memory through the GTT, which should make things faster.
ok tedu@ (for the uvm bits)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/drm/drm.h | 3 | ||||
-rw-r--r-- | sys/dev/pci/drm/drmP.h | 7 | ||||
-rw-r--r-- | sys/dev/pci/drm/drm_drv.c | 117 | ||||
-rw-r--r-- | sys/dev/pci/drm/i915/i915_gem.c | 138 | ||||
-rw-r--r-- | sys/uvm/uvm_device.c | 13 | ||||
-rw-r--r-- | sys/uvm/uvm_device.h | 3 |
6 files changed, 226 insertions, 55 deletions
diff --git a/sys/dev/pci/drm/drm.h b/sys/dev/pci/drm/drm.h index ab80f903c73..d4cfaf0d7a8 100644 --- a/sys/dev/pci/drm/drm.h +++ b/sys/dev/pci/drm/drm.h @@ -1,4 +1,4 @@ -/* $OpenBSD: drm.h,v 1.10 2013/06/01 11:21:36 matthieu Exp $ */ +/* $OpenBSD: drm.h,v 1.11 2013/06/07 20:46:14 kettenis Exp $ */ /** * \file drm.h * Header for the Direct Rendering Manager @@ -175,6 +175,7 @@ enum drm_map_type { _DRM_AGP = 3, /**< AGP/GART */ _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ _DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */ + _DRM_GEM = 6, /**< GEM object */ }; /** diff --git a/sys/dev/pci/drm/drmP.h b/sys/dev/pci/drm/drmP.h index 9d89a785846..c387c27513a 100644 --- a/sys/dev/pci/drm/drmP.h +++ b/sys/dev/pci/drm/drmP.h @@ -1,4 +1,4 @@ -/* $OpenBSD: drmP.h,v 1.136 2013/03/28 23:47:37 jsg Exp $ */ +/* $OpenBSD: drmP.h,v 1.137 2013/06/07 20:46:14 kettenis Exp $ */ /* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com */ @@ -497,6 +497,7 @@ struct drm_obj { SPLAY_ENTRY(drm_obj) entry; struct drm_device *dev; struct uvm_object *uao; + struct drm_local_map *map; size_t size; int name; @@ -924,6 +925,10 @@ void drm_unhold_object(struct drm_obj *); int drm_try_hold_object(struct drm_obj *); void drm_unhold_and_unref(struct drm_obj *); int drm_handle_create(struct drm_file *, struct drm_obj *, int *); + +void drm_gem_free_mmap_offset(struct drm_obj *obj); +int drm_gem_create_mmap_offset(struct drm_obj *obj); + struct drm_obj *drm_gem_object_lookup(struct drm_device *, struct drm_file *, int ); int drm_gem_close_ioctl(struct drm_device *, void *, struct drm_file *); diff --git a/sys/dev/pci/drm/drm_drv.c b/sys/dev/pci/drm/drm_drv.c index 754637d8691..9a20f2103a4 100644 --- a/sys/dev/pci/drm/drm_drv.c +++ b/sys/dev/pci/drm/drm_drv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_drv.c,v 1.105 2013/04/22 08:31:46 mpi Exp $ */ +/* $OpenBSD: drm_drv.c,v 1.106 2013/06/07 20:46:15 kettenis Exp $ */ /*- * Copyright 2007-2009 Owain G. Ainsworth <oga@openbsd.org> * Copyright © 2008 Intel Corporation @@ -44,6 +44,7 @@ #include <sys/limits.h> #include <sys/systm.h> #include <uvm/uvm_extern.h> +#include <uvm/uvm_device.h> #include <sys/ttycom.h> /* for TIOCSGRP */ @@ -1737,6 +1738,120 @@ free: return (ret); } +/** + * drm_gem_free_mmap_offset - release a fake mmap offset for an object + * @obj: obj in question + * + * This routine frees fake offsets allocated by drm_gem_create_mmap_offset(). + */ +void +drm_gem_free_mmap_offset(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_local_map *map = obj->map; + + TAILQ_REMOVE(&dev->maplist, map, link); + obj->map = NULL; + + /* NOCOALESCE set, can't fail */ + extent_free(dev->handle_ext, map->ext, map->size, EX_NOWAIT); + + drm_free(map); +} + +/** + * drm_gem_create_mmap_offset - create a fake mmap offset for an object + * @obj: obj in question + * + * GEM memory mapping works by handing back to userspace a fake mmap offset + * it can use in a subsequent mmap(2) call. The DRM core code then looks + * up the object based on the offset and sets up the various memory mapping + * structures. + * + * This routine allocates and attaches a fake offset for @obj. + */ +int +drm_gem_create_mmap_offset(struct drm_obj *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_local_map *map; + int ret; + + /* Set the object up for mmap'ing */ + map = drm_calloc(1, sizeof(*map)); + if (map == NULL) + return -ENOMEM; + + map->flags = _DRM_DRIVER; + map->type = _DRM_GEM; + map->size = obj->size; + map->handle = obj; + + /* Get a DRM GEM mmap offset allocated... */ + ret = extent_alloc(dev->handle_ext, map->size, PAGE_SIZE, 0, + 0, EX_NOWAIT, &map->ext); + if (ret) { + DRM_ERROR("failed to allocate offset for bo %d\n", obj->name); + ret = -ENOSPC; + goto out_free_list; + } + + TAILQ_INSERT_TAIL(&dev->maplist, map, link); + obj->map = map; + return 0; + +out_free_list: + drm_free(map); + + return ret; +} + +struct uvm_object * +udv_attach_drm(void *arg, vm_prot_t accessprot, voff_t off, vsize_t size) +{ + dev_t device = *((dev_t *)arg); + struct drm_device *dev = drm_get_device_from_kdev(kdev); + struct drm_local_map *map; + struct drm_obj *obj; + + if (cdevsw[major(device)].d_mmap != drmmmap) + return NULL; + + if (dev == NULL) + return NULL; + +again: + DRM_LOCK(); + TAILQ_FOREACH(map, &dev->maplist, link) { + if (off >= map->ext && off + size <= map->ext + map->size) + break; + } + + if (map == NULL || map->type != _DRM_GEM) { + DRM_UNLOCK(); + return NULL; + } + + obj = (struct drm_obj *)map->handle; + simple_lock(&uobj->vmobjlock); + if (obj->do_flags & DRM_BUSY) { + atomic_setbits_int(&obj->do_flags, DRM_WANTED); + simple_unlock(&uobj->vmobjlock); + DRM_UNLOCK(); + tsleep(obj, PVM, "udv_drm", 0); /* XXX msleep */ + goto again; + } +#ifdef DRMLOCKDEBUG + obj->holding_proc = curproc; +#endif + atomic_setbits_int(&obj->do_flags, DRM_BUSY); + simple_unlock(&obj->vmobjlock); + drm_ref(&obj->uobj); + drm_unhold_object(obj); + DRM_UNLOCK(); + return &obj->uobj; +} + int drm_handle_cmp(struct drm_handle *a, struct drm_handle *b) { diff --git a/sys/dev/pci/drm/i915/i915_gem.c b/sys/dev/pci/drm/i915/i915_gem.c index 42e87fc4c7d..2500793d2e2 100644 --- a/sys/dev/pci/drm/i915/i915_gem.c +++ b/sys/dev/pci/drm/i915/i915_gem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i915_gem.c,v 1.22 2013/05/27 19:29:25 kettenis Exp $ */ +/* $OpenBSD: i915_gem.c,v 1.23 2013/06/07 20:46:15 kettenis Exp $ */ /* * Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org> * @@ -82,6 +82,8 @@ int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, bool nonblocking); int i915_gem_wait_for_error(struct drm_device *); int __wait_seqno(struct intel_ring_buffer *, uint32_t, bool, struct timespec *); +int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *); +void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *); extern int ticks; @@ -624,13 +626,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, if (write_domain != 0 && read_domains != write_domain) return EINVAL; - /* - * Only allow GTT since that is all that we let userland near - * on OpenBSD. - */ - if ((write_domain | read_domains) & ~I915_GEM_DOMAIN_GTT) - return EINVAL; - ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -747,9 +742,11 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, * must free it in the case that the map fails. */ addr = 0; - ret = uvm_map(&curproc->p_vmspace->vm_map, &addr, nsize, &obj->uobj, + ret = uvm_map(&curproc->p_vmspace->vm_map, &addr, nsize, obj->uao, offset, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_SHARE, UVM_ADV_RANDOM, 0)); + if (ret == 0) + uao_reference(obj->uao); done: if (ret == 0) @@ -776,6 +773,9 @@ i915_gem_fault(struct drm_obj *gem_obj, struct uvm_faultinfo *ufi, dev_priv->entries++; + KASSERT(obj->base.map); + offset -= obj->base.map->ext; + if (rw_enter(&dev->dev_lock, RW_NOSLEEP | RW_READ) != 0) { uvmfault_unlockall(ufi, NULL, &obj->base.uobj, NULL); DRM_READLOCK(); @@ -980,62 +980,98 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } -// i915_gem_object_create_mmap_offset -// i915_gem_object_free_mmap_offset - int -i915_gem_mmap_gtt(struct drm_file *file, struct drm_device *dev, - uint32_t handle, uint64_t *mmap_offset) +i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { - struct drm_i915_gem_object *obj; - struct drm_local_map *map; - voff_t offset; - vsize_t end, nsize; - int ret; +#if 0 + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; +#endif + int ret; - offset = (voff_t)*mmap_offset; + if (obj->base.map) + return 0; - obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); - if (obj == NULL) - return ENOENT; +#if 0 + dev_priv->mm.shrinker_no_lock_stealing = true; +#endif - /* Since we are doing purely uvm-related operations here we do - * not need to hold the object, a reference alone is sufficient + ret = drm_gem_create_mmap_offset(&obj->base); +#if 0 + if (ret != -ENOSPC) + goto out; + + /* Badly fragmented mmap space? The only way we can recover + * space is by destroying unwanted objects. We can't randomly release + * mmap_offsets as userspace expects them to be persistent for the + * lifetime of the objects. The closest we can is to release the + * offsets on purgeable objects by truncating it and marking it purged, + * which prevents userspace from ever using that object again. */ + i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT); + ret = drm_gem_create_mmap_offset(&obj->base); + if (ret != -ENOSPC) + goto out; - /* Check size. */ - if (offset > obj->base.size) { - ret = EINVAL; - goto done; + i915_gem_shrink_all(dev_priv); + ret = drm_gem_create_mmap_offset(&obj->base); +out: + dev_priv->mm.shrinker_no_lock_stealing = false; +#endif + + return ret; +} + +void +i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) +{ + if (!obj->base.map) + return; + + drm_gem_free_mmap_offset(&obj->base); +} + +int +i915_gem_mmap_gtt(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle)); + if (&obj->base == NULL) { + ret = -ENOENT; + goto unlock; } - if (obj->madv != I915_MADV_WILLNEED) { - DRM_ERROR("Attempting to mmap a purgeable buffer\n"); - ret = EINVAL; - goto done; + if (obj->base.size > dev_priv->mm.gtt_mappable_end) { + ret = -E2BIG; + goto out; } - ret = i915_gem_object_bind_to_gtt(obj, 0, true, false); - if (ret) { - printf("%s: failed to bind\n", __func__); - goto done; + if (obj->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to mmap a purgeable buffer\n"); + ret = -EINVAL; + goto out; } - i915_gem_object_move_to_inactive(obj); - end = round_page(offset + obj->base.size); - offset = trunc_page(offset); - nsize = end - offset; + ret = i915_gem_object_create_mmap_offset(obj); + if (ret) + goto out; - ret = drm_addmap(dev, offset + obj->gtt_offset, nsize, _DRM_AGP, - _DRM_WRITE_COMBINING, &map); - -done: - if (ret == 0) - *mmap_offset = map->ext; - else - drm_unref(&obj->base.uobj); + *offset = (u64)obj->base.map->ext; - return (ret); +out: + drm_gem_object_unreference(&obj->base); +unlock: + DRM_UNLOCK(); + return ret; } /** @@ -1068,6 +1104,8 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) { DRM_ASSERT_HELD(&obj->base); + i915_gem_object_free_mmap_offset(obj); + simple_lock(&obj->base.uao->vmobjlock); obj->base.uao->pgops->pgo_flush(obj->base.uao, 0, obj->base.size, PGO_ALLPAGES | PGO_FREE); diff --git a/sys/uvm/uvm_device.c b/sys/uvm/uvm_device.c index 9d9a2cdac38..316d932d2cf 100644 --- a/sys/uvm/uvm_device.c +++ b/sys/uvm/uvm_device.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_device.c,v 1.42 2013/05/30 16:29:46 tedu Exp $ */ +/* $OpenBSD: uvm_device.c,v 1.43 2013/06/07 20:46:14 kettenis Exp $ */ /* $NetBSD: uvm_device.c,v 1.30 2000/11/25 06:27:59 chs Exp $ */ /* @@ -50,6 +50,8 @@ #include <uvm/uvm.h> #include <uvm/uvm_device.h> +#include "drm.h" + /* * private global data structure * @@ -104,6 +106,9 @@ udv_attach(void *arg, vm_prot_t accessprot, voff_t off, vsize_t size) dev_t device = *((dev_t *)arg); struct uvm_device *udv, *lcv; paddr_t (*mapfn)(dev_t, off_t, int); +#if NDRM > 0 + struct uvm_object *obj; +#endif /* * before we do anything, ensure this device supports mmap @@ -122,6 +127,12 @@ udv_attach(void *arg, vm_prot_t accessprot, voff_t off, vsize_t size) if (off < 0) return(NULL); +#if NDRM > 0 + obj = udv_attach_drm(arg, accessprot, off, size); + if (obj) + return(obj); +#endif + /* * Check that the specified range of the device allows the * desired protection. diff --git a/sys/uvm/uvm_device.h b/sys/uvm/uvm_device.h index e31455e8102..03bc37df44f 100644 --- a/sys/uvm/uvm_device.h +++ b/sys/uvm/uvm_device.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_device.h,v 1.8 2002/03/14 01:27:18 millert Exp $ */ +/* $OpenBSD: uvm_device.h,v 1.9 2013/06/07 20:46:14 kettenis Exp $ */ /* $NetBSD: uvm_device.h,v 1.9 2000/05/28 10:21:55 drochner Exp $ */ /* @@ -71,6 +71,7 @@ struct uvm_device { */ struct uvm_object *udv_attach(void *, vm_prot_t, voff_t, vsize_t); +struct uvm_object *udv_attach_drm(void *, vm_prot_t, voff_t, vsize_t); #endif /* _KERNEL */ |