commit 02fc1b68ba410845205930211d8be8b13dfaf130 Author: Dave Airlie Date: Thu Sep 11 06:18:04 2008 +1000 jbarnes: gtt mapping v2 +/- airlied hacks diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index d4e42e0..b9e2084 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -262,6 +262,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset, DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size); break; + case _DRM_GEM: + DRM_ERROR("tried to rmmap GEM object\n"); + break; } case _DRM_SCATTER_GATHER: if (!dev->sg) { diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3c9d0fd..79e0aaf 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -312,6 +312,7 @@ static void drm_cleanup(struct drm_device * dev) dev->driver->unload(dev); drm_ht_remove(&dev->map_hash); + drm_mm_takedown(&dev->offset_manager); drm_ctxbitmap_cleanup(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 434155b..c2fcf01 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -418,3 +418,23 @@ drm_gem_object_handle_free(struct kref *kref) } EXPORT_SYMBOL(drm_gem_object_handle_free); +int +drm_gem_mmap(struct vm_area_struct *vma, struct file *filp, struct drm_map *map) +{ + struct drm_gem_object *obj = map->handle; + + DRM_ERROR("drm_gem_mmap on object %d, vm_ops %p\n", obj->name, + obj->vm_ops); + + if (!obj->vm_ops) + return -EINVAL; + + vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP; + vma->vm_ops = obj->vm_ops; + vma->vm_private_data = map->handle; + vma->vm_file = obj->filp; + + DRM_DEBUG("set vm_ops for BO %d\n", obj->name); + + return 0; +} diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 3316067..af539f7 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -127,6 +127,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) } return 0; } +EXPORT_SYMBOL(drm_ht_insert_item); /* * Just insert an item and return any "bits" bit key that hasn't been @@ -188,6 +189,7 @@ int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item) ht->fill--; return 0; } +EXPORT_SYMBOL(drm_ht_remove_item); void drm_ht_remove(struct drm_open_hash *ht) { diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 1dc2965..eaa56ea 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -219,6 +219,7 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, if (drm_ht_create(&dev->map_hash, DRM_MAP_HASH_ORDER)) { return -ENOMEM; } + if (drm_mm_init(&dev->offset_manager, DRM_FILE_PAGE_OFFSET_START, DRM_FILE_PAGE_OFFSET_SIZE)) { drm_ht_remove(&dev->map_hash); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 18f1d54..48d65fa 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -284,6 +284,9 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) case _DRM_TTM: BUG_ON(1); break; + case _DRM_GEM: + DRM_ERROR("tried to rmmap GEM object\n"); + break; } drm_free(map, sizeof(*map), DRM_MEM_MAPS); } @@ -567,6 +570,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) struct drm_map *map = NULL; unsigned long offset = 0; struct drm_hash_item *hash; + int ret; DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n", vma->vm_start, vma->vm_end, vma->vm_pgoff); @@ -586,6 +590,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) ) return drm_mmap_dma(filp, vma); + DRM_ERROR("looking for object at 0x%08lx\n", vma->vm_pgoff); if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff, &hash)) { DRM_ERROR("Could not find map\n"); return -EINVAL; @@ -669,6 +674,12 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) break; case _DRM_TTM: return drm_bo_mmap_locked(vma, filp, map); + case _DRM_GEM: + DRM_ERROR("found GEM object, setting up vm_ops\n"); + ret = drm_gem_mmap(vma, filp, map); + if (ret) + return ret; + break; default: return -EINVAL; /* This should never happen. */ } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 45af573..07b02d0 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -831,6 +831,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0), + DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0), DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0), diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 275d6e1..cadd7df 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -384,6 +384,8 @@ struct drm_i915_gem_object { * This is the same as gtt_space->start */ uint32_t gtt_offset; + uint32_t gtt_alignment; + uint64_t mmap_offset; /** Boolean whether this object has a valid gtt offset. */ int gtt_bound; @@ -523,6 +525,8 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ee4a411..02e172b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -30,6 +30,7 @@ #include "i915_drm.h" #include "i915_drv.h" #include +#include static int @@ -46,6 +47,8 @@ i915_gem_set_domain(struct drm_gem_object *obj, static int i915_gem_object_get_page_list(struct drm_gem_object *obj); static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); +static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, + unsigned alignment); int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end) @@ -471,6 +474,103 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, return 0; } +static void i915_gem_vm_open(struct vm_area_struct *vma) +{ +} + +static void i915_gem_vm_close(struct vm_area_struct *vma) +{ +} + +static int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + unsigned long page_offset; + unsigned long pfn; + int ret = 0; + + page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> + PAGE_SHIFT; + + DRM_ERROR("faulting in object %d offset 0x%08lx\n", obj->name, + page_offset); + + /* First, let shmem grab the pages */ + if (i915_gem_object_get_page_list(obj)) + return VM_FAULT_SIGBUS; + + /* Now bind it into the GTT */ + if (i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment)) + return VM_FAULT_SIGBUS; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) + + page_offset; + /* Finally, remap it using the new GTT offset */ + ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); + + return ret == -EAGAIN ? VM_FAULT_OOM : VM_FAULT_NOPAGE; +} + +static int i915_gem_vm_mkwrite(struct vm_area_struct *vma, struct page *page) +{ + return 0; +} + +static int i915_gem_vm_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + return 0; +} + +static struct vm_operations_struct i915_gem_vm_ops = { + .open = i915_gem_vm_open, + .close = i915_gem_vm_close, + .fault = i915_gem_fault, + .page_mkwrite = i915_gem_vm_mkwrite, + .access = i915_gem_vm_access, +}; + +/** + * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing + * @dev: DRM device + * @data: GTT mapping ioctl data + * @file_priv: GEM object info + * + * Allocate a fake mmap offset for the given object and add it + * to the DRM map hash. This allows drm_mmap() to find it so that + * the vm_ops can be overridden with GEM fault & access handlers. + */ +int +i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_mmap_gtt *args = data; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (obj == NULL) + return -EBADF; + + obj_priv = obj->driver_private; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + args->addr_ptr = obj_priv->mmap_offset; + + obj_priv->gtt_alignment = args->alignment; + + return 0; +} + static void i915_gem_object_free_page_list(struct drm_gem_object *obj) { @@ -944,6 +1044,9 @@ i915_gem_object_unbind(struct drm_gem_object *obj) BUG_ON(obj_priv->active); + /* blow away mappings */ + unmap_mapping_range(dev->dev_mapping, obj_priv->mmap_offset, + obj->size, 1); i915_gem_object_free_page_list(obj); if (obj_priv->gtt_space) { @@ -2155,7 +2258,10 @@ i915_gem_throttle_ioctl(struct drm_device *dev, void *data, int i915_gem_init_object(struct drm_gem_object *obj) { + struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv; + struct drm_map_list *list; + struct drm_map *map; obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER); if (obj_priv == NULL) @@ -2173,11 +2279,58 @@ int i915_gem_init_object(struct drm_gem_object *obj) obj->driver_private = obj_priv; obj_priv->obj = obj; INIT_LIST_HEAD(&obj_priv->list); + + /* Set the object up for mmap'ing */ + list = &obj->map_list; + list->map = drm_calloc(1, sizeof(struct drm_map_list), + DRM_MEM_DRIVER); + if (!list->map) + return -ENOMEM; + + map = list->map; + map->type = _DRM_GEM; + map->size = obj->size; + map->handle = obj; + + /* Get a DRM GEM mmap offset allocated... */ + list->file_offset_node = drm_mm_search_free(&dev->offset_manager, + obj->size / PAGE_SIZE, 0, 0); + if (!list->file_offset_node) { + DRM_ERROR("failed to allocate offset for bo %d\n", obj->name); + /* cleanup map_list etc. here */ + return -ENOMEM; + } + + list->file_offset_node = drm_mm_get_block(list->file_offset_node, + obj->size / PAGE_SIZE, 0); + if (!list->file_offset_node) { + /* cleanup */ + return -ENOMEM; + } + + DRM_ERROR("adding object %d with offset 0x%08lx\n", + obj->name, list->file_offset_node->start); + list->hash.key = list->file_offset_node->start; + if (drm_ht_insert_item(&dev->map_hash, &list->hash)) { + DRM_ERROR("failed to add to map hash\n"); + /* cleanup */ + return -ENOMEM; + } + + obj->vm_ops = &i915_gem_vm_ops; + + /* By now we should be all set, any drm_mmap request on the offset + * below will get to our mmap & fault handler */ + obj_priv->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT; + return 0; } void i915_gem_free_object(struct drm_gem_object *obj) { + struct drm_device *dev = obj->dev; + struct drm_map_list *list; + struct drm_map *map; struct drm_i915_gem_object *obj_priv = obj->driver_private; while (obj_priv->pin_count > 0) @@ -2185,6 +2338,21 @@ void i915_gem_free_object(struct drm_gem_object *obj) i915_gem_object_unbind(obj); + list = &obj->map_list; + drm_ht_remove_item(&dev->map_hash, &list->hash); + + if (list->file_offset_node) { + drm_mm_put_block(list->file_offset_node); + list->file_offset_node = NULL; + } + + map = list->map; + if (!map) + return; + + drm_free(map, sizeof(*map), DRM_MEM_DRIVER); + list->map = NULL; + drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER); drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); } @@ -2300,7 +2468,15 @@ i915_gem_idle(struct drm_device *dev) * waited for a sequence higher than any pending execbuffer */ BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + if (!list_empty(&dev_priv->mm.flushing_list)) { + struct drm_i915_gem_object *obj_priv; + DRM_ERROR("flushing list not empty, still has:\n"); + list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, + list) { + DRM_ERROR(" %p: %08x\n", obj_priv, + obj_priv->last_rendering_seqno); + } + } /* Request should now be empty as we've also waited * for the last request in the list @@ -2313,7 +2489,6 @@ i915_gem_idle(struct drm_device *dev) return ret; BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); BUG_ON(!list_empty(&dev_priv->mm.request_list)); return 0; diff --git a/include/drm/drm.h b/include/drm/drm.h index 276a746..f3ffe5a 100644 --- a/include/drm/drm.h +++ b/include/drm/drm.h @@ -174,6 +174,7 @@ enum drm_map_type { _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ _DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */ _DRM_TTM = 6, /**< TTM type */ + _DRM_GEM = 7, /**< GEM object */ }; /** diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 31e2f17..dfc9f5e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -166,6 +166,11 @@ struct drm_device; #define DRM_FILE_HASH_ORDER 8 #define DRM_MM_INIT_MAX_PAGES 256 +/* + * We make up offsets for buffer objects so we can recognize them at + * mmap time. + */ + /*@}*/ /***********************************************************************/ @@ -605,6 +610,12 @@ struct drm_gem_object { /** File representing the shmem storage */ struct file *filp; + /* Mapping info for this object */ + struct drm_map_list map_list; + + /* Driver private ops for this object */ + struct vm_operations_struct *vm_ops; + /** * Size of the object, in bytes. Immutable over the object's * lifetime. @@ -1299,6 +1310,8 @@ void drm_gem_object_free(struct kref *kref); struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, size_t size); void drm_gem_object_handle_free(struct kref *kref); +int drm_gem_mmap(struct vm_area_struct *vma, struct file *filp, + struct drm_map *map); static inline void drm_gem_object_reference(struct drm_gem_object *obj) diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 7d88a21..033d6be 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -171,6 +171,7 @@ typedef struct drm_i915_sarea { #define DRM_I915_GEM_SW_FINISH 0x20 #define DRM_I915_GEM_SET_TILING 0x21 #define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_MMAP_GTT 0x23 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -200,6 +201,7 @@ typedef struct drm_i915_sarea { #define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) #define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) #define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) +#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) #define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) #define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) #define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) @@ -406,6 +408,24 @@ struct drm_i915_gem_mmap { uint64_t addr_ptr; }; +struct drm_i915_gem_mmap_gtt { + /** Handle for the object being mapped. */ + uint32_t handle; + uint32_t pad; + /** Offset in the object to map. */ + uint64_t offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + uint64_t size; + /** Returned pointer the data was mapped at */ + uint64_t addr_ptr; /* void *, but pointers are not 32/64 compatible */ + uint32_t flags; + uint32_t alignment; +}; + struct drm_i915_gem_set_domain { /** Handle for the object */ uint32_t handle;