diff options
author | Nian Wu <nian@graphics.(none)> | 2007-02-25 17:06:13 -0800 |
---|---|---|
committer | Nian Wu <nian@graphics.(none)> | 2007-02-25 17:06:13 -0800 |
commit | df2fc3ec62d6799a3266cfb18b1279a126892f44 (patch) | |
tree | 94d39b9172bce8c93cb035e40d12e635dfaeb12f /linux-core | |
parent | 80095ffe01efe79983c2124ecc99ce979d7ac6a9 (diff) | |
parent | 9d8ba2d0d479f53b996c0a0e366acfee52daab3b (diff) |
Merge git://proxy01.pd.intel.com:9419/git/mesa/drm into crestline
Diffstat (limited to 'linux-core')
-rw-r--r-- | linux-core/Makefile.kernel | 6 | ||||
-rw-r--r-- | linux-core/drmP.h | 297 | ||||
-rw-r--r-- | linux-core/drm_agpsupport.c | 13 | ||||
-rw-r--r-- | linux-core/drm_bo.c | 1542 | ||||
-rw-r--r-- | linux-core/drm_bo_move.c | 411 | ||||
-rw-r--r-- | linux-core/drm_compat.c | 265 | ||||
-rw-r--r-- | linux-core/drm_compat.h | 65 | ||||
-rw-r--r-- | linux-core/drm_fence.c | 265 | ||||
-rw-r--r-- | linux-core/drm_fops.c | 57 | ||||
-rw-r--r-- | linux-core/drm_irq.c | 4 | ||||
-rw-r--r-- | linux-core/drm_lock.c | 159 | ||||
-rw-r--r-- | linux-core/drm_mm.c | 1 | ||||
-rw-r--r-- | linux-core/drm_object.c | 14 | ||||
-rw-r--r-- | linux-core/drm_objects.h | 470 | ||||
-rw-r--r-- | linux-core/drm_stub.c | 2 | ||||
-rw-r--r-- | linux-core/drm_ttm.c | 296 | ||||
-rw-r--r-- | linux-core/drm_ttm.h | 146 | ||||
-rw-r--r-- | linux-core/drm_vm.c | 426 | ||||
-rw-r--r-- | linux-core/i810_dma.c | 4 | ||||
-rw-r--r-- | linux-core/i830_dma.c | 2 | ||||
-rw-r--r-- | linux-core/i915_buffer.c | 175 | ||||
-rw-r--r-- | linux-core/i915_drv.c | 18 | ||||
-rw-r--r-- | linux-core/i915_fence.c | 47 | ||||
-rw-r--r-- | linux-core/sis_drv.c | 2 | ||||
-rw-r--r-- | linux-core/via_buffer.c | 163 | ||||
-rw-r--r-- | linux-core/via_fence.c | 230 |
26 files changed, 3249 insertions, 1831 deletions
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 05d6e149..08c0fb2a 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -13,7 +13,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \ drm_memory_debug.o ati_pcigart.o drm_sman.o \ drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \ - drm_fence.o drm_ttm.o drm_bo.o + drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o @@ -29,8 +29,8 @@ sis-objs := sis_drv.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o \ - via_video.o via_dmablit.o -mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o + via_video.o via_dmablit.o via_fence.o via_buffer.o +mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o nv-objs := nv_drv.o ifeq ($(CONFIG_COMPAT),y) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 9c748e6e..9b5f5bdd 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -458,6 +458,10 @@ typedef struct drm_lock_data { struct file *filp; /**< File descr of lock holder (0=kernel) */ wait_queue_head_t lock_queue; /**< Queue of blocked processes */ unsigned long lock_time; /**< Time of last lock in jiffies */ + spinlock_t spinlock; + uint32_t kernel_waiters; + uint32_t user_waiters; + int idle_has_lock; } drm_lock_data_t; /** @@ -591,78 +595,8 @@ typedef struct ati_pcigart_info { drm_local_map_t mapping; } drm_ati_pcigart_info; -/* - * User space objects and their references. - */ - -#define drm_user_object_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) - -typedef enum { - drm_fence_type, - drm_buffer_type, - drm_ttm_type - - /* - * Add other user space object types here. - */ - -} drm_object_type_t; - - - - -/* - * A user object is a structure that helps the drm give out user handles - * to kernel internal objects and to keep track of these objects so that - * they can be destroyed, for example when the user space process exits. - * Designed to be accessible using a user space 32-bit handle. - */ - -typedef struct drm_user_object{ - drm_hash_item_t hash; - struct list_head list; - drm_object_type_t type; - atomic_t refcount; - int shareable; - drm_file_t *owner; - void (*ref_struct_locked) (drm_file_t *priv, struct drm_user_object *obj, - drm_ref_t ref_action); - void (*unref)(drm_file_t *priv, struct drm_user_object *obj, - drm_ref_t unref_action); - void (*remove)(drm_file_t *priv, struct drm_user_object *obj); -} drm_user_object_t; - -/* - * A ref object is a structure which is used to - * keep track of references to user objects and to keep track of these - * references so that they can be destroyed for example when the user space - * process exits. Designed to be accessible using a pointer to the _user_ object. - */ - - -typedef struct drm_ref_object { - drm_hash_item_t hash; - struct list_head list; - atomic_t refcount; - drm_ref_t unref_action; -} drm_ref_object_t; - - -#include "drm_ttm.h" - -/* - * buffer object driver - */ - -typedef struct drm_bo_driver{ - int cached[DRM_BO_MEM_TYPES]; - drm_local_map_t *iomap[DRM_BO_MEM_TYPES]; - drm_ttm_backend_t *(*create_ttm_backend_entry) - (struct drm_device *dev); - int (*fence_type)(uint32_t flags, uint32_t *class, uint32_t *type); - int (*invalidate_caches)(struct drm_device *dev, uint32_t flags); -} drm_bo_driver_t; +#include "drm_objects.h" /** * DRM driver structure. This structure represent the common code for @@ -712,6 +646,8 @@ struct drm_driver { void (*reclaim_buffers) (struct drm_device *dev, struct file * filp); void (*reclaim_buffers_locked) (struct drm_device *dev, struct file * filp); + void (*reclaim_buffers_idlelocked) (struct drm_device *dev, + struct file * filp); unsigned long (*get_map_ofs) (drm_map_t * map); unsigned long (*get_reg_ofs) (struct drm_device * dev); void (*set_version) (struct drm_device * dev, drm_set_version_t * sv); @@ -749,63 +685,6 @@ typedef struct drm_head { } drm_head_t; -typedef struct drm_fence_driver{ - int no_types; - uint32_t wrap_diff; - uint32_t flush_diff; - uint32_t sequence_mask; - int lazy_capable; - int (*emit) (struct drm_device *dev, uint32_t flags, - uint32_t *breadcrumb, - uint32_t *native_type); - void (*poke_flush) (struct drm_device *dev); -} drm_fence_driver_t; - -#define _DRM_FENCE_TYPE_EXE 0x00 - -typedef struct drm_fence_manager{ - int initialized; - rwlock_t lock; - - /* - * The list below should be maintained in sequence order and - * access is protected by the above spinlock. - */ - - struct list_head ring; - struct list_head *fence_types[32]; - volatile uint32_t pending_flush; - wait_queue_head_t fence_queue; - int pending_exe_flush; - uint32_t last_exe_flush; - uint32_t exe_flush_sequence; - atomic_t count; -} drm_fence_manager_t; - -typedef struct drm_buffer_manager{ - struct mutex init_mutex; - int nice_mode; - int initialized; - drm_file_t *last_to_validate; - int has_type[DRM_BO_MEM_TYPES]; - int use_type[DRM_BO_MEM_TYPES]; - drm_mm_t manager[DRM_BO_MEM_TYPES]; - struct list_head lru[DRM_BO_MEM_TYPES]; - struct list_head pinned[DRM_BO_MEM_TYPES]; - struct list_head unfenced; - struct list_head ddestroy; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - struct work_struct wq; -#else - struct delayed_work wq; -#endif - uint32_t fence_type; - unsigned long cur_pages; - atomic_t count; -} drm_buffer_manager_t; - - - /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. @@ -960,62 +839,6 @@ typedef struct drm_agp_ttm_priv { } drm_agp_ttm_priv; #endif -typedef struct drm_fence_object{ - drm_user_object_t base; - atomic_t usage; - - /* - * The below three fields are protected by the fence manager spinlock. - */ - - struct list_head ring; - int class; - uint32_t native_type; - uint32_t type; - uint32_t signaled; - uint32_t sequence; - uint32_t flush_mask; - uint32_t submitted_flush; -} drm_fence_object_t; - - -typedef struct drm_buffer_object{ - drm_device_t *dev; - drm_user_object_t base; - - /* - * If there is a possibility that the usage variable is zero, - * then dev->struct_mutext should be locked before incrementing it. - */ - - atomic_t usage; - drm_ttm_object_t *ttm_object; - drm_ttm_t *ttm; - unsigned long num_pages; - unsigned long buffer_start; - drm_bo_type_t type; - unsigned long offset; - uint32_t page_alignment; - atomic_t mapped; - uint32_t flags; - uint32_t mask; - uint32_t mem_type; - - drm_mm_node_t *mm_node; /* MM node for on-card RAM */ - struct list_head lru; - struct list_head ddestroy; - - uint32_t fence_type; - uint32_t fence_class; - drm_fence_object_t *fence; - uint32_t priv_flags; - wait_queue_head_t event_queue; - struct mutex mutex; -} drm_buffer_object_t; - -#define _DRM_BO_FLAG_UNFENCED 0x00000001 -#define _DRM_BO_FLAG_EVICTED 0x00000002 - static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) @@ -1193,12 +1016,14 @@ extern int drm_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -extern int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context); -extern int drm_lock_free(drm_device_t * dev, - __volatile__ unsigned int *lock, unsigned int context); +extern int drm_lock_take(drm_lock_data_t *lock_data, unsigned int context); +extern int drm_lock_free(drm_lock_data_t *lock_data, unsigned int context); +extern void drm_idlelock_take(drm_lock_data_t *lock_data); +extern void drm_idlelock_release(drm_lock_data_t *lock_data); + /* * These are exported to drivers so that they can implement fencing using - * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. + * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. */ extern int drm_i_have_hw_lock(struct file *filp); @@ -1357,106 +1182,10 @@ static inline drm_mm_t *drm_get_mm(drm_mm_node_t *block) } -/* - * User space object bookkeeping (drm_object.c) - */ - -/* - * Must be called with the struct_mutex held. - */ - -extern int drm_add_user_object(drm_file_t *priv, drm_user_object_t *item, - -/* - * Must be called with the struct_mutex held. - */ - int shareable); -extern drm_user_object_t *drm_lookup_user_object(drm_file_t *priv, uint32_t key); - -/* - * Must be called with the struct_mutex held. - * If "item" has been obtained by a call to drm_lookup_user_object. You may not - * release the struct_mutex before calling drm_remove_ref_object. - * This function may temporarily release the struct_mutex. - */ - -extern int drm_remove_user_object(drm_file_t *priv, drm_user_object_t *item); - -/* - * Must be called with the struct_mutex held. May temporarily release it. - */ - -extern int drm_add_ref_object(drm_file_t *priv, drm_user_object_t *referenced_object, - drm_ref_t ref_action); - -/* - * Must be called with the struct_mutex held. - */ - -drm_ref_object_t *drm_lookup_ref_object(drm_file_t *priv, - drm_user_object_t *referenced_object, - drm_ref_t ref_action); -/* - * Must be called with the struct_mutex held. - * If "item" has been obtained by a call to drm_lookup_ref_object. You may not - * release the struct_mutex before calling drm_remove_ref_object. - * This function may temporarily release the struct_mutex. - */ - -extern void drm_remove_ref_object(drm_file_t *priv, drm_ref_object_t *item); -extern int drm_user_object_ref(drm_file_t *priv, uint32_t user_token, drm_object_type_t type, - drm_user_object_t **object); -extern int drm_user_object_unref(drm_file_t *priv, uint32_t user_token, drm_object_type_t type); - - - -/* - * fence objects (drm_fence.c) - */ - -extern void drm_fence_handler(drm_device_t *dev, uint32_t breadcrumb, uint32_t type); -extern void drm_fence_manager_init(drm_device_t *dev); -extern void drm_fence_manager_takedown(drm_device_t *dev); -extern void drm_fence_flush_old(drm_device_t *dev, uint32_t sequence); -extern int drm_fence_object_flush(drm_device_t * dev, - volatile drm_fence_object_t * fence, - uint32_t type); -extern int drm_fence_object_signaled(volatile drm_fence_object_t * fence, - uint32_t type); -extern void drm_fence_usage_deref_locked(drm_device_t * dev, - drm_fence_object_t * fence); -extern void drm_fence_usage_deref_unlocked(drm_device_t * dev, - drm_fence_object_t * fence); -extern int drm_fence_object_wait(drm_device_t * dev, - volatile drm_fence_object_t * fence, - int lazy, int ignore_signals, uint32_t mask); -extern int drm_fence_object_create(drm_device_t *dev, uint32_t type, - uint32_t fence_flags, - drm_fence_object_t **c_fence); -extern int drm_fence_add_user_object(drm_file_t *priv, - drm_fence_object_t *fence, - int shareable); - -extern int drm_fence_ioctl(DRM_IOCTL_ARGS); - -/* - * buffer objects (drm_bo.c) - */ - -extern int drm_bo_ioctl(DRM_IOCTL_ARGS); -extern int drm_mm_init_ioctl(DRM_IOCTL_ARGS); -extern int drm_bo_driver_finish(drm_device_t *dev); -extern int drm_bo_driver_init(drm_device_t *dev); -extern int drm_fence_buffer_objects(drm_file_t * priv, - struct list_head *list, - uint32_t fence_flags, - drm_fence_object_t *fence, - drm_fence_object_t **used_fence); - extern void drm_core_ioremap(struct drm_map *map, struct drm_device *dev); extern void drm_core_ioremapfree(struct drm_map *map, struct drm_device *dev); diff --git a/linux-core/drm_agpsupport.c b/linux-core/drm_agpsupport.c index 9cdbdaf0..6b93d249 100644 --- a/linux-core/drm_agpsupport.c +++ b/linux-core/drm_agpsupport.c @@ -606,8 +606,8 @@ static int drm_agp_bind_ttm(drm_ttm_backend_t *backend, int ret; DRM_DEBUG("drm_agp_bind_ttm\n"); - DRM_MASK_VAL(backend->flags, DRM_BE_FLAG_BOUND_CACHED, - (cached) ? DRM_BE_FLAG_BOUND_CACHED : 0); + DRM_FLAG_MASKED(backend->flags, (cached) ? DRM_BE_FLAG_BOUND_CACHED : 0, + DRM_BE_FLAG_BOUND_CACHED); mem->is_flushed = TRUE; mem->type = (cached) ? agp_priv->cached_type : agp_priv->uncached_type; ret = drm_agp_bind_memory(mem, offset); @@ -620,7 +620,7 @@ static int drm_agp_bind_ttm(drm_ttm_backend_t *backend, static int drm_agp_unbind_ttm(drm_ttm_backend_t *backend) { drm_agp_ttm_priv *agp_priv = (drm_agp_ttm_priv *) backend->private; - + DRM_DEBUG("drm_agp_unbind_ttm\n"); if (agp_priv->mem->is_bound) return drm_agp_unbind_memory(agp_priv->mem); @@ -710,7 +710,6 @@ drm_ttm_backend_t *drm_agp_init_ttm(struct drm_device *dev, agp_priv->uncached_type = AGP_USER_MEMORY; agp_priv->bridge = dev->agp->bridge; agp_priv->populated = FALSE; - agp_be->aperture_base = dev->agp->agp_info.aper_base; agp_be->private = (void *) agp_priv; agp_be->needs_ub_cache_adjust = drm_agp_needs_unbind_cache_adjust; agp_be->populate = drm_agp_populate; @@ -718,10 +717,8 @@ drm_ttm_backend_t *drm_agp_init_ttm(struct drm_device *dev, agp_be->bind = drm_agp_bind_ttm; agp_be->unbind = drm_agp_unbind_ttm; agp_be->destroy = drm_agp_destroy_ttm; - DRM_MASK_VAL(agp_be->flags, DRM_BE_FLAG_NEEDS_FREE, - (backend == NULL) ? DRM_BE_FLAG_NEEDS_FREE : 0); - DRM_MASK_VAL(agp_be->flags, DRM_BE_FLAG_CBA, - (dev->agp->cant_use_aperture) ? DRM_BE_FLAG_CBA : 0); + DRM_FLAG_MASKED(agp_be->flags, (backend == NULL) ? DRM_BE_FLAG_NEEDS_FREE : 0, + DRM_BE_FLAG_NEEDS_FREE); agp_be->drm_map_type = _DRM_AGP; return agp_be; } diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 2b960c75..548ce14f 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -10,6 +10,10 @@ * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -18,11 +22,6 @@ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * * **************************************************************************/ /* @@ -32,34 +31,28 @@ #include "drmP.h" /* - * Buffer object locking policy: - * Lock dev->struct_mutex; - * Increase usage - * Unlock dev->struct_mutex; - * Lock buffer->mutex; - * Do whatever you want; - * Unlock buffer->mutex; - * Decrease usage. Call destruction if zero. + * Locking may look a bit complicated but isn't really: + * + * The buffer usage atomic_t needs to be protected by dev->struct_mutex + * when there is a chance that it can be zero before or after the operation. + * + * dev->struct_mutex also protects all lists and list heads. Hash tables and hash + * heads. * - * User object visibility ups usage just once, since it has its own - * refcounting. + * bo->mutex protects the buffer object itself excluding the usage field. + * bo->mutex does also protect the buffer list heads, so to manipulate those, we need + * both the bo->mutex and the dev->struct_mutex. * - * Destruction: - * lock dev->struct_mutex; - * Verify that usage is zero. Otherwise unlock and continue. - * Destroy object. - * unlock dev->struct_mutex; + * Locking order is bo->mutex, dev->struct_mutex. Therefore list traversal is a bit + * complicated. When dev->struct_mutex is released to grab bo->mutex, the list + * traversal will, in general, need to be restarted. * - * Mutex and spinlock locking orders: - * 1.) Buffer mutex - * 2.) Refer to ttm locking orders. */ -static void drm_bo_destroy_locked(drm_buffer_object_t *bo); - -#define DRM_FLAG_MASKED(_old, _new, _mask) {\ -(_old) ^= (((_old) ^ (_new)) & (_mask)); \ -} +static void drm_bo_destroy_locked(drm_buffer_object_t * bo); +static int drm_bo_setup_vm_locked(drm_buffer_object_t * bo); +static void drm_bo_takedown_vm_locked(drm_buffer_object_t * bo); +static void drm_bo_unmap_virtual(drm_buffer_object_t * bo); static inline uint32_t drm_bo_type_flags(unsigned type) { @@ -70,76 +63,189 @@ static inline uint32_t drm_bo_type_flags(unsigned type) * bo locked. dev->struct_mutex locked. */ -static void drm_bo_add_to_lru(drm_buffer_object_t * bo, - drm_buffer_manager_t * bm) +void drm_bo_add_to_pinned_lru(drm_buffer_object_t * bo) { - struct list_head *list; - bo->mem_type = 0; + drm_mem_type_manager_t *man; - switch(bo->flags & DRM_BO_MASK_MEM) { - case DRM_BO_FLAG_MEM_TT: - bo->mem_type = DRM_BO_MEM_TT; - break; - case DRM_BO_FLAG_MEM_VRAM: - bo->mem_type = DRM_BO_MEM_VRAM; - break; - case DRM_BO_FLAG_MEM_LOCAL: - bo->mem_type = DRM_BO_MEM_LOCAL; - break; - default: - BUG_ON(1); + man = &bo->dev->bm.man[bo->pinned_mem_type]; + list_add_tail(&bo->pinned_lru, &man->pinned); +} + +void drm_bo_add_to_lru(drm_buffer_object_t * bo) +{ + drm_mem_type_manager_t *man; + + if (!(bo->mem.mask & (DRM_BO_FLAG_NO_MOVE | DRM_BO_FLAG_NO_EVICT))) { + man = &bo->dev->bm.man[bo->mem.mem_type]; + list_add_tail(&bo->lru, &man->lru); + } else { + INIT_LIST_HEAD(&bo->lru); } - list = (bo->flags & (DRM_BO_FLAG_NO_EVICT | DRM_BO_FLAG_NO_MOVE)) ? - &bm->pinned[bo->mem_type] : &bm->lru[bo->mem_type]; - list_add_tail(&bo->lru, list); - return; +} + +static int drm_bo_vm_pre_move(drm_buffer_object_t * bo, int old_is_pci) +{ +#ifdef DRM_ODD_MM_COMPAT + int ret; + + ret = drm_bo_lock_kmm(bo); + if (ret) + return ret; + drm_bo_unmap_virtual(bo); + if (old_is_pci) + drm_bo_finish_unmap(bo); +#else + drm_bo_unmap_virtual(bo); +#endif + return 0; +} + +static void drm_bo_vm_post_move(drm_buffer_object_t * bo) +{ +#ifdef DRM_ODD_MM_COMPAT + int ret; + + ret = drm_bo_remap_bound(bo); + if (ret) { + DRM_ERROR("Failed to remap a bound buffer object.\n" + "\tThis might cause a sigbus later.\n"); + } + drm_bo_unlock_kmm(bo); +#endif } /* - * bo locked. + * Call bo->mutex locked. */ -static int drm_move_tt_to_local(drm_buffer_object_t * bo, int evict, - int force_no_move) +static int drm_bo_add_ttm(drm_buffer_object_t * bo) { drm_device_t *dev = bo->dev; - int ret; + int ret = 0; + bo->ttm = NULL; - if (bo->mm_node) { - mutex_lock(&dev->struct_mutex); - if (evict) - ret = drm_evict_ttm(bo->ttm); - else - ret = drm_unbind_ttm(bo->ttm); + switch (bo->type) { + case drm_bo_type_dc: + bo->ttm = drm_ttm_init(dev, bo->mem.num_pages << PAGE_SHIFT); + if (!bo->ttm) + ret = -ENOMEM; + break; + case drm_bo_type_user: + case drm_bo_type_fake: + break; + default: + DRM_ERROR("Illegal buffer object type\n"); + ret = -EINVAL; + break; + } - if (ret) { - mutex_unlock(&dev->struct_mutex); - if (ret == -EAGAIN) - schedule(); - return ret; - } + return ret; +} + +static int drm_bo_handle_move_mem(drm_buffer_object_t * bo, + drm_bo_mem_reg_t * mem, + int evict, int no_wait) +{ + drm_device_t *dev = bo->dev; + drm_buffer_manager_t *bm = &dev->bm; + int old_is_pci = drm_mem_reg_is_pci(dev, &bo->mem); + int new_is_pci = drm_mem_reg_is_pci(dev, mem); + drm_mem_type_manager_t *old_man = &bm->man[bo->mem.mem_type]; + drm_mem_type_manager_t *new_man = &bm->man[mem->mem_type]; + int ret = 0; - if (!(bo->flags & DRM_BO_FLAG_NO_MOVE) || force_no_move) { - drm_mm_put_block(bo->mm_node); - bo->mm_node = NULL; + if (old_is_pci || new_is_pci) + ret = drm_bo_vm_pre_move(bo, old_is_pci); + if (ret) + return ret; + + /* + * Create and bind a ttm if required. + */ + + if (!(new_man->flags & _DRM_FLAG_MEMTYPE_FIXED) && (bo->ttm == NULL)) { + ret = drm_bo_add_ttm(bo); + if (ret) + goto out_err; + + if (mem->mem_type != DRM_BO_MEM_LOCAL) { + ret = drm_bind_ttm(bo->ttm, new_man->flags & + DRM_BO_FLAG_CACHED, + mem->mm_node->start); + if (ret) + goto out_err; } - mutex_unlock(&dev->struct_mutex); } - bo->flags &= ~DRM_BO_FLAG_MEM_TT; - bo->flags |= DRM_BO_FLAG_MEM_LOCAL | DRM_BO_FLAG_CACHED; + if ((bo->mem.mem_type == DRM_BO_MEM_LOCAL) && bo->ttm == NULL) { + + drm_bo_mem_reg_t *old_mem = &bo->mem; + uint32_t save_flags = old_mem->flags; + uint32_t save_mask = old_mem->mask; + + *old_mem = *mem; + mem->mm_node = NULL; + old_mem->mask = save_mask; + DRM_FLAG_MASKED(save_flags, mem->flags, DRM_BO_MASK_MEMTYPE); + + } else if (!(old_man->flags & _DRM_FLAG_MEMTYPE_FIXED) && + !(new_man->flags & _DRM_FLAG_MEMTYPE_FIXED)) { + + ret = drm_bo_move_ttm(bo, evict, no_wait, mem); + + } else if (dev->driver->bo_driver->move) { + ret = dev->driver->bo_driver->move(bo, evict, no_wait, mem); + + } else { + + ret = drm_bo_move_memcpy(bo, evict, no_wait, mem); + + } + + if (ret) + goto out_err; + + if (old_is_pci || new_is_pci) + drm_bo_vm_post_move(bo); + + if (bo->priv_flags & _DRM_BO_FLAG_EVICTED) { + ret = + dev->driver->bo_driver->invalidate_caches(dev, + bo->mem.flags); + if (ret) + DRM_ERROR("Can not flush read caches\n"); + } + + DRM_FLAG_MASKED(bo->priv_flags, + (evict) ? _DRM_BO_FLAG_EVICTED : 0, + _DRM_BO_FLAG_EVICTED); + + if (bo->mem.mm_node) + bo->offset = bo->mem.mm_node->start << PAGE_SHIFT; return 0; -} + out_err: + if (old_is_pci || new_is_pci) + drm_bo_vm_post_move(bo); + + new_man = &bm->man[bo->mem.mem_type]; + if ((new_man->flags & _DRM_FLAG_MEMTYPE_FIXED) && bo->ttm) { + drm_ttm_unbind(bo->ttm); + drm_destroy_ttm(bo->ttm); + bo->ttm = NULL; + } + + return ret; +} /* * Call bo->mutex locked. * Wait until the buffer is idle. */ -static int drm_bo_wait(drm_buffer_object_t * bo, int lazy, int ignore_signals, - int no_wait) +int drm_bo_wait(drm_buffer_object_t * bo, int lazy, int ignore_signals, + int no_wait) { drm_fence_object_t *fence = bo->fence; @@ -168,14 +274,44 @@ static int drm_bo_wait(drm_buffer_object_t * bo, int lazy, int ignore_signals, return 0; } +static int drm_bo_expire_fence(drm_buffer_object_t * bo, int allow_errors) +{ + drm_device_t *dev = bo->dev; + drm_buffer_manager_t *bm = &dev->bm; + + if (bo->fence) { + if (bm->nice_mode) { + unsigned long _end = jiffies + 3 * DRM_HZ; + int ret; + do { + ret = drm_bo_wait(bo, 0, 1, 0); + if (ret && allow_errors) + return ret; + + } while (ret && !time_after_eq(jiffies, _end)); + + if (bo->fence) { + bm->nice_mode = 0; + DRM_ERROR("Detected GPU lockup or " + "fence driver was taken down. " + "Evicting buffer.\n"); + } + } + if (bo->fence) { + drm_fence_usage_deref_unlocked(dev, bo->fence); + bo->fence = NULL; + } + } + return 0; +} + /* * Call dev->struct_mutex locked. * Attempts to remove all private references to a buffer by expiring its * fence object and removing from lru lists and memory managers. */ - -static void drm_bo_cleanup_refs(drm_buffer_object_t *bo, int remove_all) +static void drm_bo_cleanup_refs(drm_buffer_object_t * bo, int remove_all) { drm_device_t *dev = bo->dev; drm_buffer_manager_t *bm = &dev->bm; @@ -186,32 +322,14 @@ static void drm_bo_cleanup_refs(drm_buffer_object_t *bo, int remove_all) DRM_FLAG_MASKED(bo->priv_flags, 0, _DRM_BO_FLAG_UNFENCED); - if (bo->fence && drm_fence_object_signaled(bo->fence, - bo->fence_type)) { + if (bo->fence && drm_fence_object_signaled(bo->fence, bo->fence_type)) { drm_fence_usage_deref_locked(dev, bo->fence); bo->fence = NULL; } - if (bo->fence && remove_all) { - if (bm->nice_mode) { - unsigned long _end = jiffies + 3 * DRM_HZ; - int ret; - do { - ret = drm_bo_wait(bo, 0, 1, 0); - } while (ret && !time_after_eq(jiffies, _end)); + if (bo->fence && remove_all) + (void)drm_bo_expire_fence(bo, 0); - if (bo->fence) { - bm->nice_mode = 0; - DRM_ERROR("Detected GPU lockup or " - "fence driver was taken down. " - "Evicting waiting buffers.\n"); - } - if (bo->fence) { - drm_fence_usage_deref_unlocked(dev, bo->fence); - bo->fence = NULL; - } - } - } mutex_lock(&dev->struct_mutex); if (!atomic_dec_and_test(&bo->usage)) { @@ -220,9 +338,16 @@ static void drm_bo_cleanup_refs(drm_buffer_object_t *bo, int remove_all) if (!bo->fence) { list_del_init(&bo->lru); - if (bo->mm_node) { - drm_mm_put_block(bo->mm_node); - bo->mm_node = NULL; + if (bo->mem.mm_node) { + drm_mm_put_block(bo->mem.mm_node); + if (bo->pinned_node == bo->mem.mm_node) + bo->pinned_node = NULL; + bo->mem.mm_node = NULL; + } + list_del_init(&bo->pinned_lru); + if (bo->pinned_node) { + drm_mm_put_block(bo->pinned_node); + bo->pinned_node = NULL; } list_del_init(&bo->ddestroy); mutex_unlock(&bo->mutex); @@ -234,50 +359,42 @@ static void drm_bo_cleanup_refs(drm_buffer_object_t *bo, int remove_all) drm_fence_object_flush(dev, bo->fence, bo->fence_type); list_add_tail(&bo->ddestroy, &bm->ddestroy); schedule_delayed_work(&bm->wq, - ((DRM_HZ / 100) < - 1) ? 1 : DRM_HZ / 100); + ((DRM_HZ / 100) < 1) ? 1 : DRM_HZ / 100); } -out: + out: mutex_unlock(&bo->mutex); return; } - /* * Verify that refcount is 0 and that there are no internal references * to the buffer object. Then destroy it. */ -static void drm_bo_destroy_locked(drm_buffer_object_t *bo) +static void drm_bo_destroy_locked(drm_buffer_object_t * bo) { drm_device_t *dev = bo->dev; drm_buffer_manager_t *bm = &dev->bm; - if (list_empty(&bo->lru) && bo->mm_node == NULL && atomic_read(&bo->usage) == 0) { - BUG_ON(bo->fence != NULL); - - if (bo->ttm) { - unsigned long _end = jiffies + DRM_HZ; - int ret; - - do { - ret = drm_unbind_ttm(bo->ttm); - if (ret == -EAGAIN) { - mutex_unlock(&dev->struct_mutex); - schedule(); - mutex_lock(&dev->struct_mutex); - } - } while (ret == -EAGAIN && !time_after_eq(jiffies, _end)); - - if (ret) { - DRM_ERROR("Couldn't unbind TTM region while destroying a buffer. " - "Bad. Continuing anyway\n"); - } + if (list_empty(&bo->lru) && bo->mem.mm_node == NULL && + list_empty(&bo->pinned_lru) && bo->pinned_node == NULL && + list_empty(&bo->ddestroy) && atomic_read(&bo->usage) == 0) { + if (bo->fence != NULL) { + DRM_ERROR("Fence was non-zero.\n"); + drm_bo_cleanup_refs(bo, 0); + return; } - if (bo->ttm_object) { - drm_ttm_object_deref_locked(dev, bo->ttm_object); +#ifdef DRM_ODD_MM_COMPAT + BUG_ON(!list_empty(&bo->vma_list)); + BUG_ON(!list_empty(&bo->p_mm_list)); +#endif + + if (bo->ttm) { + drm_ttm_unbind(bo->ttm); + drm_destroy_ttm(bo->ttm); + bo->ttm = NULL; } atomic_dec(&bm->count); @@ -297,7 +414,6 @@ static void drm_bo_destroy_locked(drm_buffer_object_t *bo) return; } - /* * Call dev->struct_mutex locked. */ @@ -325,7 +441,6 @@ static void drm_bo_delayed_delete(drm_device_t * dev, int remove_all) atomic_dec(&nentry->usage); } } - } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) @@ -338,11 +453,11 @@ static void drm_bo_delayed_workqueue(struct work_struct *work) drm_device_t *dev = (drm_device_t *) data; drm_buffer_manager_t *bm = &dev->bm; #else - drm_buffer_manager_t *bm = container_of(work, drm_buffer_manager_t, wq.work); + drm_buffer_manager_t *bm = + container_of(work, drm_buffer_manager_t, wq.work); drm_device_t *dev = container_of(bm, drm_device_t, bm); #endif - DRM_DEBUG("Delayed delete Worker\n"); mutex_lock(&dev->struct_mutex); @@ -358,7 +473,7 @@ static void drm_bo_delayed_workqueue(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } -static void drm_bo_usage_deref_locked(drm_buffer_object_t * bo) +void drm_bo_usage_deref_locked(drm_buffer_object_t * bo) { if (atomic_dec_and_test(&bo->usage)) { drm_bo_destroy_locked(bo); @@ -367,8 +482,11 @@ static void drm_bo_usage_deref_locked(drm_buffer_object_t * bo) static void drm_bo_base_deref_locked(drm_file_t * priv, drm_user_object_t * uo) { - drm_bo_usage_deref_locked(drm_user_object_entry(uo, drm_buffer_object_t, - base)); + drm_buffer_object_t *bo = + drm_user_object_entry(uo, drm_buffer_object_t, base); + + drm_bo_takedown_vm_locked(bo); + drm_bo_usage_deref_locked(bo); } static void drm_bo_usage_deref_unlocked(drm_buffer_object_t * bo) @@ -443,7 +561,7 @@ int drm_fence_buffer_objects(drm_file_t * priv, } } else { mutex_unlock(&dev->struct_mutex); - ret = drm_fence_object_create(dev, fence_type, + ret = drm_fence_object_create(dev, 0, fence_type, fence_flags | DRM_FENCE_FLAG_EMIT, &fence); mutex_lock(&dev->struct_mutex); @@ -454,6 +572,7 @@ int drm_fence_buffer_objects(drm_file_t * priv, count = 0; l = f_list.next; while (l != &f_list) { + prefetch(l->next); entry = list_entry(l, drm_buffer_object_t, lru); atomic_inc(&entry->usage); mutex_unlock(&dev->struct_mutex); @@ -468,7 +587,7 @@ int drm_fence_buffer_objects(drm_file_t * priv, DRM_FLAG_MASKED(entry->priv_flags, 0, _DRM_BO_FLAG_UNFENCED); DRM_WAKEUP(&entry->event_queue); - drm_bo_add_to_lru(entry, bm); + drm_bo_add_to_lru(entry); } mutex_unlock(&entry->mutex); drm_bo_usage_deref_locked(entry); @@ -489,11 +608,11 @@ EXPORT_SYMBOL(drm_fence_buffer_objects); */ static int drm_bo_evict(drm_buffer_object_t * bo, unsigned mem_type, - int no_wait, int force_no_move) + int no_wait) { int ret = 0; drm_device_t *dev = bo->dev; - drm_buffer_manager_t *bm = &dev->bm; + drm_bo_mem_reg_t evict_mem; /* * Someone might have modified the buffer before we took the buffer mutex. @@ -501,70 +620,93 @@ static int drm_bo_evict(drm_buffer_object_t * bo, unsigned mem_type, if (bo->priv_flags & _DRM_BO_FLAG_UNFENCED) goto out; - if (!(bo->flags & drm_bo_type_flags(mem_type))) + if (bo->mem.mem_type != mem_type) goto out; ret = drm_bo_wait(bo, 0, 0, no_wait); + if (ret && ret != -EAGAIN) { + DRM_ERROR("Failed to expire fence before " + "buffer eviction.\n"); + goto out; + } + + evict_mem = bo->mem; + evict_mem.mm_node = NULL; + + if (bo->type == drm_bo_type_fake) { + bo->mem.mem_type = DRM_BO_MEM_LOCAL; + bo->mem.mm_node = NULL; + goto out1; + } + + evict_mem = bo->mem; + evict_mem.mask = dev->driver->bo_driver->evict_mask(bo); + ret = drm_bo_mem_space(bo, &evict_mem, no_wait); + if (ret) { if (ret != -EAGAIN) - DRM_ERROR("Failed to expire fence before " - "buffer eviction.\n"); + DRM_ERROR("Failed to find memory space for " + "buffer 0x%p eviction.\n", bo); goto out; } - if (mem_type == DRM_BO_MEM_TT) { - ret = drm_move_tt_to_local(bo, 1, force_no_move); - if (ret) - goto out; - mutex_lock(&dev->struct_mutex); - list_del_init(&bo->lru); - drm_bo_add_to_lru(bo, bm); - mutex_unlock(&dev->struct_mutex); - } + ret = drm_bo_handle_move_mem(bo, &evict_mem, 1, no_wait); - if (ret) + if (ret) { + if (ret != -EAGAIN) + DRM_ERROR("Buffer eviction failed\n"); goto out; + } + + out1: + mutex_lock(&dev->struct_mutex); + if (evict_mem.mm_node) { + if (evict_mem.mm_node != bo->pinned_node) + drm_mm_put_block(evict_mem.mm_node); + evict_mem.mm_node = NULL; + } + list_del(&bo->lru); + drm_bo_add_to_lru(bo); + mutex_unlock(&dev->struct_mutex); DRM_FLAG_MASKED(bo->priv_flags, _DRM_BO_FLAG_EVICTED, _DRM_BO_FLAG_EVICTED); + out: return ret; } -/* - * bo->mutex locked. - */ - -int drm_bo_alloc_space(drm_buffer_object_t * bo, unsigned mem_type, - int no_wait) +static int drm_bo_mem_force_space(drm_device_t * dev, + drm_bo_mem_reg_t * mem, + uint32_t mem_type, int no_wait) { - drm_device_t *dev = bo->dev; drm_mm_node_t *node; drm_buffer_manager_t *bm = &dev->bm; drm_buffer_object_t *entry; - drm_mm_t *mm = &bm->manager[mem_type]; + drm_mem_type_manager_t *man = &bm->man[mem_type]; struct list_head *lru; - unsigned long size = bo->num_pages; + unsigned long num_pages = mem->num_pages; int ret; mutex_lock(&dev->struct_mutex); do { - node = drm_mm_search_free(mm, size, bo->page_alignment, 1); + node = drm_mm_search_free(&man->manager, num_pages, + mem->page_alignment, 1); if (node) break; - lru = &bm->lru[mem_type]; + lru = &man->lru; if (lru->next == lru) break; entry = list_entry(lru->next, drm_buffer_object_t, lru); - atomic_inc(&entry->usage); mutex_unlock(&dev->struct_mutex); mutex_lock(&entry->mutex); - BUG_ON(bo->flags & DRM_BO_FLAG_NO_MOVE); - ret = drm_bo_evict(entry, mem_type, no_wait, 0); + BUG_ON(entry->mem.flags & (DRM_BO_FLAG_NO_MOVE | DRM_BO_FLAG_NO_EVICT)); + + ret = drm_bo_evict(entry, mem_type, no_wait); mutex_unlock(&entry->mutex); drm_bo_usage_deref_unlocked(entry); if (ret) @@ -573,146 +715,163 @@ int drm_bo_alloc_space(drm_buffer_object_t * bo, unsigned mem_type, } while (1); if (!node) { - DRM_ERROR("Out of videoram / aperture space\n"); mutex_unlock(&dev->struct_mutex); return -ENOMEM; } - node = drm_mm_get_block(node, size, bo->page_alignment); + node = drm_mm_get_block(node, num_pages, mem->page_alignment); mutex_unlock(&dev->struct_mutex); - BUG_ON(!node); - node->private = (void *)bo; - - bo->mm_node = node; - bo->offset = node->start * PAGE_SIZE; + mem->mm_node = node; + mem->mem_type = mem_type; return 0; } -static int drm_move_local_to_tt(drm_buffer_object_t * bo, int no_wait) +static int drm_bo_mt_compatible(drm_mem_type_manager_t * man, + uint32_t mem_type, + uint32_t mask, uint32_t * res_mask) { - drm_device_t *dev = bo->dev; - drm_ttm_backend_t *be; - int ret; + uint32_t cur_flags = drm_bo_type_flags(mem_type); + uint32_t flag_diff; - if (!(bo->mm_node && (bo->flags & DRM_BO_FLAG_NO_MOVE))) { - BUG_ON(bo->mm_node); - ret = drm_bo_alloc_space(bo, DRM_BO_MEM_TT, no_wait); - if (ret) - return ret; - } + if (man->flags & _DRM_FLAG_MEMTYPE_CACHED) + cur_flags |= DRM_BO_FLAG_CACHED; + if (man->flags & _DRM_FLAG_MEMTYPE_MAPPABLE) + cur_flags |= DRM_BO_FLAG_MAPPABLE; + if (man->flags & _DRM_FLAG_MEMTYPE_CSELECT) + DRM_FLAG_MASKED(cur_flags, mask, DRM_BO_FLAG_CACHED); - DRM_DEBUG("Flipping in to AGP 0x%08lx\n", bo->mm_node->start); - - mutex_lock(&dev->struct_mutex); - ret = drm_bind_ttm(bo->ttm, bo->flags & DRM_BO_FLAG_BIND_CACHED, - bo->mm_node->start); - if (ret) { - drm_mm_put_block(bo->mm_node); - bo->mm_node = NULL; - } - mutex_unlock(&dev->struct_mutex); + if ((cur_flags & mask & DRM_BO_MASK_MEM) == 0) + return 0; - if (ret) { - return ret; + if (mem_type == DRM_BO_MEM_LOCAL) { + *res_mask = cur_flags; + return 1; } - be = bo->ttm->be; - if (be->needs_ub_cache_adjust(be)) - bo->flags &= ~DRM_BO_FLAG_CACHED; - bo->flags &= ~DRM_BO_MASK_MEM; - bo->flags |= DRM_BO_FLAG_MEM_TT; + flag_diff = (mask ^ cur_flags); + if ((flag_diff & DRM_BO_FLAG_CACHED) && + (!(mask & DRM_BO_FLAG_CACHED) || + (mask & DRM_BO_FLAG_FORCE_CACHING))) + return 0; - if (bo->priv_flags & _DRM_BO_FLAG_EVICTED) { - ret = dev->driver->bo_driver->invalidate_caches(dev, bo->flags); - if (ret) - DRM_ERROR("Could not flush read caches\n"); - } - DRM_FLAG_MASKED(bo->priv_flags, 0, _DRM_BO_FLAG_EVICTED); + if ((flag_diff & DRM_BO_FLAG_MAPPABLE) && + ((mask & DRM_BO_FLAG_MAPPABLE) || + (mask & DRM_BO_FLAG_FORCE_MAPPABLE)) ) + return 0; - return 0; + *res_mask = cur_flags; + return 1; } -static int drm_bo_new_flags(drm_device_t * dev, - uint32_t flags, uint32_t new_mask, uint32_t hint, - int init, uint32_t * n_flags, uint32_t * n_mask) +int drm_bo_mem_space(drm_buffer_object_t * bo, + drm_bo_mem_reg_t * mem, int no_wait) { - uint32_t new_flags = 0; - uint32_t new_props; - drm_bo_driver_t *driver = dev->driver->bo_driver; + drm_device_t *dev = bo->dev; drm_buffer_manager_t *bm = &dev->bm; - unsigned i; + drm_mem_type_manager_t *man; + + uint32_t num_prios = dev->driver->bo_driver->num_mem_type_prio; + const uint32_t *prios = dev->driver->bo_driver->mem_type_prio; + uint32_t i; + uint32_t mem_type = DRM_BO_MEM_LOCAL; + uint32_t cur_flags; + int type_found = 0; + int type_ok = 0; + int has_eagain = 0; + drm_mm_node_t *node = NULL; + int ret; - /* - * First adjust the mask to take away nonexistant memory types. - */ + mem->mm_node = NULL; + for (i = 0; i < num_prios; ++i) { + mem_type = prios[i]; + man = &bm->man[mem_type]; - for (i = 0; i < DRM_BO_MEM_TYPES; ++i) { - if (!bm->use_type[i]) - new_mask &= ~drm_bo_type_flags(i); - } + type_ok = drm_bo_mt_compatible(man, mem_type, mem->mask, + &cur_flags); - if ((new_mask & DRM_BO_FLAG_NO_EVICT) && !DRM_SUSER(DRM_CURPROC)) { - DRM_ERROR - ("DRM_BO_FLAG_NO_EVICT is only available to priviliged " - "processes\n"); - return -EPERM; - } - if (new_mask & DRM_BO_FLAG_BIND_CACHED) { - if (((new_mask & DRM_BO_FLAG_MEM_TT) && - !driver->cached[DRM_BO_MEM_TT]) && - ((new_mask & DRM_BO_FLAG_MEM_VRAM) - && !driver->cached[DRM_BO_MEM_VRAM])) { - new_mask &= ~DRM_BO_FLAG_BIND_CACHED; - } else { - if (!driver->cached[DRM_BO_MEM_TT]) - new_flags &= DRM_BO_FLAG_MEM_TT; - if (!driver->cached[DRM_BO_MEM_VRAM]) - new_flags &= DRM_BO_FLAG_MEM_VRAM; + if (!type_ok) + continue; + + if (mem_type == DRM_BO_MEM_LOCAL) + break; + + if ((mem_type == bo->pinned_mem_type) && + (bo->pinned_node != NULL)) { + node = bo->pinned_node; + break; } - } - if ((new_mask & DRM_BO_FLAG_READ_CACHED) && - !(new_mask & DRM_BO_FLAG_BIND_CACHED)) { - if ((new_mask & DRM_BO_FLAG_NO_EVICT) && - !(new_mask & DRM_BO_FLAG_MEM_LOCAL)) { - DRM_ERROR - ("Cannot read cached from a pinned VRAM / TT buffer\n"); - return -EINVAL; + mutex_lock(&dev->struct_mutex); + if (man->has_type && man->use_type) { + type_found = 1; + node = drm_mm_search_free(&man->manager, mem->num_pages, + mem->page_alignment, 1); + if (node) + node = drm_mm_get_block(node, mem->num_pages, + mem->page_alignment); } + mutex_unlock(&dev->struct_mutex); + if (node) + break; } - /* - * Determine new memory location: - */ + if ((type_ok && (mem_type == DRM_BO_MEM_LOCAL)) || node) { + mem->mm_node = node; + mem->mem_type = mem_type; + mem->flags = cur_flags; + return 0; + } - if (!(flags & new_mask & DRM_BO_MASK_MEM) || init) { + if (!type_found) + return -EINVAL; - new_flags = new_mask & DRM_BO_MASK_MEM; + num_prios = dev->driver->bo_driver->num_mem_busy_prio; + prios = dev->driver->bo_driver->mem_busy_prio; - if (!new_flags) { - DRM_ERROR("Invalid buffer object memory flags\n"); - return -EINVAL; - } + for (i = 0; i < num_prios; ++i) { + mem_type = prios[i]; + man = &bm->man[mem_type]; - if (new_flags & DRM_BO_FLAG_MEM_LOCAL) { - if ((hint & DRM_BO_HINT_AVOID_LOCAL) && - new_flags & (DRM_BO_FLAG_MEM_VRAM | - DRM_BO_FLAG_MEM_TT)) { - new_flags &= ~DRM_BO_FLAG_MEM_LOCAL; - } else { - new_flags = DRM_BO_FLAG_MEM_LOCAL; - } - } - if (new_flags & DRM_BO_FLAG_MEM_TT) { - if ((new_mask & DRM_BO_FLAG_PREFER_VRAM) && - new_flags & DRM_BO_FLAG_MEM_VRAM) { - new_flags = DRM_BO_FLAG_MEM_VRAM; - } else { - new_flags = DRM_BO_FLAG_MEM_TT; - } + if (!drm_bo_mt_compatible(man, mem_type, mem->mask, &cur_flags)) + continue; + + ret = drm_bo_mem_force_space(dev, mem, mem_type, no_wait); + + if (ret == 0) { + mem->flags = cur_flags; + return 0; } - } else { - new_flags = flags & DRM_BO_MASK_MEM; + + if (ret == -EAGAIN) + has_eagain = 1; + } + + ret = (has_eagain) ? -EAGAIN : -ENOMEM; + return ret; +} + +EXPORT_SYMBOL(drm_bo_mem_space); + +static int drm_bo_new_mask(drm_buffer_object_t * bo, + uint32_t new_mask, uint32_t hint) +{ + uint32_t new_props; + + if (bo->type == drm_bo_type_user) { + DRM_ERROR("User buffers are not supported yet\n"); + return -EINVAL; + } + if (bo->type == drm_bo_type_fake && + !(new_mask & (DRM_BO_FLAG_NO_MOVE | DRM_BO_FLAG_NO_EVICT))) { + DRM_ERROR("Fake buffers must be pinned.\n"); + return -EINVAL; + } + + if ((new_mask & DRM_BO_FLAG_NO_EVICT) && !DRM_SUSER(DRM_CURPROC)) { + DRM_ERROR + ("DRM_BO_FLAG_NO_EVICT is only available to priviliged " + "processes\n"); + return -EPERM; } new_props = new_mask & (DRM_BO_FLAG_EXE | DRM_BO_FLAG_WRITE | @@ -723,22 +882,7 @@ static int drm_bo_new_flags(drm_device_t * dev, return -EINVAL; } - new_flags |= new_mask & ~DRM_BO_MASK_MEM; - - if (((flags ^ new_flags) & DRM_BO_FLAG_BIND_CACHED) && - (new_flags & DRM_BO_FLAG_NO_EVICT) && - (flags & (DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_MEM_VRAM))) { - if (!(flags & DRM_BO_FLAG_CACHED)) { - DRM_ERROR - ("Cannot change caching policy of pinned buffer\n"); - return -EINVAL; - } else { - new_flags &= ~DRM_BO_FLAG_CACHED; - } - } - - *n_flags = new_flags; - *n_mask = new_mask; + bo->mem.mask = new_mask; return 0; } @@ -825,8 +969,8 @@ static int drm_bo_read_cached(drm_buffer_object_t * bo) int ret = 0; BUG_ON(bo->priv_flags & _DRM_BO_FLAG_UNFENCED); - if (bo->mm_node) - ret = drm_bo_evict(bo, DRM_BO_MEM_TT, 1, 0); + if (bo->mem.mm_node) + ret = drm_bo_evict(bo, DRM_BO_MEM_TT, 1); return ret; } @@ -916,21 +1060,15 @@ static void drm_bo_fill_rep_arg(drm_buffer_object_t * bo, drm_bo_arg_reply_t * rep) { rep->handle = bo->base.hash.key; - rep->flags = bo->flags; - rep->size = bo->num_pages * PAGE_SIZE; + rep->flags = bo->mem.flags; + rep->size = bo->mem.num_pages * PAGE_SIZE; rep->offset = bo->offset; - - if (bo->ttm_object) { - rep->arg_handle = bo->ttm_object->map_list.user_token; - } else { - rep->arg_handle = 0; - } - - rep->mask = bo->mask; + rep->arg_handle = bo->map_list.user_token; + rep->mask = bo->mem.mask; rep->buffer_start = bo->buffer_start; rep->fence_flags = bo->fence_type; rep->rep_flags = 0; - rep->page_alignment = bo->page_alignment; + rep->page_alignment = bo->mem.page_alignment; if ((bo->priv_flags & _DRM_BO_FLAG_UNFENCED) || drm_bo_quick_busy(bo)) { DRM_FLAG_MASKED(rep->rep_flags, DRM_BO_REP_BUSY, @@ -988,14 +1126,14 @@ static int drm_buffer_object_map(drm_file_t * priv, uint32_t handle, } if ((map_flags & DRM_BO_FLAG_READ) && - (bo->flags & DRM_BO_FLAG_READ_CACHED) && - (!(bo->flags & DRM_BO_FLAG_CACHED))) { + (bo->mem.flags & DRM_BO_FLAG_READ_CACHED) && + (!(bo->mem.flags & DRM_BO_FLAG_CACHED))) { drm_bo_read_cached(bo); } break; } else if ((map_flags & DRM_BO_FLAG_READ) && - (bo->flags & DRM_BO_FLAG_READ_CACHED) && - (!(bo->flags & DRM_BO_FLAG_CACHED))) { + (bo->mem.flags & DRM_BO_FLAG_READ_CACHED) && + (!(bo->mem.flags & DRM_BO_FLAG_CACHED))) { /* * We are already mapped with different flags. @@ -1078,46 +1216,124 @@ static void drm_buffer_user_object_unmap(drm_file_t * priv, /* * bo->mutex locked. + * Note that new_mem_flags are NOT transferred to the bo->mem.mask. */ -static int drm_bo_move_buffer(drm_buffer_object_t * bo, uint32_t new_flags, - int no_wait, int force_no_move) +int drm_bo_move_buffer(drm_buffer_object_t * bo, uint32_t new_mem_flags, + int no_wait, int move_unfenced) { + drm_device_t *dev = bo->dev; + drm_buffer_manager_t *bm = &dev->bm; int ret = 0; - + drm_bo_mem_reg_t mem; /* * Flush outstanding fences. */ + drm_bo_busy(bo); /* - * Make sure we're not mapped. + * Wait for outstanding fences. */ - ret = drm_bo_wait_unmapped(bo, no_wait); + ret = drm_bo_wait(bo, 0, 0, no_wait); if (ret) return ret; + mem.num_pages = bo->mem.num_pages; + mem.size = mem.num_pages << PAGE_SHIFT; + mem.mask = new_mem_flags; + mem.page_alignment = bo->mem.page_alignment; + + mutex_lock(&bm->evict_mutex); + mutex_lock(&dev->struct_mutex); + list_del(&bo->lru); + list_add_tail(&bo->lru, &bm->unfenced); + DRM_FLAG_MASKED(bo->priv_flags, _DRM_BO_FLAG_UNFENCED, + _DRM_BO_FLAG_UNFENCED); + mutex_unlock(&dev->struct_mutex); + /* - * Wait for outstanding fences. + * Determine where to move the buffer. */ + ret = drm_bo_mem_space(bo, &mem, no_wait); + if (ret) + goto out_unlock; - ret = drm_bo_wait(bo, 0, 0, no_wait); + ret = drm_bo_handle_move_mem(bo, &mem, 0, no_wait); - if (ret == -EINTR) - return -EAGAIN; - if (ret) - return ret; + out_unlock: + if (ret || !move_unfenced) { + mutex_lock(&dev->struct_mutex); + if (mem.mm_node) { + if (mem.mm_node != bo->pinned_node) + drm_mm_put_block(mem.mm_node); + mem.mm_node = NULL; + } + DRM_FLAG_MASKED(bo->priv_flags, 0, _DRM_BO_FLAG_UNFENCED); + DRM_WAKEUP(&bo->event_queue); + list_del(&bo->lru); + drm_bo_add_to_lru(bo); + mutex_unlock(&dev->struct_mutex); + } - if (new_flags & DRM_BO_FLAG_MEM_TT) { - ret = drm_move_local_to_tt(bo, no_wait); - if (ret) - return ret; - } else { - drm_move_tt_to_local(bo, 0, force_no_move); + mutex_unlock(&bm->evict_mutex); + return ret; +} + +static int drm_bo_mem_compat(drm_bo_mem_reg_t * mem) +{ + uint32_t flag_diff = (mem->mask ^ mem->flags); + + if ((mem->mask & mem->flags & DRM_BO_MASK_MEM) == 0) + return 0; + if ((flag_diff & DRM_BO_FLAG_CACHED) && + (!(mem->mask & DRM_BO_FLAG_CACHED) || + (mem->mask & DRM_BO_FLAG_FORCE_CACHING))) { + return 0; + } + if ((flag_diff & DRM_BO_FLAG_MAPPABLE) && + ((mem->mask & DRM_BO_FLAG_MAPPABLE) || + (mem->mask & DRM_BO_FLAG_FORCE_MAPPABLE))) + return 0; + return 1; +} + +static int drm_bo_check_fake(drm_device_t * dev, drm_bo_mem_reg_t * mem) +{ + drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man; + uint32_t num_prios = dev->driver->bo_driver->num_mem_type_prio; + const uint32_t *prios = dev->driver->bo_driver->mem_type_prio; + uint32_t i; + int type_ok = 0; + uint32_t mem_type = 0; + uint32_t cur_flags; + + if (drm_bo_mem_compat(mem)) + return 0; + + BUG_ON(mem->mm_node); + + for (i = 0; i < num_prios; ++i) { + mem_type = prios[i]; + man = &bm->man[mem_type]; + type_ok = drm_bo_mt_compatible(man, mem_type, mem->mask, + &cur_flags); + if (type_ok) + break; } - return 0; + if (type_ok) { + mem->mm_node = NULL; + mem->mem_type = mem_type; + mem->flags = cur_flags; + DRM_FLAG_MASKED(mem->flags, mem->mask, ~DRM_BO_MASK_MEMTYPE); + return 0; + } + + DRM_ERROR("Illegal fake buffer flags 0x%08x\n", mem->mask); + return -EINVAL; } /* @@ -1125,98 +1341,105 @@ static int drm_bo_move_buffer(drm_buffer_object_t * bo, uint32_t new_flags, */ static int drm_buffer_object_validate(drm_buffer_object_t * bo, - uint32_t new_flags, int move_unfenced, int no_wait) { drm_device_t *dev = bo->dev; drm_buffer_manager_t *bm = &dev->bm; - uint32_t flag_diff = (new_flags ^ bo->flags); drm_bo_driver_t *driver = dev->driver->bo_driver; - int ret; - if (new_flags & DRM_BO_FLAG_MEM_VRAM) { - DRM_ERROR("Vram support not implemented yet\n"); - return -EINVAL; - } - - DRM_DEBUG("New flags 0x%08x, Old flags 0x%08x\n", new_flags, bo->flags); - ret = driver->fence_type(new_flags, &bo->fence_class, &bo->fence_type); + DRM_DEBUG("New flags 0x%08x, Old flags 0x%08x\n", bo->mem.mask, + bo->mem.flags); + ret = + driver->fence_type(bo, &bo->fence_class, &bo->fence_type); if (ret) { DRM_ERROR("Driver did not support given buffer permissions\n"); return ret; } + ret = drm_bo_wait_unmapped(bo, no_wait); + if (ret) + return ret; + + if (bo->type == drm_bo_type_fake) { + ret = drm_bo_check_fake(dev, &bo->mem); + if (ret) + return ret; + } + /* - * Move out if we need to change caching policy. + * Check whether we need to move buffer. */ - if ((flag_diff & DRM_BO_FLAG_BIND_CACHED) && - !(bo->flags & DRM_BO_FLAG_MEM_LOCAL)) { - if (bo->flags & (DRM_BO_FLAG_NO_EVICT | DRM_BO_FLAG_NO_MOVE)) { - DRM_ERROR("Cannot change caching policy of " - "pinned buffer.\n"); - return -EINVAL; - } - ret = drm_bo_move_buffer(bo, DRM_BO_FLAG_MEM_LOCAL, no_wait, 0); + if (!drm_bo_mem_compat(&bo->mem)) { + ret = drm_bo_move_buffer(bo, bo->mem.mask, no_wait, + move_unfenced); if (ret) { if (ret != -EAGAIN) DRM_ERROR("Failed moving buffer.\n"); return ret; } } - DRM_MASK_VAL(bo->flags, DRM_BO_FLAG_BIND_CACHED, new_flags); - flag_diff = (new_flags ^ bo->flags); /* - * Check whether we dropped no_move policy, and in that case, - * release reserved manager regions. + * Pinned buffers. */ - if ((flag_diff & DRM_BO_FLAG_NO_MOVE) && - !(new_flags & DRM_BO_FLAG_NO_MOVE)) { + if (bo->mem.mask & (DRM_BO_FLAG_NO_EVICT | DRM_BO_FLAG_NO_MOVE)) { + bo->pinned_mem_type = bo->mem.mem_type; mutex_lock(&dev->struct_mutex); - if (bo->mm_node) { - drm_mm_put_block(bo->mm_node); - bo->mm_node = NULL; + list_del_init(&bo->pinned_lru); + drm_bo_add_to_pinned_lru(bo); + + if (bo->pinned_node != bo->mem.mm_node) { + if (bo->pinned_node != NULL) + drm_mm_put_block(bo->pinned_node); + bo->pinned_node = bo->mem.mm_node; } + mutex_unlock(&dev->struct_mutex); + + } else if (bo->pinned_node != NULL) { + + mutex_lock(&dev->struct_mutex); + drm_mm_put_block(bo->pinned_node); + list_del_init(&bo->pinned_lru); + bo->pinned_node = NULL; + mutex_unlock(&dev->struct_mutex); + } /* - * Check whether we need to move buffer. + * We might need to add a TTM. */ - if ((bo->type != drm_bo_type_fake) && (flag_diff & DRM_BO_MASK_MEM)) { - ret = drm_bo_move_buffer(bo, new_flags, no_wait, 1); - if (ret) { - if (ret != -EAGAIN) - DRM_ERROR("Failed moving buffer.\n"); + if (bo->mem.mem_type == DRM_BO_MEM_LOCAL && bo->ttm == NULL) { + ret = drm_bo_add_ttm(bo); + if (ret) return ret; - } } + DRM_FLAG_MASKED(bo->mem.flags, bo->mem.mask, ~DRM_BO_MASK_MEMTYPE); - if (move_unfenced) { - - /* - * Place on unfenced list. - */ + /* + * Finally, adjust lru to be sure. + */ + mutex_lock(&dev->struct_mutex); + list_del(&bo->lru); + if (move_unfenced) { + list_add_tail(&bo->lru, &bm->unfenced); DRM_FLAG_MASKED(bo->priv_flags, _DRM_BO_FLAG_UNFENCED, _DRM_BO_FLAG_UNFENCED); - mutex_lock(&dev->struct_mutex); - list_del(&bo->lru); - list_add_tail(&bo->lru, &bm->unfenced); - mutex_unlock(&dev->struct_mutex); } else { - - mutex_lock(&dev->struct_mutex); - list_del_init(&bo->lru); - drm_bo_add_to_lru(bo, bm); - mutex_unlock(&dev->struct_mutex); + drm_bo_add_to_lru(bo); + if (bo->priv_flags & _DRM_BO_FLAG_UNFENCED) { + DRM_WAKEUP(&bo->event_queue); + DRM_FLAG_MASKED(bo->priv_flags, 0, + _DRM_BO_FLAG_UNFENCED); + } } + mutex_unlock(&dev->struct_mutex); - bo->flags = new_flags; return 0; } @@ -1225,10 +1448,8 @@ static int drm_bo_handle_validate(drm_file_t * priv, uint32_t handle, drm_bo_arg_reply_t * rep) { drm_buffer_object_t *bo; - drm_device_t *dev = priv->head->dev; int ret; int no_wait = hint & DRM_BO_HINT_DONT_BLOCK; - uint32_t new_flags; bo = drm_lookup_buffer_object(priv, handle, 1); if (!bo) { @@ -1241,22 +1462,20 @@ static int drm_bo_handle_validate(drm_file_t * priv, uint32_t handle, if (ret) goto out; - ret = drm_bo_new_flags(dev, bo->flags, - (flags & mask) | (bo->mask & ~mask), hint, - 0, &new_flags, &bo->mask); - + DRM_FLAG_MASKED(flags, bo->mem.mask, ~mask); + ret = drm_bo_new_mask(bo, flags, hint); if (ret) goto out; ret = - drm_buffer_object_validate(bo, new_flags, - !(hint & DRM_BO_HINT_DONT_FENCE), + drm_buffer_object_validate(bo, !(hint & DRM_BO_HINT_DONT_FENCE), no_wait); drm_bo_fill_rep_arg(bo, rep); out: mutex_unlock(&bo->mutex); + drm_bo_usage_deref_unlocked(bo); return ret; } @@ -1307,90 +1526,6 @@ static int drm_bo_handle_wait(drm_file_t * priv, uint32_t handle, return ret; } -/* - * Call bo->mutex locked. - */ - -static int drm_bo_add_ttm(drm_file_t * priv, drm_buffer_object_t * bo) -{ - drm_device_t *dev = bo->dev; - drm_ttm_object_t *to = NULL; - int ret = 0; - uint32_t ttm_flags = 0; - - bo->ttm_object = NULL; - bo->ttm = NULL; - - switch (bo->type) { - case drm_bo_type_dc: - mutex_lock(&dev->struct_mutex); - ret = drm_ttm_object_create(dev, bo->num_pages * PAGE_SIZE, - ttm_flags, &to); - mutex_unlock(&dev->struct_mutex); - break; - case drm_bo_type_user: - case drm_bo_type_fake: - break; - default: - DRM_ERROR("Illegal buffer object type\n"); - ret = -EINVAL; - break; - } - - if (ret) { - return ret; - } - - if (to) { - bo->ttm_object = to; - bo->ttm = drm_ttm_from_object(to); - } - return ret; -} - -/* - * Transfer a buffer object's memory and LRU status to a newly - * created object. User-space references remains with the old - * object. Call bo->mutex locked. - */ - -int drm_buffer_object_transfer(drm_buffer_object_t *bo, - drm_buffer_object_t **new_obj) -{ - drm_buffer_object_t *fbo; - drm_device_t *dev = bo->dev; - drm_buffer_manager_t *bm = &dev->bm; - - fbo = drm_ctl_calloc(1, sizeof(*bo), DRM_MEM_BUFOBJ); - if (!fbo) - return -ENOMEM; - - *fbo = *bo; - mutex_init(&fbo->mutex); - mutex_lock(&fbo->mutex); - mutex_lock(&dev->struct_mutex); - - INIT_LIST_HEAD(&fbo->ddestroy); - INIT_LIST_HEAD(&fbo->lru); - list_splice_init(&bo->lru, &fbo->lru); - - bo->mm_node = NULL; - bo->ttm = NULL; - bo->ttm_object = NULL; - bo->fence = NULL; - bo->flags = 0; - - fbo->mm_node->private = (void *)fbo; - atomic_set(&fbo->usage, 1); - atomic_inc(&bm->count); - mutex_unlock(&dev->struct_mutex); - mutex_unlock(&fbo->mutex); - - *new_obj = fbo; - return 0; -} - - int drm_buffer_object_create(drm_file_t * priv, unsigned long size, drm_bo_type_t type, @@ -1404,7 +1539,6 @@ int drm_buffer_object_create(drm_file_t * priv, drm_buffer_manager_t *bm = &dev->bm; drm_buffer_object_t *bo; int ret = 0; - uint32_t new_flags; unsigned long num_pages; if ((buffer_start & ~PAGE_MASK) && (type != drm_bo_type_fake)) { @@ -1429,12 +1563,18 @@ int drm_buffer_object_create(drm_file_t * priv, atomic_set(&bo->mapped, -1); DRM_INIT_WAITQUEUE(&bo->event_queue); INIT_LIST_HEAD(&bo->lru); + INIT_LIST_HEAD(&bo->pinned_lru); INIT_LIST_HEAD(&bo->ddestroy); +#ifdef DRM_ODD_MM_COMPAT + INIT_LIST_HEAD(&bo->p_mm_list); + INIT_LIST_HEAD(&bo->vma_list); +#endif bo->dev = dev; bo->type = type; - bo->num_pages = num_pages; - bo->mm_node = NULL; - bo->page_alignment = page_alignment; + bo->mem.mem_type = DRM_BO_MEM_LOCAL; + bo->mem.num_pages = num_pages; + bo->mem.mm_node = NULL; + bo->mem.page_alignment = page_alignment; if (bo->type == drm_bo_type_fake) { bo->offset = buffer_start; bo->buffer_start = 0; @@ -1442,18 +1582,22 @@ int drm_buffer_object_create(drm_file_t * priv, bo->buffer_start = buffer_start; } bo->priv_flags = 0; - bo->flags = DRM_BO_FLAG_MEM_LOCAL | DRM_BO_FLAG_CACHED; + bo->mem.flags = 0; + bo->mem.mask = 0; atomic_inc(&bm->count); - ret = drm_bo_new_flags(dev, bo->flags, mask, hint, - 1, &new_flags, &bo->mask); - if (ret) - goto out_err; - ret = drm_bo_add_ttm(priv, bo); + ret = drm_bo_new_mask(bo, mask, hint); + if (ret) goto out_err; - ret = drm_buffer_object_validate(bo, new_flags, 0, - hint & DRM_BO_HINT_DONT_BLOCK); + if (bo->type == drm_bo_type_dc) { + mutex_lock(&dev->struct_mutex); + ret = drm_bo_setup_vm_locked(bo); + mutex_unlock(&dev->struct_mutex); + if (ret) + goto out_err; + } + ret = drm_buffer_object_validate(bo, 0, hint & DRM_BO_HINT_DONT_BLOCK); if (ret) goto out_err; @@ -1463,6 +1607,7 @@ int drm_buffer_object_create(drm_file_t * priv, out_err: mutex_unlock(&bo->mutex); + drm_bo_usage_deref_unlocked(bo); return ret; } @@ -1630,111 +1775,174 @@ int drm_bo_ioctl(DRM_IOCTL_ARGS) return 0; } +/** + *Clean the unfenced list and put on regular LRU. + *This is part of the memory manager cleanup and should only be + *called with the DRI lock held. + *Call dev->struct_sem locked. + */ + +static void drm_bo_clean_unfenced(drm_device_t *dev) +{ + drm_buffer_manager_t *bm = &dev->bm; + struct list_head *head, *list; + drm_buffer_object_t *entry; + + head = &bm->unfenced; + + list = head->next; + while(list != head) { + prefetch(list->next); + entry = list_entry(list, drm_buffer_object_t, lru); + + atomic_inc(&entry->usage); + mutex_unlock(&dev->struct_mutex); + mutex_lock(&entry->mutex); + mutex_lock(&dev->struct_mutex); + + list_del(&entry->lru); + DRM_FLAG_MASKED(entry->priv_flags, 0, _DRM_BO_FLAG_UNFENCED); + drm_bo_add_to_lru(entry); + mutex_unlock(&entry->mutex); + list = head->next; + } +} + +static int drm_bo_leave_list(drm_buffer_object_t * bo, + uint32_t mem_type, + int free_pinned, int allow_errors) +{ + drm_device_t *dev = bo->dev; + int ret = 0; + + mutex_lock(&bo->mutex); + + ret = drm_bo_expire_fence(bo, allow_errors); + if (ret) + goto out; + + if (free_pinned) { + DRM_FLAG_MASKED(bo->mem.flags, 0, DRM_BO_FLAG_NO_MOVE); + mutex_lock(&dev->struct_mutex); + list_del_init(&bo->pinned_lru); + if (bo->pinned_node == bo->mem.mm_node) + bo->pinned_node = NULL; + if (bo->pinned_node != NULL) { + drm_mm_put_block(bo->pinned_node); + bo->pinned_node = NULL; + } + mutex_unlock(&dev->struct_mutex); + } + + if (bo->mem.flags & DRM_BO_FLAG_NO_EVICT) { + DRM_ERROR("A DRM_BO_NO_EVICT buffer present at " + "cleanup. Removing flag and evicting.\n"); + bo->mem.flags &= ~DRM_BO_FLAG_NO_EVICT; + bo->mem.mask &= ~DRM_BO_FLAG_NO_EVICT; + } + + if (bo->mem.mem_type == mem_type) + ret = drm_bo_evict(bo, mem_type, 0); + + if (ret) { + if (allow_errors) { + goto out; + } else { + ret = 0; + DRM_ERROR("Cleanup eviction failed\n"); + } + } + + out: + mutex_unlock(&bo->mutex); + return ret; +} + + +static drm_buffer_object_t *drm_bo_entry(struct list_head *list, + int pinned_list) +{ + if (pinned_list) + return list_entry(list, drm_buffer_object_t, pinned_lru); + else + return list_entry(list, drm_buffer_object_t, lru); +} + /* - * dev->struct_sem locked. + * dev->struct_mutex locked. */ static int drm_bo_force_list_clean(drm_device_t * dev, struct list_head *head, unsigned mem_type, - int force_no_move, int allow_errors) + int free_pinned, + int allow_errors, + int pinned_list) { - drm_buffer_manager_t *bm = &dev->bm; struct list_head *list, *next, *prev; - drm_buffer_object_t *entry; + drm_buffer_object_t *entry, *nentry; int ret; - int clean; + int do_restart; - retry: - clean = 1; + /* + * The list traversal is a bit odd here, because an item may + * disappear from the list when we release the struct_mutex or + * when we decrease the usage count. Also we're not guaranteed + * to drain pinned lists, so we can't always restart. + */ + +restart: + nentry = NULL; list_for_each_safe(list, next, head) { prev = list->prev; - entry = list_entry(list, drm_buffer_object_t, lru); + + entry = (nentry != NULL) ? nentry: drm_bo_entry(list, pinned_list); atomic_inc(&entry->usage); - mutex_unlock(&dev->struct_mutex); - mutex_lock(&entry->mutex); - mutex_lock(&dev->struct_mutex); + if (nentry) { + atomic_dec(&nentry->usage); + nentry = NULL; + } - if (prev != list->prev || next != list->next) { - mutex_unlock(&entry->mutex); - drm_bo_usage_deref_locked(entry); - goto retry; + /* + * Protect the next item from destruction, so we can check + * its list pointers later on. + */ + + if (next != head) { + nentry = drm_bo_entry(next, pinned_list); + atomic_inc(&nentry->usage); } - if (entry->mm_node) { - clean = 0; + mutex_unlock(&dev->struct_mutex); - /* - * Expire the fence. - */ + ret = drm_bo_leave_list(entry, mem_type, free_pinned, + allow_errors); + mutex_lock(&dev->struct_mutex); - mutex_unlock(&dev->struct_mutex); - if (entry->fence && bm->nice_mode) { - unsigned long _end = jiffies + 3 * DRM_HZ; - do { - ret = drm_bo_wait(entry, 0, 1, 0); - if (ret && allow_errors) { - if (ret == -EINTR) - ret = -EAGAIN; - goto out_err; - } - } while (ret && !time_after_eq(jiffies, _end)); - - if (entry->fence) { - bm->nice_mode = 0; - DRM_ERROR("Detected GPU hang or " - "fence manager was taken down. " - "Evicting waiting buffers\n"); - } - } - if (entry->fence) { - drm_fence_usage_deref_unlocked(dev, - entry->fence); - entry->fence = NULL; - } + drm_bo_usage_deref_locked(entry); + if (ret) + return ret; - DRM_MASK_VAL(entry->priv_flags, _DRM_BO_FLAG_UNFENCED, - 0); + /* + * Has the next item disappeared from the list? + */ - if (force_no_move) { - DRM_MASK_VAL(entry->flags, DRM_BO_FLAG_NO_MOVE, - 0); - } - if (entry->flags & DRM_BO_FLAG_NO_EVICT) { - DRM_ERROR("A DRM_BO_NO_EVICT buffer present at " - "cleanup. Removing flag and evicting.\n"); - entry->flags &= ~DRM_BO_FLAG_NO_EVICT; - entry->mask &= ~DRM_BO_FLAG_NO_EVICT; - } + do_restart = ((next->prev != list) && (next->prev != prev)); - ret = drm_bo_evict(entry, mem_type, 1, force_no_move); - if (ret) { - if (allow_errors) { - goto out_err; - } else { - DRM_ERROR("Aargh. Eviction failed.\n"); - } - } - mutex_lock(&dev->struct_mutex); - } - mutex_unlock(&entry->mutex); - drm_bo_usage_deref_locked(entry); - if (prev != list->prev || next != list->next) { - goto retry; + if (nentry != NULL && do_restart) { + drm_bo_usage_deref_locked(nentry); + nentry = NULL; } + + if (do_restart) + goto restart; } - if (!clean) - goto retry; return 0; - out_err: - mutex_unlock(&entry->mutex); - drm_bo_usage_deref_unlocked(entry); - mutex_lock(&dev->struct_mutex); - return ret; } int drm_bo_clean_mm(drm_device_t * dev, unsigned mem_type) { drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man = &bm->man[mem_type]; int ret = -EINVAL; if (mem_type >= DRM_BO_MEM_TYPES) { @@ -1742,36 +1950,23 @@ int drm_bo_clean_mm(drm_device_t * dev, unsigned mem_type) return ret; } - if (!bm->has_type[mem_type]) { + if (!man->has_type) { DRM_ERROR("Trying to take down uninitialized " "memory manager type\n"); return ret; } - bm->use_type[mem_type] = 0; - bm->has_type[mem_type] = 0; + man->use_type = 0; + man->has_type = 0; ret = 0; if (mem_type > 0) { - /* - * Throw out unfenced buffers. - */ - - drm_bo_force_list_clean(dev, &bm->unfenced, mem_type, 1, 0); + drm_bo_clean_unfenced(dev); + drm_bo_force_list_clean(dev, &man->lru, mem_type, 1, 0, 0); + drm_bo_force_list_clean(dev, &man->pinned, mem_type, 1, 0, 1); - /* - * Throw out evicted no-move buffers. - */ - - drm_bo_force_list_clean(dev, &bm->pinned[DRM_BO_MEM_LOCAL], - mem_type, 1, 0); - drm_bo_force_list_clean(dev, &bm->lru[mem_type], mem_type, 1, - 0); - drm_bo_force_list_clean(dev, &bm->pinned[mem_type], mem_type, 1, - 0); - - if (drm_mm_clean(&bm->manager[mem_type])) { - drm_mm_takedown(&bm->manager[mem_type]); + if (drm_mm_clean(&man->manager)) { + drm_mm_takedown(&man->manager); } else { ret = -EBUSY; } @@ -1780,62 +1975,75 @@ int drm_bo_clean_mm(drm_device_t * dev, unsigned mem_type) return ret; } +/** + *Evict all buffers of a particular mem_type, but leave memory manager + *regions for NO_MOVE buffers intact. New buffers cannot be added at this + *point since we have the hardware lock. + */ + static int drm_bo_lock_mm(drm_device_t * dev, unsigned mem_type) { int ret; drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man = &bm->man[mem_type]; if (mem_type == 0 || mem_type >= DRM_BO_MEM_TYPES) { DRM_ERROR("Illegal memory manager memory type %u,\n", mem_type); return -EINVAL; } - ret = drm_bo_force_list_clean(dev, &bm->unfenced, mem_type, 0, 1); + drm_bo_clean_unfenced(dev); + ret = drm_bo_force_list_clean(dev, &man->lru, mem_type, 0, 1, 0); if (ret) return ret; - ret = drm_bo_force_list_clean(dev, &bm->lru[mem_type], mem_type, 0, 1); - if (ret) - return ret; - ret = - drm_bo_force_list_clean(dev, &bm->pinned[mem_type], mem_type, 0, 1); + ret = drm_bo_force_list_clean(dev, &man->pinned, mem_type, 0, 1, 1); + return ret; } -static int drm_bo_init_mm(drm_device_t * dev, - unsigned type, - unsigned long p_offset, unsigned long p_size) +int drm_bo_init_mm(drm_device_t * dev, + unsigned type, + unsigned long p_offset, unsigned long p_size) { drm_buffer_manager_t *bm = &dev->bm; int ret = -EINVAL; + drm_mem_type_manager_t *man; if (type >= DRM_BO_MEM_TYPES) { DRM_ERROR("Illegal memory type %d\n", type); return ret; } - if (bm->has_type[type]) { + + man = &bm->man[type]; + if (man->has_type) { DRM_ERROR("Memory manager already initialized for type %d\n", type); return ret; } + ret = dev->driver->bo_driver->init_mem_type(dev, type, man); + if (ret) + return ret; + ret = 0; if (type != DRM_BO_MEM_LOCAL) { if (!p_size) { DRM_ERROR("Zero size memory manager type %d\n", type); return ret; } - ret = drm_mm_init(&bm->manager[type], p_offset, p_size); + ret = drm_mm_init(&man->manager, p_offset, p_size); if (ret) return ret; } - bm->has_type[type] = 1; - bm->use_type[type] = 1; + man->has_type = 1; + man->use_type = 1; - INIT_LIST_HEAD(&bm->lru[type]); - INIT_LIST_HEAD(&bm->pinned[type]); + INIT_LIST_HEAD(&man->lru); + INIT_LIST_HEAD(&man->pinned); return 0; } +EXPORT_SYMBOL(drm_bo_init_mm); /* * This is called from lastclose, so we don't need to bother about @@ -1847,6 +2055,7 @@ int drm_bo_driver_finish(drm_device_t * dev) drm_buffer_manager_t *bm = &dev->bm; int ret = 0; unsigned i = DRM_BO_MEM_TYPES; + drm_mem_type_manager_t *man; mutex_lock(&dev->bm.init_mutex); mutex_lock(&dev->struct_mutex); @@ -1856,17 +2065,19 @@ int drm_bo_driver_finish(drm_device_t * dev) bm->initialized = 0; while (i--) { - if (bm->has_type[i]) { - bm->use_type[i] = 0; + man = &bm->man[i]; + if (man->has_type) { + man->use_type = 0; if ((i != DRM_BO_MEM_LOCAL) && drm_bo_clean_mm(dev, i)) { ret = -EBUSY; DRM_ERROR("DRM memory manager type %d " "is not clean.\n", i); } - bm->has_type[i] = 0; + man->has_type = 0; } } mutex_unlock(&dev->struct_mutex); + if (!cancel_delayed_work(&bm->wq)) { flush_scheduled_work(); } @@ -1875,10 +2086,10 @@ int drm_bo_driver_finish(drm_device_t * dev) if (list_empty(&bm->ddestroy)) { DRM_DEBUG("Delayed destroy list was clean\n"); } - if (list_empty(&bm->lru[0])) { + if (list_empty(&bm->man[0].lru)) { DRM_DEBUG("Swap list was clean\n"); } - if (list_empty(&bm->pinned[0])) { + if (list_empty(&bm->man[0].pinned)) { DRM_DEBUG("NO_MOVE list was clean\n"); } if (list_empty(&bm->unfenced)) { @@ -2006,3 +2217,144 @@ int drm_mm_init_ioctl(DRM_IOCTL_ARGS) DRM_COPY_TO_USER_IOCTL((void __user *)data, arg, sizeof(arg)); return 0; } + +/* + * buffer object vm functions. + */ + +int drm_mem_reg_is_pci(drm_device_t * dev, drm_bo_mem_reg_t * mem) +{ + drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man = &bm->man[mem->mem_type]; + + if (!(man->flags & _DRM_FLAG_MEMTYPE_FIXED)) { + if (mem->mem_type == DRM_BO_MEM_LOCAL) + return 0; + + if (man->flags & _DRM_FLAG_MEMTYPE_CMA) + return 0; + + if (mem->flags & DRM_BO_FLAG_CACHED) + return 0; + } + return 1; +} + +EXPORT_SYMBOL(drm_mem_reg_is_pci); + +/** + * \c Get the PCI offset for the buffer object memory. + * + * \param bo The buffer object. + * \param bus_base On return the base of the PCI region + * \param bus_offset On return the byte offset into the PCI region + * \param bus_size On return the byte size of the buffer object or zero if + * the buffer object memory is not accessible through a PCI region. + * \return Failure indication. + * + * Returns -EINVAL if the buffer object is currently not mappable. + * Otherwise returns zero. + */ + +int drm_bo_pci_offset(drm_device_t * dev, + drm_bo_mem_reg_t * mem, + unsigned long *bus_base, + unsigned long *bus_offset, unsigned long *bus_size) +{ + drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man = &bm->man[mem->mem_type]; + + *bus_size = 0; + if (!(man->flags & _DRM_FLAG_MEMTYPE_MAPPABLE)) + return -EINVAL; + + if (drm_mem_reg_is_pci(dev, mem)) { + *bus_offset = mem->mm_node->start << PAGE_SHIFT; + *bus_size = mem->num_pages << PAGE_SHIFT; + *bus_base = man->io_offset; + } + + return 0; +} + +/** + * \c Kill all user-space virtual mappings of this buffer object. + * + * \param bo The buffer object. + * + * Call bo->mutex locked. + */ + +void drm_bo_unmap_virtual(drm_buffer_object_t * bo) +{ + drm_device_t *dev = bo->dev; + loff_t offset = ((loff_t) bo->map_list.hash.key) << PAGE_SHIFT; + loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; + + unmap_mapping_range(dev->dev_mapping, offset, holelen, 1); +} + +static void drm_bo_takedown_vm_locked(drm_buffer_object_t * bo) +{ + drm_map_list_t *list = &bo->map_list; + drm_local_map_t *map; + drm_device_t *dev = bo->dev; + + if (list->user_token) { + drm_ht_remove_item(&dev->map_hash, &list->hash); + list->user_token = 0; + } + 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_ctl_free(map, sizeof(*map), DRM_MEM_BUFOBJ); + list->map = NULL; + list->user_token = 0ULL; + drm_bo_usage_deref_locked(bo); +} + +static int drm_bo_setup_vm_locked(drm_buffer_object_t * bo) +{ + drm_map_list_t *list = &bo->map_list; + drm_local_map_t *map; + drm_device_t *dev = bo->dev; + + list->map = drm_ctl_calloc(1, sizeof(*map), DRM_MEM_BUFOBJ); + if (!list->map) + return -ENOMEM; + + map = list->map; + map->offset = 0; + map->type = _DRM_TTM; + map->flags = _DRM_REMOVABLE; + map->size = bo->mem.num_pages * PAGE_SIZE; + atomic_inc(&bo->usage); + map->handle = (void *)bo; + + list->file_offset_node = drm_mm_search_free(&dev->offset_manager, + bo->mem.num_pages, 0, 0); + + if (!list->file_offset_node) { + drm_bo_takedown_vm_locked(bo); + return -ENOMEM; + } + + list->file_offset_node = drm_mm_get_block(list->file_offset_node, + bo->mem.num_pages, 0); + + list->hash.key = list->file_offset_node->start; + if (drm_ht_insert_item(&dev->map_hash, &list->hash)) { + drm_bo_takedown_vm_locked(bo); + return -ENOMEM; + } + + list->user_token = ((drm_u64_t) list->hash.key) << PAGE_SHIFT; + + return 0; +} diff --git a/linux-core/drm_bo_move.c b/linux-core/drm_bo_move.c new file mode 100644 index 00000000..4f752065 --- /dev/null +++ b/linux-core/drm_bo_move.c @@ -0,0 +1,411 @@ +/************************************************************************** + * + * Copyright (c) 2007 Tungsten Graphics, Inc., Cedar Park, TX., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" + +/** + * Free the old memory node unless it's a pinned region and we + * have not been requested to free also pinned regions. + */ + +static void drm_bo_free_old_node(drm_buffer_object_t * bo) +{ + drm_bo_mem_reg_t *old_mem = &bo->mem; + + if (old_mem->mm_node && (old_mem->mm_node != bo->pinned_node)) { + mutex_lock(&bo->dev->struct_mutex); + drm_mm_put_block(old_mem->mm_node); + old_mem->mm_node = NULL; + mutex_unlock(&bo->dev->struct_mutex); + } + old_mem->mm_node = NULL; +} + +int drm_bo_move_ttm(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem) +{ + drm_ttm_t *ttm = bo->ttm; + drm_bo_mem_reg_t *old_mem = &bo->mem; + uint32_t save_flags = old_mem->flags; + uint32_t save_mask = old_mem->mask; + int ret; + + if (old_mem->mem_type == DRM_BO_MEM_TT) { + if (evict) + drm_ttm_evict(ttm); + else + drm_ttm_unbind(ttm); + + drm_bo_free_old_node(bo); + DRM_FLAG_MASKED(old_mem->flags, + DRM_BO_FLAG_CACHED | DRM_BO_FLAG_MAPPABLE | + DRM_BO_FLAG_MEM_LOCAL, DRM_BO_MASK_MEMTYPE); + old_mem->mem_type = DRM_BO_MEM_LOCAL; + save_flags = old_mem->flags; + } + if (new_mem->mem_type != DRM_BO_MEM_LOCAL) { + ret = drm_bind_ttm(ttm, + new_mem->flags & DRM_BO_FLAG_CACHED, + new_mem->mm_node->start); + if (ret) + return ret; + } + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + old_mem->mask = save_mask; + DRM_FLAG_MASKED(save_flags, new_mem->flags, DRM_BO_MASK_MEMTYPE); + return 0; +} + +EXPORT_SYMBOL(drm_bo_move_ttm); + +/** + * \c Return a kernel virtual address to the buffer object PCI memory. + * + * \param bo The buffer object. + * \return Failure indication. + * + * Returns -EINVAL if the buffer object is currently not mappable. + * Returns -ENOMEM if the ioremap operation failed. + * Otherwise returns zero. + * + * After a successfull call, bo->iomap contains the virtual address, or NULL + * if the buffer object content is not accessible through PCI space. + * Call bo->mutex locked. + */ + +int drm_mem_reg_ioremap(drm_device_t * dev, drm_bo_mem_reg_t * mem, + void **virtual) +{ + drm_buffer_manager_t *bm = &dev->bm; + drm_mem_type_manager_t *man = &bm->man[mem->mem_type]; + unsigned long bus_offset; + unsigned long bus_size; + unsigned long bus_base; + int ret; + void *addr; + + *virtual = NULL; + ret = drm_bo_pci_offset(dev, mem, &bus_base, &bus_offset, &bus_size); + if (ret || bus_size == 0) + return ret; + + if (!(man->flags & _DRM_FLAG_NEEDS_IOREMAP)) + addr = (void *)(((u8 *) man->io_addr) + bus_offset); + else { + addr = ioremap_nocache(bus_base + bus_offset, bus_size); + if (!addr) + return -ENOMEM; + } + *virtual = addr; + return 0; +} + +/** + * \c Unmap mapping obtained using drm_bo_ioremap + * + * \param bo The buffer object. + * + * Call bo->mutex locked. + */ + +void drm_mem_reg_iounmap(drm_device_t * dev, drm_bo_mem_reg_t * mem, + void *virtual) +{ + drm_buffer_manager_t *bm; + drm_mem_type_manager_t *man; + + bm = &dev->bm; + man = &bm->man[mem->mem_type]; + + if (virtual && (man->flags & _DRM_FLAG_NEEDS_IOREMAP)) { + iounmap(virtual); + } +} + +static int drm_copy_io_page(void *dst, void *src, unsigned long page) +{ + uint32_t *dstP = + (uint32_t *) ((unsigned long)dst + (page << PAGE_SHIFT)); + uint32_t *srcP = + (uint32_t *) ((unsigned long)src + (page << PAGE_SHIFT)); + + int i; + for (i = 0; i < PAGE_SIZE / sizeof(uint32_t); ++i) + iowrite32(ioread32(srcP++), dstP++); + return 0; +} + +static int drm_copy_io_ttm_page(drm_ttm_t * ttm, void *src, unsigned long page) +{ + struct page *d = drm_ttm_get_page(ttm, page); + void *dst; + + if (!d) + return -ENOMEM; + + src = (void *)((unsigned long)src + (page << PAGE_SHIFT)); + dst = kmap(d); + if (!dst) + return -ENOMEM; + + memcpy_fromio(dst, src, PAGE_SIZE); + kunmap(d); + return 0; +} + +static int drm_copy_ttm_io_page(drm_ttm_t * ttm, void *dst, unsigned long page) +{ + struct page *s = drm_ttm_get_page(ttm, page); + void *src; + + if (!s) + return -ENOMEM; + + dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT)); + src = kmap(s); + if (!src) + return -ENOMEM; + + memcpy_toio(dst, src, PAGE_SIZE); + kunmap(s); + return 0; +} + +int drm_bo_move_memcpy(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem) +{ + drm_device_t *dev = bo->dev; + drm_mem_type_manager_t *man = &dev->bm.man[new_mem->mem_type]; + drm_ttm_t *ttm = bo->ttm; + drm_bo_mem_reg_t *old_mem = &bo->mem; + drm_bo_mem_reg_t old_copy = *old_mem; + void *old_iomap; + void *new_iomap; + int ret; + uint32_t save_flags = old_mem->flags; + uint32_t save_mask = old_mem->mask; + unsigned long i; + unsigned long page; + unsigned long add = 0; + int dir; + + ret = drm_mem_reg_ioremap(dev, old_mem, &old_iomap); + if (ret) + return ret; + ret = drm_mem_reg_ioremap(dev, new_mem, &new_iomap); + if (ret) + goto out; + + if (old_iomap == NULL && new_iomap == NULL) + goto out2; + if (old_iomap == NULL && ttm == NULL) + goto out2; + + add = 0; + dir = 1; + + if ((old_mem->mem_type == new_mem->mem_type) && + (new_mem->mm_node->start < + old_mem->mm_node->start + old_mem->mm_node->size)) { + dir = -1; + add = new_mem->num_pages - 1; + } + + for (i = 0; i < new_mem->num_pages; ++i) { + page = i * dir + add; + if (old_iomap == NULL) + ret = drm_copy_ttm_io_page(ttm, new_iomap, page); + else if (new_iomap == NULL) + ret = drm_copy_io_ttm_page(ttm, old_iomap, page); + else + ret = drm_copy_io_page(new_iomap, old_iomap, page); + if (ret) + goto out1; + } + mb(); + out2: + drm_bo_free_old_node(bo); + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + old_mem->mask = save_mask; + DRM_FLAG_MASKED(save_flags, new_mem->flags, DRM_BO_MASK_MEMTYPE); + + if ((man->flags & _DRM_FLAG_MEMTYPE_FIXED) && (ttm != NULL)) { + drm_ttm_unbind(ttm); + drm_destroy_ttm(ttm); + bo->ttm = NULL; + } + + out1: + drm_mem_reg_iounmap(dev, new_mem, new_iomap); + out: + drm_mem_reg_iounmap(dev, &old_copy, old_iomap); + return ret; +} + +EXPORT_SYMBOL(drm_bo_move_memcpy); + +/* + * Transfer a buffer object's memory and LRU status to a newly + * created object. User-space references remains with the old + * object. Call bo->mutex locked. + */ + +int drm_buffer_object_transfer(drm_buffer_object_t * bo, + drm_buffer_object_t ** new_obj) +{ + drm_buffer_object_t *fbo; + drm_device_t *dev = bo->dev; + drm_buffer_manager_t *bm = &dev->bm; + + fbo = drm_ctl_calloc(1, sizeof(*fbo), DRM_MEM_BUFOBJ); + if (!fbo) + return -ENOMEM; + + *fbo = *bo; + mutex_init(&fbo->mutex); + mutex_lock(&fbo->mutex); + mutex_lock(&dev->struct_mutex); + + DRM_INIT_WAITQUEUE(&bo->event_queue); + INIT_LIST_HEAD(&fbo->ddestroy); + INIT_LIST_HEAD(&fbo->lru); + INIT_LIST_HEAD(&fbo->pinned_lru); +#ifdef DRM_ODD_MM_COMPAT + INIT_LIST_HEAD(&fbo->vma_list); + INIT_LIST_HEAD(&fbo->p_mm_list); +#endif + + atomic_inc(&bo->fence->usage); + fbo->pinned_node = NULL; + fbo->mem.mm_node->private = (void *)fbo; + atomic_set(&fbo->usage, 1); + atomic_inc(&bm->count); + mutex_unlock(&dev->struct_mutex); + mutex_unlock(&fbo->mutex); + + *new_obj = fbo; + return 0; +} + +/* + * Since move is underway, we need to block signals in this function. + * We cannot restart until it has finished. + */ + +int drm_bo_move_accel_cleanup(drm_buffer_object_t * bo, + int evict, + int no_wait, + uint32_t fence_class, + uint32_t fence_type, + uint32_t fence_flags, drm_bo_mem_reg_t * new_mem) +{ + drm_device_t *dev = bo->dev; + drm_mem_type_manager_t *man = &dev->bm.man[new_mem->mem_type]; + drm_bo_mem_reg_t *old_mem = &bo->mem; + int ret; + uint32_t save_flags = old_mem->flags; + uint32_t save_mask = old_mem->mask; + drm_buffer_object_t *old_obj; + + if (bo->fence) + drm_fence_usage_deref_unlocked(dev, bo->fence); + ret = drm_fence_object_create(dev, fence_class, fence_type, + fence_flags | DRM_FENCE_FLAG_EMIT, + &bo->fence); + if (ret) + return ret; + +#ifdef DRM_ODD_MM_COMPAT + /* + * In this mode, we don't allow pipelining a copy blit, + * since the buffer will be accessible from user space + * the moment we return and rebuild the page tables. + * + * With normal vm operation, page tables are rebuilt + * on demand using fault(), which waits for buffer idle. + */ + if (1) +#else + if (evict || ((bo->mem.mm_node == bo->pinned_node) && + bo->mem.mm_node != NULL)) +#endif + { + ret = drm_bo_wait(bo, 0, 1, 0); + if (ret) + return ret; + + drm_bo_free_old_node(bo); + + if ((man->flags & _DRM_FLAG_MEMTYPE_FIXED) && (bo->ttm != NULL)) { + drm_ttm_unbind(bo->ttm); + drm_destroy_ttm(bo->ttm); + bo->ttm = NULL; + } + } else { + + /* This should help pipeline ordinary buffer moves. + * + * Hang old buffer memory on a new buffer object, + * and leave it to be released when the GPU + * operation has completed. + */ + + ret = drm_buffer_object_transfer(bo, &old_obj); + + if (ret) + return ret; + + if (!(man->flags & _DRM_FLAG_MEMTYPE_FIXED)) + old_obj->ttm = NULL; + else + bo->ttm = NULL; + + mutex_lock(&dev->struct_mutex); + list_del_init(&old_obj->lru); + DRM_FLAG_MASKED(bo->priv_flags, 0, _DRM_BO_FLAG_UNFENCED); + drm_bo_add_to_lru(old_obj); + + drm_bo_usage_deref_locked(old_obj); + mutex_unlock(&dev->struct_mutex); + + } + + *old_mem = *new_mem; + new_mem->mm_node = NULL; + old_mem->mask = save_mask; + DRM_FLAG_MASKED(save_flags, new_mem->flags, DRM_BO_MASK_MEMTYPE); + return 0; +} + +EXPORT_SYMBOL(drm_bo_move_accel_cleanup); diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c index 6bb58424..4825f0c0 100644 --- a/linux-core/drm_compat.c +++ b/linux-core/drm_compat.c @@ -79,54 +79,14 @@ pgprot_t vm_get_page_prot(unsigned long vm_flags) #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) /* - * vm code for kernels below 2,6,15 in which version a major vm write + * vm code for kernels below 2.6.15 in which version a major vm write * occured. This implement a simple straightforward * version similar to what's going to be - * in kernel 2.6.20+? + * in kernel 2.6.19+ + * Kernels below 2.6.15 use nopage whereas 2.6.19 and upwards use + * nopfn. */ -static int drm_pte_is_clear(struct vm_area_struct *vma, - unsigned long addr) -{ - struct mm_struct *mm = vma->vm_mm; - int ret = 1; - pte_t *pte; - pmd_t *pmd; - pud_t *pud; - pgd_t *pgd; - - - spin_lock(&mm->page_table_lock); - pgd = pgd_offset(mm, addr); - if (pgd_none(*pgd)) - goto unlock; - pud = pud_offset(pgd, addr); - if (pud_none(*pud)) - goto unlock; - pmd = pmd_offset(pud, addr); - if (pmd_none(*pmd)) - goto unlock; - pte = pte_offset_map(pmd, addr); - if (!pte) - goto unlock; - ret = pte_none(*pte); - pte_unmap(pte); - unlock: - spin_unlock(&mm->page_table_lock); - return ret; -} - -int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, - unsigned long pfn, pgprot_t pgprot) -{ - int ret; - if (!drm_pte_is_clear(vma, addr)) - return -EBUSY; - - ret = io_remap_pfn_range(vma, addr, pfn, PAGE_SIZE, pgprot); - return ret; -} - static struct { spinlock_t lock; struct page *dummy_page; @@ -160,7 +120,7 @@ void free_nopage_retry(void) } } -struct page *drm_vm_ttm_nopage(struct vm_area_struct *vma, +struct page *drm_bo_vm_nopage(struct vm_area_struct *vma, unsigned long address, int *type) { @@ -171,7 +131,7 @@ struct page *drm_vm_ttm_nopage(struct vm_area_struct *vma, data.address = address; data.vma = vma; - drm_vm_ttm_fault(vma, &data); + drm_bo_vm_fault(vma, &data); switch (data.type) { case VM_FAULT_OOM: return NOPAGE_OOM; @@ -186,10 +146,85 @@ struct page *drm_vm_ttm_nopage(struct vm_area_struct *vma, #endif +#if !defined(DRM_FULL_MM_COMPAT) && \ + ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))) + +static int drm_pte_is_clear(struct vm_area_struct *vma, + unsigned long addr) +{ + struct mm_struct *mm = vma->vm_mm; + int ret = 1; + pte_t *pte; + pmd_t *pmd; + pud_t *pud; + pgd_t *pgd; + + spin_lock(&mm->page_table_lock); + pgd = pgd_offset(mm, addr); + if (pgd_none(*pgd)) + goto unlock; + pud = pud_offset(pgd, addr); + if (pud_none(*pud)) + goto unlock; + pmd = pmd_offset(pud, addr); + if (pmd_none(*pmd)) + goto unlock; + pte = pte_offset_map(pmd, addr); + if (!pte) + goto unlock; + ret = pte_none(*pte); + pte_unmap(pte); + unlock: + spin_unlock(&mm->page_table_lock); + return ret; +} + +int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn) +{ + int ret; + if (!drm_pte_is_clear(vma, addr)) + return -EBUSY; + + ret = io_remap_pfn_range(vma, addr, pfn, PAGE_SIZE, vma->vm_page_prot); + return ret; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) && !defined(DRM_FULL_MM_COMPAT)) + +/** + * While waiting for the fault() handler to appear in + * we accomplish approximately + * the same wrapping it with nopfn. + */ + +unsigned long drm_bo_vm_nopfn(struct vm_area_struct * vma, + unsigned long address) +{ + struct fault_data data; + data.address = address; + + (void) drm_bo_vm_fault(vma, &data); + if (data.type == VM_FAULT_OOM) + return NOPFN_OOM; + else if (data.type == VM_FAULT_SIGBUS) + return NOPFN_SIGBUS; + + /* + * pfn already set. + */ + + return 0; +} +#endif + + #ifdef DRM_ODD_MM_COMPAT /* - * VM compatibility code for 2.6.15-2.6.19(?). This code implements a complicated + * VM compatibility code for 2.6.15-2.6.18. This code implements a complicated * workaround for a single BUG statement in do_no_page in these versions. The * tricky thing is that we need to take the mmap_sem in exclusive mode for _all_ * vmas mapping the ttm, before dev->struct_mutex is taken. The way we do this is to @@ -212,109 +247,100 @@ typedef struct vma_entry { } vma_entry_t; -struct page *drm_vm_ttm_nopage(struct vm_area_struct *vma, +struct page *drm_bo_vm_nopage(struct vm_area_struct *vma, unsigned long address, int *type) { - drm_local_map_t *map = (drm_local_map_t *) vma->vm_private_data; + drm_buffer_object_t *bo = (drm_buffer_object_t *) vma->vm_private_data; unsigned long page_offset; struct page *page; drm_ttm_t *ttm; - drm_buffer_manager_t *bm; drm_device_t *dev; - /* - * FIXME: Check can't map aperture flag. - */ + mutex_lock(&bo->mutex); if (type) *type = VM_FAULT_MINOR; - if (!map) - return NOPAGE_OOM; - - if (address > vma->vm_end) - return NOPAGE_SIGBUS; + if (address > vma->vm_end) { + page = NOPAGE_SIGBUS; + goto out_unlock; + } + + dev = bo->dev; - ttm = (drm_ttm_t *) map->offset; - dev = ttm->dev; - mutex_lock(&dev->struct_mutex); - drm_fixup_ttm_caching(ttm); - BUG_ON(ttm->page_flags & DRM_TTM_PAGE_UNCACHED); + if (drm_mem_reg_is_pci(dev, &bo->mem)) { + DRM_ERROR("Invalid compat nopage.\n"); + page = NOPAGE_SIGBUS; + goto out_unlock; + } - bm = &dev->bm; + ttm = bo->ttm; + drm_ttm_fixup_caching(ttm); page_offset = (address - vma->vm_start) >> PAGE_SHIFT; - page = ttm->pages[page_offset]; - + page = drm_ttm_get_page(ttm, page_offset); if (!page) { - if (drm_alloc_memctl(PAGE_SIZE)) { - page = NOPAGE_OOM; - goto out; - } - page = ttm->pages[page_offset] = - alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); - if (!page) { - drm_free_memctl(PAGE_SIZE); - page = NOPAGE_OOM; - goto out; - } - ++bm->cur_pages; - SetPageLocked(page); + page = NOPAGE_OOM; + goto out_unlock; } get_page(page); - out: - mutex_unlock(&dev->struct_mutex); +out_unlock: + mutex_unlock(&bo->mutex); return page; } -int drm_ttm_map_bound(struct vm_area_struct *vma) +int drm_bo_map_bound(struct vm_area_struct *vma) { - drm_local_map_t *map = (drm_local_map_t *)vma->vm_private_data; - drm_ttm_t *ttm = (drm_ttm_t *) map->offset; + drm_buffer_object_t *bo = (drm_buffer_object_t *)vma->vm_private_data; int ret = 0; - - if (ttm->page_flags & DRM_TTM_PAGE_UNCACHED) { - unsigned long pfn = ttm->aper_offset + - (ttm->be->aperture_base >> PAGE_SHIFT); - pgprot_t pgprot = drm_io_prot(ttm->be->drm_map_type, vma); - + unsigned long bus_base; + unsigned long bus_offset; + unsigned long bus_size; + + ret = drm_bo_pci_offset(bo->dev, &bo->mem, &bus_base, + &bus_offset, &bus_size); + BUG_ON(ret); + + if (bus_size) { + drm_mem_type_manager_t *man = &bo->dev->bm.man[bo->mem.mem_type]; + unsigned long pfn = (bus_base + bus_offset) >> PAGE_SHIFT; + pgprot_t pgprot = drm_io_prot(man->drm_bus_maptype, vma); ret = io_remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start, pgprot); } + return ret; } -int drm_ttm_add_vma(drm_ttm_t * ttm, struct vm_area_struct *vma) +int drm_bo_add_vma(drm_buffer_object_t * bo, struct vm_area_struct *vma) { p_mm_entry_t *entry, *n_entry; vma_entry_t *v_entry; - drm_local_map_t *map = (drm_local_map_t *) - vma->vm_private_data; struct mm_struct *mm = vma->vm_mm; - v_entry = drm_ctl_alloc(sizeof(*v_entry), DRM_MEM_TTM); + v_entry = drm_ctl_alloc(sizeof(*v_entry), DRM_MEM_BUFOBJ); if (!v_entry) { DRM_ERROR("Allocation of vma pointer entry failed\n"); return -ENOMEM; } v_entry->vma = vma; - map->handle = (void *) v_entry; - list_add_tail(&v_entry->head, &ttm->vma_list); - list_for_each_entry(entry, &ttm->p_mm_list, head) { + list_add_tail(&v_entry->head, &bo->vma_list); + + list_for_each_entry(entry, &bo->p_mm_list, head) { if (mm == entry->mm) { atomic_inc(&entry->refcount); return 0; } else if ((unsigned long)mm < (unsigned long)entry->mm) ; } - n_entry = drm_ctl_alloc(sizeof(*n_entry), DRM_MEM_TTM); + n_entry = drm_ctl_alloc(sizeof(*n_entry), DRM_MEM_BUFOBJ); if (!n_entry) { DRM_ERROR("Allocation of process mm pointer entry failed\n"); return -ENOMEM; @@ -328,29 +354,29 @@ int drm_ttm_add_vma(drm_ttm_t * ttm, struct vm_area_struct *vma) return 0; } -void drm_ttm_delete_vma(drm_ttm_t * ttm, struct vm_area_struct *vma) +void drm_bo_delete_vma(drm_buffer_object_t * bo, struct vm_area_struct *vma) { p_mm_entry_t *entry, *n; vma_entry_t *v_entry, *v_n; int found = 0; struct mm_struct *mm = vma->vm_mm; - list_for_each_entry_safe(v_entry, v_n, &ttm->vma_list, head) { + list_for_each_entry_safe(v_entry, v_n, &bo->vma_list, head) { if (v_entry->vma == vma) { found = 1; list_del(&v_entry->head); - drm_ctl_free(v_entry, sizeof(*v_entry), DRM_MEM_TTM); + drm_ctl_free(v_entry, sizeof(*v_entry), DRM_MEM_BUFOBJ); break; } } BUG_ON(!found); - list_for_each_entry_safe(entry, n, &ttm->p_mm_list, head) { + list_for_each_entry_safe(entry, n, &bo->p_mm_list, head) { if (mm == entry->mm) { if (atomic_add_negative(-1, &entry->refcount)) { list_del(&entry->head); BUG_ON(entry->locked); - drm_ctl_free(entry, sizeof(*entry), DRM_MEM_TTM); + drm_ctl_free(entry, sizeof(*entry), DRM_MEM_BUFOBJ); } return; } @@ -360,12 +386,12 @@ void drm_ttm_delete_vma(drm_ttm_t * ttm, struct vm_area_struct *vma) -int drm_ttm_lock_mm(drm_ttm_t * ttm) +int drm_bo_lock_kmm(drm_buffer_object_t * bo) { p_mm_entry_t *entry; int lock_ok = 1; - list_for_each_entry(entry, &ttm->p_mm_list, head) { + list_for_each_entry(entry, &bo->p_mm_list, head) { BUG_ON(entry->locked); if (!down_write_trylock(&entry->mm->mmap_sem)) { lock_ok = 0; @@ -377,7 +403,7 @@ int drm_ttm_lock_mm(drm_ttm_t * ttm) if (lock_ok) return 0; - list_for_each_entry(entry, &ttm->p_mm_list, head) { + list_for_each_entry(entry, &bo->p_mm_list, head) { if (!entry->locked) break; up_write(&entry->mm->mmap_sem); @@ -392,43 +418,40 @@ int drm_ttm_lock_mm(drm_ttm_t * ttm) return -EAGAIN; } -void drm_ttm_unlock_mm(drm_ttm_t * ttm) +void drm_bo_unlock_kmm(drm_buffer_object_t * bo) { p_mm_entry_t *entry; - list_for_each_entry(entry, &ttm->p_mm_list, head) { + list_for_each_entry(entry, &bo->p_mm_list, head) { BUG_ON(!entry->locked); up_write(&entry->mm->mmap_sem); entry->locked = 0; } } -int drm_ttm_remap_bound(drm_ttm_t *ttm) +int drm_bo_remap_bound(drm_buffer_object_t *bo) { vma_entry_t *v_entry; int ret = 0; - - list_for_each_entry(v_entry, &ttm->vma_list, head) { - ret = drm_ttm_map_bound(v_entry->vma); - if (ret) - break; + + if (drm_mem_reg_is_pci(bo->dev, &bo->mem)) { + list_for_each_entry(v_entry, &bo->vma_list, head) { + ret = drm_bo_map_bound(v_entry->vma); + if (ret) + break; + } } - drm_ttm_unlock_mm(ttm); return ret; } -void drm_ttm_finish_unmap(drm_ttm_t *ttm) +void drm_bo_finish_unmap(drm_buffer_object_t *bo) { vma_entry_t *v_entry; - - if (!(ttm->page_flags & DRM_TTM_PAGE_UNCACHED)) - return; - list_for_each_entry(v_entry, &ttm->vma_list, head) { + list_for_each_entry(v_entry, &bo->vma_list, head) { v_entry->vma->vm_flags &= ~VM_PFNMAP; } - drm_ttm_unlock_mm(ttm); } #endif diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index 3cb5d202..7741714a 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -97,8 +97,6 @@ #define __GFP_COMP 0 #endif -#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) static inline int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t pgprot) { @@ -154,15 +152,25 @@ static __inline__ void *kcalloc(size_t nmemb, size_t size, int flags) (tmp);}) #endif +#ifndef list_for_each_entry_safe_reverse +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) +#endif #include <linux/mm.h> #include <asm/page.h> -#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) && \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))) +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) && \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))) #define DRM_ODD_MM_COMPAT #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) +#define DRM_FULL_MM_COMPAT +#endif /* @@ -200,18 +208,23 @@ extern int drm_map_page_into_agp(struct page *page); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) extern struct page *get_nopage_retry(void); extern void free_nopage_retry(void); -struct fault_data; -extern struct page *drm_vm_ttm_fault(struct vm_area_struct *vma, - struct fault_data *data); #define NOPAGE_REFAULT get_nopage_retry() #endif +#if !defined(DRM_FULL_MM_COMPAT) && \ + ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))) -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) +struct fault_data; +extern struct page *drm_bo_vm_fault(struct vm_area_struct *vma, + struct fault_data *data); + +#endif +#ifndef DRM_FULL_MM_COMPAT /* - * Hopefully, real NOPAGE_RETRY functionality will be in 2.6.19. + * Hopefully, real NOPAGE_RETRY functionality will be in 2.6.19. * For now, just return a dummy page that we've allocated out of * static space. The page will be put by do_nopage() since we've already * filled out the pte. @@ -228,17 +241,21 @@ struct fault_data { extern int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, - unsigned long pfn, pgprot_t pgprot); - -extern struct page *drm_vm_ttm_nopage(struct vm_area_struct *vma, - unsigned long address, - int *type); + unsigned long pfn); -#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +extern struct page *drm_bo_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int *type); +#else +extern unsigned long drm_bo_vm_nopfn(struct vm_area_struct *vma, + unsigned long address); +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) */ +#endif /* ndef DRM_FULL_MM_COMPAT */ #ifdef DRM_ODD_MM_COMPAT -struct drm_ttm; +struct drm_buffer_object; /* @@ -246,14 +263,14 @@ struct drm_ttm; * process mm pointer to the ttm mm list. Needs the ttm mutex. */ -extern int drm_ttm_add_vma(struct drm_ttm * ttm, +extern int drm_bo_add_vma(struct drm_buffer_object * bo, struct vm_area_struct *vma); /* * Delete a vma and the corresponding mm pointer from the * ttm lists. Needs the ttm mutex. */ -extern void drm_ttm_delete_vma(struct drm_ttm * ttm, - struct vm_area_struct *vma); +extern void drm_bo_delete_vma(struct drm_buffer_object * bo, + struct vm_area_struct *vma); /* * Attempts to lock all relevant mmap_sems for a ttm, while @@ -262,12 +279,12 @@ extern void drm_ttm_delete_vma(struct drm_ttm * ttm, * schedule() and try again. */ -extern int drm_ttm_lock_mm(struct drm_ttm * ttm); +extern int drm_bo_lock_kmm(struct drm_buffer_object * bo); /* * Unlock all relevant mmap_sems for a ttm. */ -extern void drm_ttm_unlock_mm(struct drm_ttm * ttm); +extern void drm_bo_unlock_kmm(struct drm_buffer_object * bo); /* * If the ttm was bound to the aperture, this function shall be called @@ -277,7 +294,7 @@ extern void drm_ttm_unlock_mm(struct drm_ttm * ttm); * releases the mmap_sems for this ttm. */ -extern void drm_ttm_finish_unmap(struct drm_ttm *ttm); +extern void drm_bo_finish_unmap(struct drm_buffer_object *bo); /* * Remap all vmas of this ttm using io_remap_pfn_range. We cannot @@ -286,14 +303,14 @@ extern void drm_ttm_finish_unmap(struct drm_ttm *ttm); * releases the mmap_sems for this ttm. */ -extern int drm_ttm_remap_bound(struct drm_ttm *ttm); +extern int drm_bo_remap_bound(struct drm_buffer_object *bo); /* * Remap a vma for a bound ttm. Call with the ttm mutex held and * the relevant mmap_sem locked. */ -extern int drm_ttm_map_bound(struct vm_area_struct *vma); +extern int drm_bo_map_bound(struct vm_area_struct *vma); #endif #endif diff --git a/linux-core/drm_fence.c b/linux-core/drm_fence.c index 06d48255..6dd04a35 100644 --- a/linux-core/drm_fence.c +++ b/linux-core/drm_fence.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -11,6 +11,10 @@ * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL @@ -19,11 +23,6 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * **************************************************************************/ /* * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> @@ -35,21 +34,42 @@ * Typically called by the IRQ handler. */ -void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type) +void drm_fence_handler(drm_device_t * dev, uint32_t class, + uint32_t sequence, uint32_t type) { int wake = 0; uint32_t diff; uint32_t relevant; drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *fc = &fm->class[class]; drm_fence_driver_t *driver = dev->driver->fence_driver; - struct list_head *list, *prev; - drm_fence_object_t *fence; + struct list_head *head; + drm_fence_object_t *fence, *next; int found = 0; + int is_exe = (type & DRM_FENCE_TYPE_EXE); + int ge_last_exe; + + + + diff = (sequence - fc->exe_flush_sequence) & driver->sequence_mask; + + if (fc->pending_exe_flush && is_exe && diff < driver->wrap_diff) + fc->pending_exe_flush = 0; - if (list_empty(&fm->ring)) + diff = (sequence - fc->last_exe_flush) & driver->sequence_mask; + ge_last_exe = diff < driver->wrap_diff; + + if (ge_last_exe) + fc->pending_flush &= ~type; + + if (is_exe && ge_last_exe) { + fc->last_exe_flush = sequence; + } + + if (list_empty(&fc->ring)) return; - list_for_each_entry(fence, &fm->ring, ring) { + list_for_each_entry(fence, &fc->ring, ring) { diff = (sequence - fence->sequence) & driver->sequence_mask; if (diff > driver->wrap_diff) { found = 1; @@ -57,11 +77,11 @@ void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type) } } - list = (found) ? fence->ring.prev : fm->ring.prev; - prev = list->prev; + head = (found) ? &fence->ring : &fc->ring; - for (; list != &fm->ring; list = prev, prev = list->prev) { - fence = list_entry(list, drm_fence_object_t, ring); + list_for_each_entry_safe_reverse(fence, next, head, ring) { + if (&fence->ring == &fc->ring) + break; type |= fence->native_type; relevant = type & fence->type; @@ -78,7 +98,7 @@ void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type) ~(fence->signaled | fence->submitted_flush); if (relevant) { - fm->pending_flush |= relevant; + fc->pending_flush |= relevant; fence->submitted_flush = fence->flush_mask; } @@ -89,9 +109,9 @@ void drm_fence_handler(drm_device_t * dev, uint32_t sequence, uint32_t type) } } - + if (wake) { - DRM_WAKEUP(&fm->fence_queue); + DRM_WAKEUP(&fc->fence_queue); } } @@ -147,7 +167,7 @@ static void drm_fence_object_destroy(drm_file_t * priv, drm_fence_usage_deref_locked(dev, fence); } -static int fence_signaled(drm_device_t * dev, volatile +static int fence_signaled(drm_device_t * dev, drm_fence_object_t * fence, uint32_t mask, int poke_flush) { @@ -157,7 +177,7 @@ static int fence_signaled(drm_device_t * dev, volatile drm_fence_driver_t *driver = dev->driver->fence_driver; if (poke_flush) - driver->poke_flush(dev); + driver->poke_flush(dev, fence->class); read_lock_irqsave(&fm->lock, flags); signaled = (fence->type & mask & fence->signaled) == (fence->type & mask); @@ -166,52 +186,35 @@ static int fence_signaled(drm_device_t * dev, volatile return signaled; } -static void drm_fence_flush_exe(drm_fence_manager_t * fm, +static void drm_fence_flush_exe(drm_fence_class_manager_t * fc, drm_fence_driver_t * driver, uint32_t sequence) { uint32_t diff; - if (!fm->pending_exe_flush) { - volatile struct list_head *list; - - /* - * Last_exe_flush is invalid. Find oldest sequence. - */ - -/* list = fm->fence_types[_DRM_FENCE_TYPE_EXE];*/ - list = &fm->ring; - if (list->next == &fm->ring) { - return; - } else { - drm_fence_object_t *fence = - list_entry(list->next, drm_fence_object_t, ring); - fm->last_exe_flush = (fence->sequence - 1) & - driver->sequence_mask; - } - diff = (sequence - fm->last_exe_flush) & driver->sequence_mask; - if (diff >= driver->wrap_diff) - return; - fm->exe_flush_sequence = sequence; - fm->pending_exe_flush = 1; + if (!fc->pending_exe_flush) { + fc->exe_flush_sequence = sequence; + fc->pending_exe_flush = 1; } else { diff = - (sequence - fm->exe_flush_sequence) & driver->sequence_mask; + (sequence - fc->exe_flush_sequence) & driver->sequence_mask; if (diff < driver->wrap_diff) { - fm->exe_flush_sequence = sequence; + fc->exe_flush_sequence = sequence; } } } -int drm_fence_object_signaled(volatile drm_fence_object_t * fence, +int drm_fence_object_signaled(drm_fence_object_t * fence, uint32_t type) { return ((fence->signaled & type) == type); } int drm_fence_object_flush(drm_device_t * dev, - volatile drm_fence_object_t * fence, uint32_t type) + drm_fence_object_t * fence, + uint32_t type) { drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *fc = &fm->class[fence->class]; drm_fence_driver_t *driver = dev->driver->fence_driver; unsigned long flags; @@ -226,16 +229,16 @@ int drm_fence_object_flush(drm_device_t * dev, if (fence->submitted_flush == fence->signaled) { if ((fence->type & DRM_FENCE_TYPE_EXE) && !(fence->submitted_flush & DRM_FENCE_TYPE_EXE)) { - drm_fence_flush_exe(fm, driver, fence->sequence); + drm_fence_flush_exe(fc, driver, fence->sequence); fence->submitted_flush |= DRM_FENCE_TYPE_EXE; } else { - fm->pending_flush |= (fence->flush_mask & + fc->pending_flush |= (fence->flush_mask & ~fence->submitted_flush); fence->submitted_flush = fence->flush_mask; } } write_unlock_irqrestore(&fm->lock, flags); - driver->poke_flush(dev); + driver->poke_flush(dev, fence->class); return 0; } @@ -244,24 +247,35 @@ int drm_fence_object_flush(drm_device_t * dev, * wrapped around and reused. */ -void drm_fence_flush_old(drm_device_t * dev, uint32_t sequence) +void drm_fence_flush_old(drm_device_t * dev, uint32_t class, uint32_t sequence) { drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *fc = &fm->class[class]; drm_fence_driver_t *driver = dev->driver->fence_driver; uint32_t old_sequence; unsigned long flags; drm_fence_object_t *fence; uint32_t diff; + write_lock_irqsave(&fm->lock, flags); + old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask; + diff = (old_sequence - fc->last_exe_flush) & driver->sequence_mask; + + if ((diff < driver->wrap_diff) && !fc->pending_exe_flush) { + fc->pending_exe_flush = 1; + fc->exe_flush_sequence = sequence - (driver->flush_diff / 2); + } + write_unlock_irqrestore(&fm->lock, flags); + mutex_lock(&dev->struct_mutex); read_lock_irqsave(&fm->lock, flags); - if (fm->ring.next == &fm->ring) { + + if (list_empty(&fc->ring)) { read_unlock_irqrestore(&fm->lock, flags); mutex_unlock(&dev->struct_mutex); return; } - old_sequence = (sequence - driver->flush_diff) & driver->sequence_mask; - fence = list_entry(fm->ring.next, drm_fence_object_t, ring); + fence = list_entry(fc->ring.next, drm_fence_object_t, ring); atomic_inc(&fence->usage); mutex_unlock(&dev->struct_mutex); diff = (old_sequence - fence->sequence) & driver->sequence_mask; @@ -274,11 +288,40 @@ void drm_fence_flush_old(drm_device_t * dev, uint32_t sequence) EXPORT_SYMBOL(drm_fence_flush_old); +static int drm_fence_lazy_wait(drm_device_t *dev, + drm_fence_object_t *fence, + int ignore_signals, + uint32_t mask) +{ + drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *fc = &fm->class[fence->class]; + + unsigned long _end = jiffies + 3*DRM_HZ; + int ret = 0; + + do { + DRM_WAIT_ON(ret, fc->fence_queue, 3 * DRM_HZ, + fence_signaled(dev, fence, mask, 0)); + if (time_after_eq(jiffies, _end)) + break; + } while (ret == -EINTR && ignore_signals); + if (time_after_eq(jiffies, _end) && (ret != 0)) + ret = -EBUSY; + if (ret) { + if (ret == -EBUSY) { + DRM_ERROR("Fence timeout. " + "GPU lockup or fence driver was " + "taken down.\n"); + } + return ((ret == -EINTR) ? -EAGAIN : ret); + } + return 0; +} + int drm_fence_object_wait(drm_device_t * dev, - volatile drm_fence_object_t * fence, + drm_fence_object_t * fence, int lazy, int ignore_signals, uint32_t mask) { - drm_fence_manager_t *fm = &dev->fm; drm_fence_driver_t *driver = dev->driver->fence_driver; int ret = 0; unsigned long _end; @@ -299,44 +342,29 @@ int drm_fence_object_wait(drm_device_t * dev, if (lazy && driver->lazy_capable) { - do { - DRM_WAIT_ON(ret, fm->fence_queue, 3 * DRM_HZ, - fence_signaled(dev, fence, mask, 1)); - if (time_after_eq(jiffies, _end)) - break; - } while (ret == -EINTR && ignore_signals); - if (time_after_eq(jiffies, _end) && (ret != 0)) - ret = -EBUSY; - if (ret) { - if (ret == -EBUSY) { - DRM_ERROR("Fence timeout. " - "GPU lockup or fence driver was " - "taken down.\n"); - } - return ((ret == -EINTR) ? -EAGAIN : ret); - } - } else if ((fence->class == 0) && (mask & DRM_FENCE_TYPE_EXE) && - driver->lazy_capable) { + ret = drm_fence_lazy_wait(dev, fence, ignore_signals, mask); + if (ret) + return ret; - /* - * We use IRQ wait for EXE fence if available to gain - * CPU in some cases. - */ + } else { - do { - DRM_WAIT_ON(ret, fm->fence_queue, 3 * DRM_HZ, - fence_signaled(dev, fence, - DRM_FENCE_TYPE_EXE, 1)); - if (time_after_eq(jiffies, _end)) - break; - } while (ret == -EINTR && ignore_signals); - if (time_after_eq(jiffies, _end) && (ret != 0)) - ret = -EBUSY; - if (ret) - return ((ret == -EINTR) ? -EAGAIN : ret); - } + if (driver->has_irq(dev, fence->class, + DRM_FENCE_TYPE_EXE)) { + ret = drm_fence_lazy_wait(dev, fence, ignore_signals, + DRM_FENCE_TYPE_EXE); + if (ret) + return ret; + } - if (fence_signaled(dev, fence, mask, 0)) + if (driver->has_irq(dev, fence->class, + mask & ~DRM_FENCE_TYPE_EXE)) { + ret = drm_fence_lazy_wait(dev, fence, ignore_signals, + mask); + if (ret) + return ret; + } + } + if (drm_fence_object_signaled(fence, mask)) return 0; /* @@ -358,33 +386,38 @@ int drm_fence_object_wait(drm_device_t * dev, } int drm_fence_object_emit(drm_device_t * dev, drm_fence_object_t * fence, - uint32_t fence_flags, uint32_t type) + uint32_t fence_flags, uint32_t class, uint32_t type) { drm_fence_manager_t *fm = &dev->fm; drm_fence_driver_t *driver = dev->driver->fence_driver; + drm_fence_class_manager_t *fc = &fm->class[fence->class]; unsigned long flags; uint32_t sequence; uint32_t native_type; int ret; drm_fence_unring(dev, &fence->ring); - ret = driver->emit(dev, fence_flags, &sequence, &native_type); + ret = driver->emit(dev, class, fence_flags, &sequence, &native_type); if (ret) return ret; write_lock_irqsave(&fm->lock, flags); + fence->class = class; fence->type = type; fence->flush_mask = 0x00; fence->submitted_flush = 0x00; fence->signaled = 0x00; fence->sequence = sequence; fence->native_type = native_type; - list_add_tail(&fence->ring, &fm->ring); + if (list_empty(&fc->ring)) + fc->last_exe_flush = sequence - 1; + list_add_tail(&fence->ring, &fc->ring); write_unlock_irqrestore(&fm->lock, flags); return 0; } -static int drm_fence_object_init(drm_device_t * dev, uint32_t type, +static int drm_fence_object_init(drm_device_t * dev, uint32_t class, + uint32_t type, uint32_t fence_flags, drm_fence_object_t * fence) { @@ -398,7 +431,7 @@ static int drm_fence_object_init(drm_device_t * dev, uint32_t type, write_lock_irqsave(&fm->lock, flags); INIT_LIST_HEAD(&fence->ring); - fence->class = 0; + fence->class = class; fence->type = type; fence->flush_mask = 0; fence->submitted_flush = 0; @@ -406,7 +439,8 @@ static int drm_fence_object_init(drm_device_t * dev, uint32_t type, fence->sequence = 0; write_unlock_irqrestore(&fm->lock, flags); if (fence_flags & DRM_FENCE_FLAG_EMIT) { - ret = drm_fence_object_emit(dev, fence, fence_flags, type); + ret = drm_fence_object_emit(dev, fence, fence_flags, + fence->class, type); } return ret; } @@ -430,7 +464,7 @@ int drm_fence_add_user_object(drm_file_t * priv, drm_fence_object_t * fence, EXPORT_SYMBOL(drm_fence_add_user_object); -int drm_fence_object_create(drm_device_t * dev, uint32_t type, +int drm_fence_object_create(drm_device_t * dev, uint32_t class, uint32_t type, unsigned flags, drm_fence_object_t ** c_fence) { drm_fence_object_t *fence; @@ -440,7 +474,7 @@ int drm_fence_object_create(drm_device_t * dev, uint32_t type, fence = drm_ctl_alloc(sizeof(*fence), DRM_MEM_FENCE); if (!fence) return -ENOMEM; - ret = drm_fence_object_init(dev, type, flags, fence); + ret = drm_fence_object_init(dev, class, type, flags, fence); if (ret) { drm_fence_usage_deref_unlocked(dev, fence); return ret; @@ -456,22 +490,31 @@ EXPORT_SYMBOL(drm_fence_object_create); void drm_fence_manager_init(drm_device_t * dev) { drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *class; drm_fence_driver_t *fed = dev->driver->fence_driver; int i; + fm->lock = RW_LOCK_UNLOCKED; write_lock(&fm->lock); - INIT_LIST_HEAD(&fm->ring); - fm->pending_flush = 0; - DRM_INIT_WAITQUEUE(&fm->fence_queue); fm->initialized = 0; - if (fed) { - fm->initialized = 1; - atomic_set(&fm->count, 0); - for (i = 0; i < fed->no_types; ++i) { - fm->fence_types[i] = &fm->ring; - } + if (!fed) + goto out_unlock; + + fm->initialized = 1; + fm->num_classes = fed->num_classes; + BUG_ON(fm->num_classes > _DRM_FENCE_CLASSES); + + for (i=0; i<fm->num_classes; ++i) { + class = &fm->class[i]; + + INIT_LIST_HEAD(&class->ring); + class->pending_flush = 0; + DRM_INIT_WAITQUEUE(&class->fence_queue); } + + atomic_set(&fm->count, 0); + out_unlock: write_unlock(&fm->lock); } @@ -518,7 +561,8 @@ int drm_fence_ioctl(DRM_IOCTL_ARGS) case drm_fence_create: if (arg.flags & DRM_FENCE_FLAG_EMIT) LOCK_TEST_WITH_RETURN(dev, filp); - ret = drm_fence_object_create(dev, arg.type, arg.flags, &fence); + ret = drm_fence_object_create(dev, arg.class, + arg.type, arg.flags, &fence); if (ret) return ret; ret = drm_fence_add_user_object(priv, fence, @@ -581,7 +625,8 @@ int drm_fence_ioctl(DRM_IOCTL_ARGS) fence = drm_lookup_fence_object(priv, arg.handle); if (!fence) return -EINVAL; - ret = drm_fence_object_emit(dev, fence, arg.flags, arg.type); + ret = drm_fence_object_emit(dev, fence, arg.flags, arg.class, + arg.type); break; case drm_fence_buffers: if (!dev->bm.initialized) { diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 84e06c87..faf76726 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -427,38 +427,51 @@ int drm_release(struct inode *inode, struct file *filp) dev->open_count); if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) { - unsigned long _end = jiffies + DRM_HZ*3; - - do { - retcode = drm_kernel_take_hw_lock(filp); - } while(retcode && !time_after_eq(jiffies,_end)); - - if (!retcode) { + if (drm_i_have_hw_lock(filp)) { dev->driver->reclaim_buffers_locked(dev, filp); - - drm_lock_free(dev, &dev->lock.hw_lock->lock, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); } else { + unsigned long _end=jiffies + 3*DRM_HZ; + int locked = 0; + + drm_idlelock_take(&dev->lock); /* - * FIXME: This is not a good solution. We should perhaps associate the - * DRM lock with a process context, and check whether the current process - * holds the lock. Then we can run reclaim buffers locked anyway. + * Wait for a while. */ - DRM_ERROR("Reclaim buffers locked deadlock.\n" - "\tThis is probably a single thread having multiple\n" - "\tDRM file descriptors open either dying or" - " closing file descriptors\n" - "\twhile having the lock. I will not reclaim buffers.\n" - "\tLocking context is 0x%08x\n", - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + do{ + spin_lock(&dev->lock.spinlock); + locked = dev->lock.idle_has_lock; + spin_unlock(&dev->lock.spinlock); + if (locked) + break; + schedule(); + } while (!time_after_eq(jiffies, _end)); + + if (!locked) { + DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" + "\tdriver to use reclaim_buffers_idlelocked() instead.\n" + "\tI will go on reclaiming the buffers anyway.\n"); + } + + dev->driver->reclaim_buffers_locked(dev, filp); + drm_idlelock_release(&dev->lock); } - } else if (drm_i_have_hw_lock(filp)) { + } + + if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) { + + drm_idlelock_take(&dev->lock); + dev->driver->reclaim_buffers_idlelocked(dev, filp); + drm_idlelock_release(&dev->lock); + + } + + if (drm_i_have_hw_lock(filp)) { DRM_DEBUG("File %p released, freeing lock for context %d\n", filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); - drm_lock_free(dev, &dev->lock.hw_lock->lock, + drm_lock_free(&dev->lock, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); } diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index c365c08e..92cf7f5c 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -422,7 +422,7 @@ static void drm_locked_tasklet_func(unsigned long data) spin_lock_irqsave(&dev->tasklet_lock, irqflags); if (!dev->locked_tasklet_func || - !drm_lock_take(&dev->lock.hw_lock->lock, + !drm_lock_take(&dev->lock, DRM_KERNEL_CONTEXT)) { spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); return; @@ -433,7 +433,7 @@ static void drm_locked_tasklet_func(unsigned long data) dev->locked_tasklet_func(dev); - drm_lock_free(dev, &dev->lock.hw_lock->lock, + drm_lock_free(&dev->lock, DRM_KERNEL_CONTEXT); dev->locked_tasklet_func = NULL; diff --git a/linux-core/drm_lock.c b/linux-core/drm_lock.c index d11c570e..f02df36b 100644 --- a/linux-core/drm_lock.c +++ b/linux-core/drm_lock.c @@ -35,12 +35,6 @@ #include "drmP.h" -#if 0 -static int drm_lock_transfer(drm_device_t * dev, - __volatile__ unsigned int *lock, - unsigned int context); -#endif - static int drm_notifier(void *priv); /** @@ -83,6 +77,9 @@ int drm_lock(struct inode *inode, struct file *filp, return -EINVAL; add_wait_queue(&dev->lock.lock_queue, &entry); + spin_lock(&dev->lock.spinlock); + dev->lock.user_waiters++; + spin_unlock(&dev->lock.spinlock); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); if (!dev->lock.hw_lock) { @@ -90,7 +87,7 @@ int drm_lock(struct inode *inode, struct file *filp, ret = -EINTR; break; } - if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) { + if (drm_lock_take(&dev->lock, lock.context)) { dev->lock.filp = filp; dev->lock.lock_time = jiffies; atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); @@ -104,6 +101,9 @@ int drm_lock(struct inode *inode, struct file *filp, break; } } + spin_lock(&dev->lock.spinlock); + dev->lock.user_waiters--; + spin_unlock(&dev->lock.spinlock); __set_current_state(TASK_RUNNING); remove_wait_queue(&dev->lock.lock_queue, &entry); @@ -184,8 +184,7 @@ int drm_unlock(struct inode *inode, struct file *filp, if (dev->driver->kernel_context_switch_unlock) dev->driver->kernel_context_switch_unlock(dev); else { - if (drm_lock_free(dev, &dev->lock.hw_lock->lock, - lock.context)) { + if (drm_lock_free(&dev->lock,lock.context)) { /* FIXME: Should really bail out here. */ } } @@ -203,18 +202,26 @@ int drm_unlock(struct inode *inode, struct file *filp, * * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction. */ -int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) +int drm_lock_take(drm_lock_data_t *lock_data, + unsigned int context) { unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + spin_lock(&lock_data->spinlock); do { old = *lock; if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; - else - new = context | _DRM_LOCK_HELD | _DRM_LOCK_CONT; + else { + new = context | _DRM_LOCK_HELD | + ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ? + _DRM_LOCK_CONT : 0); + } prev = cmpxchg(lock, old, new); } while (prev != old); + spin_unlock(&lock_data->spinlock); + if (_DRM_LOCKING_CONTEXT(old) == context) { if (old & _DRM_LOCK_HELD) { if (context != DRM_KERNEL_CONTEXT) { @@ -224,14 +231,15 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) return 0; } } - if (new == (context | _DRM_LOCK_HELD | _DRM_LOCK_CONT)) { + + if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) { /* Have lock */ + return 1; } return 0; } -#if 0 /** * This takes a lock forcibly and hands it to context. Should ONLY be used * inside *_unlock to give lock to kernel before calling *_dma_schedule. @@ -244,13 +252,13 @@ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) * Resets the lock file pointer. * Marks the lock as held by the given context, via the \p cmpxchg instruction. */ -static int drm_lock_transfer(drm_device_t * dev, - __volatile__ unsigned int *lock, +static int drm_lock_transfer(drm_lock_data_t *lock_data, unsigned int context) { unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; - dev->lock.filp = NULL; + lock_data->filp = NULL; do { old = *lock; new = context | _DRM_LOCK_HELD; @@ -258,7 +266,6 @@ static int drm_lock_transfer(drm_device_t * dev, } while (prev != old); return 1; } -#endif /** * Free lock. @@ -271,10 +278,19 @@ static int drm_lock_transfer(drm_device_t * dev, * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task * waiting on the lock queue. */ -int drm_lock_free(drm_device_t * dev, - __volatile__ unsigned int *lock, unsigned int context) +int drm_lock_free(drm_lock_data_t *lock_data, unsigned int context) { unsigned int old, new, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + + spin_lock(&lock_data->spinlock); + if (lock_data->kernel_waiters != 0) { + drm_lock_transfer(lock_data, 0); + lock_data->idle_has_lock = 1; + spin_unlock(&lock_data->spinlock); + return 1; + } + spin_unlock(&lock_data->spinlock); do { old = *lock; @@ -287,7 +303,7 @@ int drm_lock_free(drm_device_t * dev, context, _DRM_LOCKING_CONTEXT(old)); return 1; } - wake_up_interruptible(&dev->lock.lock_queue); + wake_up_interruptible(&lock_data->lock_queue); return 0; } @@ -322,65 +338,66 @@ static int drm_notifier(void *priv) return 0; } -/* - * Can be used by drivers to take the hardware lock if necessary. - * (Waiting for idle before reclaiming buffers etc.) +/** + * This function returns immediately and takes the hw lock + * with the kernel context if it is free, otherwise it gets the highest priority when and if + * it is eventually released. + * + * This guarantees that the kernel will _eventually_ have the lock _unless_ it is held + * by a blocked process. (In the latter case an explicit wait for the hardware lock would cause + * a deadlock, which is why the "idlelock" was invented). + * + * This should be sufficient to wait for GPU idle without + * having to worry about starvation. */ -int drm_i_have_hw_lock(struct file *filp) +void drm_idlelock_take(drm_lock_data_t *lock_data) { - DRM_DEVICE; - - return (priv->lock_count && dev->lock.hw_lock && - _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && - dev->lock.filp == filp); -} + int ret = 0; -EXPORT_SYMBOL(drm_i_have_hw_lock); + spin_lock(&lock_data->spinlock); + lock_data->kernel_waiters++; + if (!lock_data->idle_has_lock) { -int drm_kernel_take_hw_lock(struct file *filp) -{ - DRM_DEVICE; + spin_unlock(&lock_data->spinlock); + ret = drm_lock_take(lock_data, DRM_KERNEL_CONTEXT); + spin_lock(&lock_data->spinlock); - int ret = 0; - unsigned long _end = jiffies + 3*DRM_HZ; - - if (!drm_i_have_hw_lock(filp)) { - - DECLARE_WAITQUEUE(entry, current); - - add_wait_queue(&dev->lock.lock_queue, &entry); - for (;;) { - __set_current_state(TASK_INTERRUPTIBLE); - if (!dev->lock.hw_lock) { - /* Device has been unregistered */ - ret = -EINTR; - break; - } - if (drm_lock_take(&dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT)) { - dev->lock.filp = filp; - dev->lock.lock_time = jiffies; - atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); - break; /* Got lock */ - } - /* Contention */ - if (time_after_eq(jiffies,_end)) { - ret = -EBUSY; - break; - } + if (ret == 1) + lock_data->idle_has_lock = 1; + } + spin_unlock(&lock_data->spinlock); +} +EXPORT_SYMBOL(drm_idlelock_take); - schedule_timeout(1); - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } +void drm_idlelock_release(drm_lock_data_t *lock_data) +{ + unsigned int old, prev; + volatile unsigned int *lock = &lock_data->hw_lock->lock; + + spin_lock(&lock_data->spinlock); + if (--lock_data->kernel_waiters == 0) { + if (lock_data->idle_has_lock) { + do { + old = *lock; + prev = cmpxchg(lock, old, DRM_KERNEL_CONTEXT); + } while (prev != old); + wake_up_interruptible(&lock_data->lock_queue); + lock_data->idle_has_lock = 0; } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&dev->lock.lock_queue, &entry); } - return ret; + spin_unlock(&lock_data->spinlock); } +EXPORT_SYMBOL(drm_idlelock_release); -EXPORT_SYMBOL(drm_kernel_take_hw_lock); +int drm_i_have_hw_lock(struct file *filp) +{ + DRM_DEVICE; + + return (priv->lock_count && dev->lock.hw_lock && + _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && + dev->lock.filp == filp); +} + +EXPORT_SYMBOL(drm_i_have_hw_lock); diff --git a/linux-core/drm_mm.c b/linux-core/drm_mm.c index 5889ee4d..634a1782 100644 --- a/linux-core/drm_mm.c +++ b/linux-core/drm_mm.c @@ -217,6 +217,7 @@ void drm_mm_put_block(drm_mm_node_t * cur) drm_ctl_free(cur, sizeof(*cur), DRM_MEM_MM); } } +EXPORT_SYMBOL(drm_mm_put_block); drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm, unsigned long size, diff --git a/linux-core/drm_object.c b/linux-core/drm_object.c index 0157329c..939cf0d7 100644 --- a/linux-core/drm_object.c +++ b/linux-core/drm_object.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -10,6 +10,10 @@ * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -18,13 +22,11 @@ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * * **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ #include "drmP.h" diff --git a/linux-core/drm_objects.h b/linux-core/drm_objects.h new file mode 100644 index 00000000..98228ada --- /dev/null +++ b/linux-core/drm_objects.h @@ -0,0 +1,470 @@ +/************************************************************************** + * + * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#ifndef _DRM_OBJECTS_H +#define _DRM_OJBECTS_H +#define DRM_HAS_TTM + +struct drm_device; + +/*************************************************** + * User space objects. (drm_object.c) + */ + +#define drm_user_object_entry(_ptr, _type, _member) container_of(_ptr, _type, _member) + +typedef enum { + drm_fence_type, + drm_buffer_type, + drm_ttm_type + /* + * Add other user space object types here. + */ +} drm_object_type_t; + +/* + * A user object is a structure that helps the drm give out user handles + * to kernel internal objects and to keep track of these objects so that + * they can be destroyed, for example when the user space process exits. + * Designed to be accessible using a user space 32-bit handle. + */ + +typedef struct drm_user_object { + drm_hash_item_t hash; + struct list_head list; + drm_object_type_t type; + atomic_t refcount; + int shareable; + drm_file_t *owner; + void (*ref_struct_locked) (drm_file_t * priv, + struct drm_user_object * obj, + drm_ref_t ref_action); + void (*unref) (drm_file_t * priv, struct drm_user_object * obj, + drm_ref_t unref_action); + void (*remove) (drm_file_t * priv, struct drm_user_object * obj); +} drm_user_object_t; + +/* + * A ref object is a structure which is used to + * keep track of references to user objects and to keep track of these + * references so that they can be destroyed for example when the user space + * process exits. Designed to be accessible using a pointer to the _user_ object. + */ + +typedef struct drm_ref_object { + drm_hash_item_t hash; + struct list_head list; + atomic_t refcount; + drm_ref_t unref_action; +} drm_ref_object_t; + +/** + * Must be called with the struct_mutex held. + */ + +extern int drm_add_user_object(drm_file_t * priv, drm_user_object_t * item, + int shareable); +/** + * Must be called with the struct_mutex held. + */ + +extern drm_user_object_t *drm_lookup_user_object(drm_file_t * priv, + uint32_t key); + +/* + * Must be called with the struct_mutex held. + * If "item" has been obtained by a call to drm_lookup_user_object. You may not + * release the struct_mutex before calling drm_remove_ref_object. + * This function may temporarily release the struct_mutex. + */ + +extern int drm_remove_user_object(drm_file_t * priv, drm_user_object_t * item); + +/* + * Must be called with the struct_mutex held. May temporarily release it. + */ + +extern int drm_add_ref_object(drm_file_t * priv, + drm_user_object_t * referenced_object, + drm_ref_t ref_action); + +/* + * Must be called with the struct_mutex held. + */ + +drm_ref_object_t *drm_lookup_ref_object(drm_file_t * priv, + drm_user_object_t * referenced_object, + drm_ref_t ref_action); +/* + * Must be called with the struct_mutex held. + * If "item" has been obtained by a call to drm_lookup_ref_object. You may not + * release the struct_mutex before calling drm_remove_ref_object. + * This function may temporarily release the struct_mutex. + */ + +extern void drm_remove_ref_object(drm_file_t * priv, drm_ref_object_t * item); +extern int drm_user_object_ref(drm_file_t * priv, uint32_t user_token, + drm_object_type_t type, + drm_user_object_t ** object); +extern int drm_user_object_unref(drm_file_t * priv, uint32_t user_token, + drm_object_type_t type); + +/*************************************************** + * Fence objects. (drm_fence.c) + */ + +typedef struct drm_fence_object { + drm_user_object_t base; + atomic_t usage; + + /* + * The below three fields are protected by the fence manager spinlock. + */ + + struct list_head ring; + int class; + uint32_t native_type; + uint32_t type; + uint32_t signaled; + uint32_t sequence; + uint32_t flush_mask; + uint32_t submitted_flush; +} drm_fence_object_t; + +#define _DRM_FENCE_CLASSES 8 +#define _DRM_FENCE_TYPE_EXE 0x00 + +typedef struct drm_fence_class_manager { + struct list_head ring; + uint32_t pending_flush; + wait_queue_head_t fence_queue; + int pending_exe_flush; + uint32_t last_exe_flush; + uint32_t exe_flush_sequence; +} drm_fence_class_manager_t; + +typedef struct drm_fence_manager { + int initialized; + rwlock_t lock; + drm_fence_class_manager_t class[_DRM_FENCE_CLASSES]; + uint32_t num_classes; + atomic_t count; +} drm_fence_manager_t; + +typedef struct drm_fence_driver { + uint32_t num_classes; + uint32_t wrap_diff; + uint32_t flush_diff; + uint32_t sequence_mask; + int lazy_capable; + int (*has_irq) (struct drm_device * dev, uint32_t class, + uint32_t flags); + int (*emit) (struct drm_device * dev, uint32_t class, uint32_t flags, + uint32_t * breadcrumb, uint32_t * native_type); + void (*poke_flush) (struct drm_device * dev, uint32_t class); +} drm_fence_driver_t; + +extern void drm_fence_handler(struct drm_device *dev, uint32_t class, + uint32_t sequence, uint32_t type); +extern void drm_fence_manager_init(struct drm_device *dev); +extern void drm_fence_manager_takedown(struct drm_device *dev); +extern void drm_fence_flush_old(struct drm_device *dev, uint32_t class, + uint32_t sequence); +extern int drm_fence_object_flush(struct drm_device *dev, + drm_fence_object_t * fence, uint32_t type); +extern int drm_fence_object_signaled(drm_fence_object_t * fence, uint32_t type); +extern void drm_fence_usage_deref_locked(struct drm_device *dev, + drm_fence_object_t * fence); +extern void drm_fence_usage_deref_unlocked(struct drm_device *dev, + drm_fence_object_t * fence); +extern int drm_fence_object_wait(struct drm_device *dev, + drm_fence_object_t * fence, + int lazy, int ignore_signals, uint32_t mask); +extern int drm_fence_object_create(struct drm_device *dev, uint32_t type, + uint32_t fence_flags, uint32_t class, + drm_fence_object_t ** c_fence); +extern int drm_fence_add_user_object(drm_file_t * priv, + drm_fence_object_t * fence, int shareable); +extern int drm_fence_ioctl(DRM_IOCTL_ARGS); + +/************************************************** + *TTMs + */ + +/* + * The ttm backend GTT interface. (In our case AGP). + * Any similar type of device (PCIE?) + * needs only to implement these functions to be usable with the "TTM" interface. + * The AGP backend implementation lives in drm_agpsupport.c + * basically maps these calls to available functions in agpgart. + * Each drm device driver gets an + * additional function pointer that creates these types, + * so that the device can choose the correct aperture. + * (Multiple AGP apertures, etc.) + * Most device drivers will let this point to the standard AGP implementation. + */ + +#define DRM_BE_FLAG_NEEDS_FREE 0x00000001 +#define DRM_BE_FLAG_BOUND_CACHED 0x00000002 + +typedef struct drm_ttm_backend { + void *private; + uint32_t flags; + uint32_t drm_map_type; + int (*needs_ub_cache_adjust) (struct drm_ttm_backend * backend); + int (*populate) (struct drm_ttm_backend * backend, + unsigned long num_pages, struct page ** pages); + void (*clear) (struct drm_ttm_backend * backend); + int (*bind) (struct drm_ttm_backend * backend, + unsigned long offset, int cached); + int (*unbind) (struct drm_ttm_backend * backend); + void (*destroy) (struct drm_ttm_backend * backend); +} drm_ttm_backend_t; + +typedef struct drm_ttm { + struct page **pages; + uint32_t page_flags; + unsigned long num_pages; + unsigned long aper_offset; + atomic_t vma_count; + struct drm_device *dev; + int destroy; + uint32_t mapping_offset; + drm_ttm_backend_t *be; + enum { + ttm_bound, + ttm_evicted, + ttm_unbound, + ttm_unpopulated, + } state; + +} drm_ttm_t; + +extern drm_ttm_t *drm_ttm_init(struct drm_device *dev, unsigned long size); +extern int drm_bind_ttm(drm_ttm_t * ttm, int cached, unsigned long aper_offset); +extern void drm_ttm_unbind(drm_ttm_t * ttm); +extern void drm_ttm_evict(drm_ttm_t * ttm); +extern void drm_ttm_fixup_caching(drm_ttm_t * ttm); +extern struct page *drm_ttm_get_page(drm_ttm_t * ttm, int index); + +/* + * Destroy a ttm. The user normally calls drmRmMap or a similar IOCTL to do this, + * which calls this function iff there are no vmas referencing it anymore. Otherwise it is called + * when the last vma exits. + */ + +extern int drm_destroy_ttm(drm_ttm_t * ttm); + +#define DRM_FLAG_MASKED(_old, _new, _mask) {\ +(_old) ^= (((_old) ^ (_new)) & (_mask)); \ +} + +#define DRM_TTM_MASK_FLAGS ((1 << PAGE_SHIFT) - 1) +#define DRM_TTM_MASK_PFN (0xFFFFFFFFU - DRM_TTM_MASK_FLAGS) + +/* + * Page flags. + */ + +#define DRM_TTM_PAGE_UNCACHED 0x01 +#define DRM_TTM_PAGE_USED 0x02 +#define DRM_TTM_PAGE_BOUND 0x04 +#define DRM_TTM_PAGE_PRESENT 0x08 +#define DRM_TTM_PAGE_VMALLOC 0x10 + +/*************************************************** + * Buffer objects. (drm_bo.c, drm_bo_move.c) + */ + +typedef struct drm_bo_mem_reg { + drm_mm_node_t *mm_node; + unsigned long size; + unsigned long num_pages; + uint32_t page_alignment; + uint32_t mem_type; + uint32_t flags; + uint32_t mask; +} drm_bo_mem_reg_t; + +typedef struct drm_buffer_object { + struct drm_device *dev; + drm_user_object_t base; + + /* + * If there is a possibility that the usage variable is zero, + * then dev->struct_mutext should be locked before incrementing it. + */ + + atomic_t usage; + unsigned long buffer_start; + drm_bo_type_t type; + unsigned long offset; + atomic_t mapped; + drm_bo_mem_reg_t mem; + + struct list_head lru; + struct list_head ddestroy; + + uint32_t fence_type; + uint32_t fence_class; + drm_fence_object_t *fence; + uint32_t priv_flags; + wait_queue_head_t event_queue; + struct mutex mutex; + + /* For pinned buffers */ + drm_mm_node_t *pinned_node; + uint32_t pinned_mem_type; + struct list_head pinned_lru; + + /* For vm */ + + drm_ttm_t *ttm; + drm_map_list_t map_list; + uint32_t memory_type; + unsigned long bus_offset; + uint32_t vm_flags; + void *iomap; + +#ifdef DRM_ODD_MM_COMPAT + /* dev->struct_mutex only protected. */ + struct list_head vma_list; + struct list_head p_mm_list; +#endif + +} drm_buffer_object_t; + +#define _DRM_BO_FLAG_UNFENCED 0x00000001 +#define _DRM_BO_FLAG_EVICTED 0x00000002 + +typedef struct drm_mem_type_manager { + int has_type; + int use_type; + drm_mm_t manager; + struct list_head lru; + struct list_head pinned; + uint32_t flags; + uint32_t drm_bus_maptype; + unsigned long io_offset; + unsigned long io_size; + void *io_addr; +} drm_mem_type_manager_t; + +#define _DRM_FLAG_MEMTYPE_FIXED 0x00000001 /* Fixed (on-card) PCI memory */ +#define _DRM_FLAG_MEMTYPE_MAPPABLE 0x00000002 /* Memory mappable */ +#define _DRM_FLAG_MEMTYPE_CACHED 0x00000004 /* Cached binding */ +#define _DRM_FLAG_NEEDS_IOREMAP 0x00000008 /* Fixed memory needs ioremap + before kernel access. */ +#define _DRM_FLAG_MEMTYPE_CMA 0x00000010 /* Can't map aperture */ +#define _DRM_FLAG_MEMTYPE_CSELECT 0x00000020 /* Select caching */ + +typedef struct drm_buffer_manager { + struct mutex init_mutex; + struct mutex evict_mutex; + int nice_mode; + int initialized; + drm_file_t *last_to_validate; + drm_mem_type_manager_t man[DRM_BO_MEM_TYPES]; + struct list_head unfenced; + struct list_head ddestroy; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + struct work_struct wq; +#else + struct delayed_work wq; +#endif + uint32_t fence_type; + unsigned long cur_pages; + atomic_t count; +} drm_buffer_manager_t; + +typedef struct drm_bo_driver { + const uint32_t *mem_type_prio; + const uint32_t *mem_busy_prio; + uint32_t num_mem_type_prio; + uint32_t num_mem_busy_prio; + drm_ttm_backend_t *(*create_ttm_backend_entry) + (struct drm_device * dev); + int (*fence_type) (struct drm_buffer_object *bo, uint32_t * class, uint32_t * type); + int (*invalidate_caches) (struct drm_device * dev, uint32_t flags); + int (*init_mem_type) (struct drm_device * dev, uint32_t type, + drm_mem_type_manager_t * man); + uint32_t(*evict_mask) (struct drm_buffer_object *bo); + int (*move) (struct drm_buffer_object * bo, + int evict, int no_wait, struct drm_bo_mem_reg * new_mem); +} drm_bo_driver_t; + +/* + * buffer objects (drm_bo.c) + */ + +extern int drm_bo_ioctl(DRM_IOCTL_ARGS); +extern int drm_mm_init_ioctl(DRM_IOCTL_ARGS); +extern int drm_bo_driver_finish(struct drm_device *dev); +extern int drm_bo_driver_init(struct drm_device *dev); +extern int drm_bo_pci_offset(struct drm_device *dev, + drm_bo_mem_reg_t * mem, + unsigned long *bus_base, + unsigned long *bus_offset, + unsigned long *bus_size); +extern int drm_mem_reg_is_pci(struct drm_device *dev, drm_bo_mem_reg_t * mem); + +extern void drm_bo_usage_deref_locked(drm_buffer_object_t * bo); +extern int drm_fence_buffer_objects(drm_file_t * priv, + struct list_head *list, + uint32_t fence_flags, + drm_fence_object_t * fence, + drm_fence_object_t ** used_fence); +extern void drm_bo_add_to_lru(drm_buffer_object_t * bo); +extern int drm_bo_wait(drm_buffer_object_t * bo, int lazy, int ignore_signals, + int no_wait); +extern int drm_bo_mem_space(drm_buffer_object_t * bo, + drm_bo_mem_reg_t * mem, int no_wait); +extern int drm_bo_move_buffer(drm_buffer_object_t * bo, uint32_t new_mem_flags, + int no_wait, int move_unfenced); + +/* + * Buffer object memory move helpers. + * drm_bo_move.c + */ + +extern int drm_bo_move_ttm(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem); +extern int drm_bo_move_memcpy(drm_buffer_object_t * bo, + int evict, + int no_wait, drm_bo_mem_reg_t * new_mem); +extern int drm_bo_move_accel_cleanup(drm_buffer_object_t * bo, + int evict, + int no_wait, + uint32_t fence_class, + uint32_t fence_type, + uint32_t fence_flags, + drm_bo_mem_reg_t * new_mem); + +#endif diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index 60123cdc..348cd2c6 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -63,10 +63,12 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev, spin_lock_init(&dev->count_lock); spin_lock_init(&dev->drw_lock); spin_lock_init(&dev->tasklet_lock); + spin_lock_init(&dev->lock.spinlock); init_timer(&dev->timer); mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); mutex_init(&dev->bm.init_mutex); + mutex_init(&dev->bm.evict_mutex); dev->pdev = pdev; dev->pci_device = pdev->device; diff --git a/linux-core/drm_ttm.c b/linux-core/drm_ttm.c index c17c41cb..e67719e2 100644 --- a/linux-core/drm_ttm.c +++ b/linux-core/drm_ttm.c @@ -1,6 +1,6 @@ /************************************************************************** * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA + * Copyright (c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -11,6 +11,10 @@ * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL @@ -18,13 +22,11 @@ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * * **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ #include "drmP.h" @@ -33,18 +35,17 @@ static void drm_ttm_ipi_handler(void *null) flush_agp_cache(); } -static void drm_ttm_cache_flush(void) +static void drm_ttm_cache_flush(void) { if (on_each_cpu(drm_ttm_ipi_handler, NULL, 1, 1) != 0) DRM_ERROR("Timed out waiting for drm cache flush.\n"); } - /* * Use kmalloc if possible. Otherwise fall back to vmalloc. */ -static void ttm_alloc_pages(drm_ttm_t *ttm) +static void ttm_alloc_pages(drm_ttm_t * ttm) { unsigned long size = ttm->num_pages * sizeof(*ttm->pages); ttm->pages = NULL; @@ -65,7 +66,7 @@ static void ttm_alloc_pages(drm_ttm_t *ttm) } } -static void ttm_free_pages(drm_ttm_t *ttm) +static void ttm_free_pages(drm_ttm_t * ttm) { unsigned long size = ttm->num_pages * sizeof(*ttm->pages); @@ -79,27 +80,24 @@ static void ttm_free_pages(drm_ttm_t *ttm) ttm->pages = NULL; } -/* - * Unmap all vma pages from vmas mapping this ttm. - */ - -static int unmap_vma_pages(drm_ttm_t * ttm) +static struct page *drm_ttm_alloc_page(void) { - drm_device_t *dev = ttm->dev; - loff_t offset = ((loff_t) ttm->mapping_offset) << PAGE_SHIFT; - loff_t holelen = ((loff_t) ttm->num_pages) << PAGE_SHIFT; + struct page *page; -#ifdef DRM_ODD_MM_COMPAT - int ret; - ret = drm_ttm_lock_mm(ttm); - if (ret) - return ret; -#endif - unmap_mapping_range(dev->dev_mapping, offset, holelen, 1); -#ifdef DRM_ODD_MM_COMPAT - drm_ttm_finish_unmap(ttm); + if (drm_alloc_memctl(PAGE_SIZE)) { + return NULL; + } + page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); + if (!page) { + drm_free_memctl(PAGE_SIZE); + return NULL; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) + SetPageLocked(page); +#else + SetPageReserved(page); #endif - return 0; + return page; } /* @@ -116,7 +114,7 @@ static int drm_set_caching(drm_ttm_t * ttm, int noncached) if ((ttm->page_flags & DRM_TTM_PAGE_UNCACHED) == noncached) return 0; - if (noncached) + if (noncached) drm_ttm_cache_flush(); for (i = 0; i < ttm->num_pages; ++i) { @@ -135,7 +133,7 @@ static int drm_set_caching(drm_ttm_t * ttm, int noncached) if (do_tlbflush) flush_agp_mappings(); - DRM_MASK_VAL(ttm->page_flags, DRM_TTM_PAGE_UNCACHED, noncached); + DRM_FLAG_MASKED(ttm->page_flags, noncached, DRM_TTM_PAGE_UNCACHED); return 0; } @@ -154,18 +152,6 @@ int drm_destroy_ttm(drm_ttm_t * ttm) if (!ttm) return 0; - if (atomic_read(&ttm->vma_count) > 0) { - ttm->destroy = 1; - DRM_ERROR("VMAs are still alive. Skipping destruction.\n"); - return -EBUSY; - } - - DRM_DEBUG("Destroying a ttm\n"); - -#ifdef DRM_TTM_ODD_COMPAT - BUG_ON(!list_empty(&ttm->vma_list)); - BUG_ON(!list_empty(&ttm->p_mm_list)); -#endif be = ttm->be; if (be) { be->destroy(be); @@ -193,11 +179,6 @@ int drm_destroy_ttm(drm_ttm_t * ttm) DRM_ERROR("Erroneous map count. " "Leaking page mappings.\n"); } - - /* - * End debugging. - */ - __free_page(*cur_page); drm_free_memctl(PAGE_SIZE); --bm->cur_pages; @@ -210,37 +191,36 @@ int drm_destroy_ttm(drm_ttm_t * ttm) return 0; } +struct page *drm_ttm_get_page(drm_ttm_t * ttm, int index) +{ + struct page *p; + drm_buffer_manager_t *bm = &ttm->dev->bm; + + p = ttm->pages[index]; + if (!p) { + p = drm_ttm_alloc_page(); + if (!p) + return NULL; + ttm->pages[index] = p; + ++bm->cur_pages; + } + return p; +} + static int drm_ttm_populate(drm_ttm_t * ttm) { struct page *page; unsigned long i; - drm_buffer_manager_t *bm; drm_ttm_backend_t *be; if (ttm->state != ttm_unpopulated) return 0; - bm = &ttm->dev->bm; be = ttm->be; for (i = 0; i < ttm->num_pages; ++i) { - page = ttm->pages[i]; - if (!page) { - if (drm_alloc_memctl(PAGE_SIZE)) { - return -ENOMEM; - } - page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); - if (!page) { - drm_free_memctl(PAGE_SIZE); - return -ENOMEM; - } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) - SetPageLocked(page); -#else - SetPageReserved(page); -#endif - ttm->pages[i] = page; - ++bm->cur_pages; - } + page = drm_ttm_get_page(ttm, i); + if (!page) + return -ENOMEM; } be->populate(be, ttm->num_pages, ttm->pages); ttm->state = ttm_unbound; @@ -251,7 +231,7 @@ static int drm_ttm_populate(drm_ttm_t * ttm) * Initialize a ttm. */ -static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) +drm_ttm_t *drm_ttm_init(struct drm_device * dev, unsigned long size) { drm_bo_driver_t *bo_driver = dev->driver->bo_driver; drm_ttm_t *ttm; @@ -263,11 +243,6 @@ static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) if (!ttm) return NULL; -#ifdef DRM_ODD_MM_COMPAT - INIT_LIST_HEAD(&ttm->p_mm_list); - INIT_LIST_HEAD(&ttm->vma_list); -#endif - ttm->dev = dev; atomic_set(&ttm->vma_count, 0); @@ -300,29 +275,20 @@ static drm_ttm_t *drm_init_ttm(struct drm_device *dev, unsigned long size) * Unbind a ttm region from the aperture. */ -int drm_evict_ttm(drm_ttm_t * ttm) +void drm_ttm_evict(drm_ttm_t * ttm) { drm_ttm_backend_t *be = ttm->be; int ret; - switch (ttm->state) { - case ttm_bound: - if (be->needs_ub_cache_adjust(be)) { - ret = unmap_vma_pages(ttm); - if (ret) { - return ret; - } - } - be->unbind(be); - break; - default: - break; + if (ttm->state == ttm_bound) { + ret = be->unbind(be); + BUG_ON(ret); } + ttm->state = ttm_evicted; - return 0; } -void drm_fixup_ttm_caching(drm_ttm_t * ttm) +void drm_ttm_fixup_caching(drm_ttm_t * ttm) { if (ttm->state == ttm_evicted) { @@ -334,18 +300,12 @@ void drm_fixup_ttm_caching(drm_ttm_t * ttm) } } -int drm_unbind_ttm(drm_ttm_t * ttm) +void drm_ttm_unbind(drm_ttm_t * ttm) { - int ret = 0; - if (ttm->state == ttm_bound) - ret = drm_evict_ttm(ttm); - - if (ret) - return ret; + drm_ttm_evict(ttm); - drm_fixup_ttm_caching(ttm); - return 0; + drm_ttm_fixup_caching(ttm); } int drm_bind_ttm(drm_ttm_t * ttm, int cached, unsigned long aper_offset) @@ -364,26 +324,13 @@ int drm_bind_ttm(drm_ttm_t * ttm, int cached, unsigned long aper_offset) ret = drm_ttm_populate(ttm); if (ret) return ret; - if (ttm->state == ttm_unbound && !cached) { - ret = unmap_vma_pages(ttm); - if (ret) - return ret; + if (ttm->state == ttm_unbound && !cached) { drm_set_caching(ttm, DRM_TTM_PAGE_UNCACHED); } -#ifdef DRM_ODD_MM_COMPAT - else if (ttm->state == ttm_evicted && !cached) { - ret = drm_ttm_lock_mm(ttm); - if (ret) - return ret; - } -#endif + if ((ret = be->bind(be, aper_offset, cached))) { ttm->state = ttm_evicted; -#ifdef DRM_ODD_MM_COMPAT - if (be->needs_ub_cache_adjust(be)) - drm_ttm_unlock_mm(ttm); -#endif DRM_ERROR("Couldn't bind backend.\n"); return ret; } @@ -391,130 +338,7 @@ int drm_bind_ttm(drm_ttm_t * ttm, int cached, unsigned long aper_offset) ttm->aper_offset = aper_offset; ttm->state = ttm_bound; -#ifdef DRM_ODD_MM_COMPAT - if (be->needs_ub_cache_adjust(be)) { - ret = drm_ttm_remap_bound(ttm); - if (ret) - return ret; - } -#endif - return 0; } -/* - * dev->struct_mutex locked. - */ -static void drm_ttm_object_remove(drm_device_t * dev, drm_ttm_object_t * object) -{ - drm_map_list_t *list = &object->map_list; - drm_local_map_t *map; - - if (list->user_token) - 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) { - drm_ttm_t *ttm = (drm_ttm_t *) map->offset; - if (ttm) { - if (drm_destroy_ttm(ttm) != -EBUSY) { - drm_ctl_free(map, sizeof(*map), DRM_MEM_TTM); - } - } else { - drm_ctl_free(map, sizeof(*map), DRM_MEM_TTM); - } - } - - drm_ctl_free(object, sizeof(*object), DRM_MEM_TTM); -} - -void drm_ttm_object_deref_locked(drm_device_t * dev, drm_ttm_object_t * to) -{ - if (atomic_dec_and_test(&to->usage)) { - drm_ttm_object_remove(dev, to); - } -} - -void drm_ttm_object_deref_unlocked(drm_device_t * dev, drm_ttm_object_t * to) -{ - if (atomic_dec_and_test(&to->usage)) { - mutex_lock(&dev->struct_mutex); - if (atomic_read(&to->usage) == 0) - drm_ttm_object_remove(dev, to); - mutex_unlock(&dev->struct_mutex); - } -} - -/* - * Create a ttm and add it to the drm book-keeping. - * dev->struct_mutex locked. - */ - -int drm_ttm_object_create(drm_device_t * dev, unsigned long size, - uint32_t flags, drm_ttm_object_t ** ttm_object) -{ - drm_ttm_object_t *object; - drm_map_list_t *list; - drm_local_map_t *map; - drm_ttm_t *ttm; - - object = drm_ctl_calloc(1, sizeof(*object), DRM_MEM_TTM); - if (!object) - return -ENOMEM; - object->flags = flags; - list = &object->map_list; - - list->map = drm_ctl_calloc(1, sizeof(*map), DRM_MEM_TTM); - if (!list->map) { - drm_ttm_object_remove(dev, object); - return -ENOMEM; - } - map = list->map; - - ttm = drm_init_ttm(dev, size); - if (!ttm) { - DRM_ERROR("Could not create ttm\n"); - drm_ttm_object_remove(dev, object); - return -ENOMEM; - } - - map->offset = (unsigned long)ttm; - map->type = _DRM_TTM; - map->flags = _DRM_REMOVABLE; - map->size = ttm->num_pages * PAGE_SIZE; - map->handle = (void *)object; - - /* - * Add a one-page "hole" to the block size to avoid the mm subsystem - * merging vmas. - * FIXME: Is this really needed? - */ - - list->file_offset_node = drm_mm_search_free(&dev->offset_manager, - ttm->num_pages + 1, 0, 0); - if (!list->file_offset_node) { - drm_ttm_object_remove(dev, object); - return -ENOMEM; - } - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - ttm->num_pages + 1, 0); - - list->hash.key = list->file_offset_node->start; - - if (drm_ht_insert_item(&dev->map_hash, &list->hash)) { - drm_ttm_object_remove(dev, object); - return -ENOMEM; - } - - list->user_token = ((drm_u64_t) list->hash.key) << PAGE_SHIFT; - ttm->mapping_offset = list->hash.key; - atomic_set(&object->usage, 1); - *ttm_object = object; - return 0; -} +EXPORT_SYMBOL(drm_bind_ttm); diff --git a/linux-core/drm_ttm.h b/linux-core/drm_ttm.h deleted file mode 100644 index 796f2317..00000000 --- a/linux-core/drm_ttm.h +++ /dev/null @@ -1,146 +0,0 @@ -/************************************************************************** - * - * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * - **************************************************************************/ -/* - * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> - */ - -#ifndef _DRM_TTM_H -#define _DRM_TTM_H -#define DRM_HAS_TTM - -/* - * The backend GART interface. (In our case AGP). Any similar type of device (PCIE?) - * needs only to implement these functions to be usable with the "TTM" interface. - * The AGP backend implementation lives in drm_agpsupport.c - * basically maps these calls to available functions in agpgart. Each drm device driver gets an - * additional function pointer that creates these types, - * so that the device can choose the correct aperture. - * (Multiple AGP apertures, etc.) - * Most device drivers will let this point to the standard AGP implementation. - */ - -#define DRM_BE_FLAG_NEEDS_FREE 0x00000001 -#define DRM_BE_FLAG_BOUND_CACHED 0x00000002 -#define DRM_BE_FLAG_CBA 0x00000004 - -typedef struct drm_ttm_backend { - unsigned long aperture_base; - void *private; - uint32_t flags; - uint32_t drm_map_type; - int (*needs_ub_cache_adjust) (struct drm_ttm_backend * backend); - int (*populate) (struct drm_ttm_backend * backend, - unsigned long num_pages, struct page ** pages); - void (*clear) (struct drm_ttm_backend * backend); - int (*bind) (struct drm_ttm_backend * backend, - unsigned long offset, int cached); - int (*unbind) (struct drm_ttm_backend * backend); - void (*destroy) (struct drm_ttm_backend * backend); -} drm_ttm_backend_t; - -typedef struct drm_ttm { - struct page **pages; - uint32_t page_flags; - unsigned long num_pages; - unsigned long aper_offset; - atomic_t vma_count; - struct drm_device *dev; - int destroy; - uint32_t mapping_offset; - drm_ttm_backend_t *be; - enum { - ttm_bound, - ttm_evicted, - ttm_unbound, - ttm_unpopulated, - } state; -#ifdef DRM_ODD_MM_COMPAT - struct list_head vma_list; - struct list_head p_mm_list; -#endif - -} drm_ttm_t; - -typedef struct drm_ttm_object { - atomic_t usage; - uint32_t flags; - drm_map_list_t map_list; -} drm_ttm_object_t; - -extern int drm_ttm_object_create(struct drm_device *dev, unsigned long size, - uint32_t flags, - drm_ttm_object_t ** ttm_object); -extern void drm_ttm_object_deref_locked(struct drm_device *dev, - drm_ttm_object_t * to); -extern void drm_ttm_object_deref_unlocked(struct drm_device *dev, - drm_ttm_object_t * to); -extern drm_ttm_object_t *drm_lookup_ttm_object(drm_file_t * priv, - uint32_t handle, - int check_owner); -extern int drm_bind_ttm(drm_ttm_t * ttm, int cached, unsigned long aper_offset); - -extern int drm_unbind_ttm(drm_ttm_t * ttm); - -/* - * Evict a ttm region. Keeps Aperture caching policy. - */ - -extern int drm_evict_ttm(drm_ttm_t * ttm); -extern void drm_fixup_ttm_caching(drm_ttm_t * ttm); - -/* - * Destroy a ttm. The user normally calls drmRmMap or a similar IOCTL to do this, - * which calls this function iff there are no vmas referencing it anymore. Otherwise it is called - * when the last vma exits. - */ - -extern int drm_destroy_ttm(drm_ttm_t * ttm); -extern int drm_ttm_ioctl(DRM_IOCTL_ARGS); - -static __inline__ drm_ttm_t *drm_ttm_from_object(drm_ttm_object_t * to) -{ - return (drm_ttm_t *) to->map_list.map->offset; -} - -#define DRM_MASK_VAL(dest, mask, val) \ - (dest) = ((dest) & ~(mask)) | ((val) & (mask)); - -#define DRM_TTM_MASK_FLAGS ((1 << PAGE_SHIFT) - 1) -#define DRM_TTM_MASK_PFN (0xFFFFFFFFU - DRM_TTM_MASK_FLAGS) - -/* - * Page flags. - */ - -#define DRM_TTM_PAGE_UNCACHED 0x01 -#define DRM_TTM_PAGE_USED 0x02 -#define DRM_TTM_PAGE_BOUND 0x04 -#define DRM_TTM_PAGE_PRESENT 0x08 -#define DRM_TTM_PAGE_VMALLOC 0x10 - -#endif diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c index 827a7bdb..f3b1088f 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -41,9 +41,9 @@ static void drm_vm_open(struct vm_area_struct *vma); static void drm_vm_close(struct vm_area_struct *vma); -static void drm_vm_ttm_close(struct vm_area_struct *vma); -static int drm_vm_ttm_open(struct vm_area_struct *vma); -static void drm_vm_ttm_open_wrapper(struct vm_area_struct *vma); +static int drm_bo_mmap_locked(struct vm_area_struct *vma, + struct file *filp, + drm_local_map_t *map); pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma) @@ -159,96 +159,6 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma, } #endif /* __OS_HAS_AGP */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) || \ - LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)) -static -#endif -struct page *drm_vm_ttm_fault(struct vm_area_struct *vma, - struct fault_data *data) -{ - unsigned long address = data->address; - drm_local_map_t *map = (drm_local_map_t *) vma->vm_private_data; - unsigned long page_offset; - struct page *page; - drm_ttm_t *ttm; - drm_buffer_manager_t *bm; - drm_device_t *dev; - unsigned long pfn; - int err; - pgprot_t pgprot; - - if (!map) { - data->type = VM_FAULT_OOM; - return NULL; - } - - if (address > vma->vm_end) { - data->type = VM_FAULT_SIGBUS; - return NULL; - } - - ttm = (drm_ttm_t *) map->offset; - - dev = ttm->dev; - - /* - * Perhaps retry here? - */ - - mutex_lock(&dev->struct_mutex); - drm_fixup_ttm_caching(ttm); - - bm = &dev->bm; - page_offset = (address - vma->vm_start) >> PAGE_SHIFT; - page = ttm->pages[page_offset]; - - if (!page) { - if (drm_alloc_memctl(PAGE_SIZE)) { - data->type = VM_FAULT_OOM; - goto out; - } - page = ttm->pages[page_offset] = - alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA32); - if (!page) { - drm_free_memctl(PAGE_SIZE); - data->type = VM_FAULT_OOM; - goto out; - } - ++bm->cur_pages; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)) - SetPageLocked(page); -#else - SetPageReserved(page); -#endif - } - - if (ttm->page_flags & DRM_TTM_PAGE_UNCACHED) { - - /* - * FIXME: Check can't map aperture flag. - */ - - pfn = ttm->aper_offset + page_offset + - (ttm->be->aperture_base >> PAGE_SHIFT); - pgprot = drm_io_prot(ttm->be->drm_map_type, vma); - } else { - pfn = page_to_pfn(page); - pgprot = vma->vm_page_prot; - } - - err = vm_insert_pfn(vma, address, pfn, pgprot); - - if (!err || err == -EBUSY) - data->type = VM_FAULT_MINOR; - else - data->type = VM_FAULT_OOM; - out: - mutex_unlock(&dev->struct_mutex); - return NULL; -} -#endif - /** * \c nopage method for shared virtual memory. * @@ -508,20 +418,6 @@ static struct vm_operations_struct drm_vm_sg_ops = { .close = drm_vm_close, }; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) -static struct vm_operations_struct drm_vm_ttm_ops = { - .nopage = drm_vm_ttm_nopage, - .open = drm_vm_ttm_open_wrapper, - .close = drm_vm_ttm_close, -}; -#else -static struct vm_operations_struct drm_vm_ttm_ops = { - .fault = drm_vm_ttm_fault, - .open = drm_vm_ttm_open_wrapper, - .close = drm_vm_ttm_close, -}; -#endif - /** * \c open method for shared virtual memory. * @@ -530,7 +426,7 @@ static struct vm_operations_struct drm_vm_ttm_ops = { * Create a new drm_vma_entry structure as the \p vma private data entry and * add it to drm_device::vmalist. */ -static void drm_vm_open(struct vm_area_struct *vma) +static void drm_vm_open_locked(struct vm_area_struct *vma) { drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->head->dev; @@ -542,36 +438,21 @@ static void drm_vm_open(struct vm_area_struct *vma) vma_entry = drm_ctl_alloc(sizeof(*vma_entry), DRM_MEM_VMAS); if (vma_entry) { - mutex_lock(&dev->struct_mutex); vma_entry->vma = vma; vma_entry->next = dev->vmalist; vma_entry->pid = current->pid; dev->vmalist = vma_entry; - mutex_unlock(&dev->struct_mutex); } } -static int drm_vm_ttm_open(struct vm_area_struct *vma) { - - drm_local_map_t *map = (drm_local_map_t *)vma->vm_private_data; - drm_ttm_t *ttm; +static void drm_vm_open(struct vm_area_struct *vma) +{ drm_file_t *priv = vma->vm_file->private_data; drm_device_t *dev = priv->head->dev; - drm_vm_open(vma); mutex_lock(&dev->struct_mutex); - ttm = (drm_ttm_t *) map->offset; - atomic_inc(&ttm->vma_count); -#ifdef DRM_ODD_MM_COMPAT - drm_ttm_add_vma(ttm, vma); -#endif + drm_vm_open_locked(vma); mutex_unlock(&dev->struct_mutex); - return 0; -} - -static void drm_vm_ttm_open_wrapper(struct vm_area_struct *vma) -{ - drm_vm_ttm_open(vma); } /** @@ -608,34 +489,6 @@ static void drm_vm_close(struct vm_area_struct *vma) } -static void drm_vm_ttm_close(struct vm_area_struct *vma) -{ - drm_local_map_t *map = (drm_local_map_t *) vma->vm_private_data; - drm_ttm_t *ttm; - drm_device_t *dev; - int ret; - - drm_vm_close(vma); - if (map) { - ttm = (drm_ttm_t *) map->offset; - dev = ttm->dev; - mutex_lock(&dev->struct_mutex); -#ifdef DRM_ODD_MM_COMPAT - drm_ttm_delete_vma(ttm, vma); -#endif - if (atomic_dec_and_test(&ttm->vma_count)) { - if (ttm->destroy) { - ret = drm_destroy_ttm(ttm); - BUG_ON(ret); - drm_ctl_free(map, sizeof(*map), DRM_MEM_TTM); - } - } - mutex_unlock(&dev->struct_mutex); - } - return; -} - - /** * mmap DMA memory. * @@ -653,7 +506,6 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) drm_device_dma_t *dma; unsigned long length = vma->vm_end - vma->vm_start; - lock_kernel(); dev = priv->head->dev; dma = dev->dma; DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n", @@ -661,10 +513,8 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) /* Length must match exact page count */ if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { - unlock_kernel(); return -EINVAL; } - unlock_kernel(); if (!capable(CAP_SYS_ADMIN) && (dma->flags & _DRM_DMA_USE_PCI_RO)) { @@ -686,7 +536,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_RESERVED; /* Don't swap */ vma->vm_file = filp; /* Needed for drm_vm_open() */ - drm_vm_open(vma); + drm_vm_open_locked(vma); return 0; } @@ -719,7 +569,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs); * according to the mapping type and remaps the pages. Finally sets the file * pointer and calls vm_open(). */ -int drm_mmap(struct file *filp, struct vm_area_struct *vma) +static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; @@ -737,6 +587,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) * the AGP mapped at physical address 0 * --BenH. */ + if (!vma->vm_pgoff #if __OS_HAS_AGP && (!dev->agp @@ -833,27 +684,254 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_private_data = (void *)map; vma->vm_flags |= VM_RESERVED; break; - case _DRM_TTM: { - vma->vm_ops = &drm_vm_ttm_ops; - vma->vm_private_data = (void *) map; - vma->vm_file = filp; - vma->vm_flags |= VM_RESERVED | VM_IO; -#ifdef DRM_ODD_MM_COMPAT - mutex_lock(&dev->struct_mutex); - drm_ttm_map_bound(vma); - mutex_unlock(&dev->struct_mutex); -#endif - if (drm_vm_ttm_open(vma)) - return -EAGAIN; - return 0; - } + case _DRM_TTM: + return drm_bo_mmap_locked(vma, filp, map); default: return -EINVAL; /* This should never happen. */ } vma->vm_flags |= VM_RESERVED; /* Don't swap */ vma->vm_file = filp; /* Needed for drm_vm_open() */ - drm_vm_open(vma); + drm_vm_open_locked(vma); return 0; } + +int drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + int ret; + + mutex_lock(&dev->struct_mutex); + ret = drm_mmap_locked(filp, vma); + mutex_unlock(&dev->struct_mutex); + + return ret; +} EXPORT_SYMBOL(drm_mmap); + +/** + * buffer object vm functions. + */ + +/** + * \c Pagefault method for buffer objects. + * + * \param vma Virtual memory area. + * \param data Fault data on failure or refault. + * \return Always NULL as we insert pfns directly. + * + * It's important that pfns are inserted while holding the bo->mutex lock. + * otherwise we might race with unmap_mapping_range() which is always + * called with the bo->mutex lock held. + * + * It's not pretty to modify the vma->vm_page_prot variable while not + * holding the mm semaphore in write mode. However, we have it i read mode, + * so we won't be racing with any other writers, and we only actually modify + * it when no ptes are present so it shouldn't be a big deal. + */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) || \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +#ifdef DRM_FULL_MM_COMPAT +static +#endif +struct page *drm_bo_vm_fault(struct vm_area_struct *vma, + struct fault_data *data) +{ + unsigned long address = data->address; + drm_buffer_object_t *bo = (drm_buffer_object_t *) vma->vm_private_data; + unsigned long page_offset; + struct page *page = NULL; + drm_ttm_t *ttm; + drm_device_t *dev; + unsigned long pfn; + int err; + unsigned long bus_base; + unsigned long bus_offset; + unsigned long bus_size; + + + mutex_lock(&bo->mutex); + + err = drm_bo_wait(bo, 0, 0, 0); + if (err) { + data->type = (err == -EAGAIN) ? + VM_FAULT_MINOR : VM_FAULT_SIGBUS; + goto out_unlock; + } + + + /* + * If buffer happens to be in a non-mappable location, + * move it to a mappable. + */ + +#ifdef DRM_BO_FULL_COMPAT + if (!(bo->mem.flags & DRM_BO_FLAG_MAPPABLE)) { + uint32_t new_mask = bo->mem.mask | + DRM_BO_FLAG_MAPPABLE | + DRM_BO_FLAG_FORCE_MAPPABLE; + err = drm_bo_move_buffer(bo, new_mask, 0, 0); + + if (err) { + data->type = (err == -EAGAIN) ? + VM_FAULT_MINOR : VM_FAULT_SIGBUS; + goto out_unlock; + } + } +#else + if (!(bo->mem.flags & DRM_BO_FLAG_MAPPABLE)) { + unsigned long _end = jiffies + 3*DRM_HZ; + uint32_t new_mask = bo->mem.mask | + DRM_BO_FLAG_MAPPABLE | + DRM_BO_FLAG_FORCE_MAPPABLE; + + do { + err = drm_bo_move_buffer(bo, new_mask, 0, 0); + } while((err == -EAGAIN) && !time_after_eq(jiffies, _end)); + + if (err) { + DRM_ERROR("Timeout moving buffer to mappable location.\n"); + data->type = VM_FAULT_SIGBUS; + goto out_unlock; + } + } +#endif + + if (address > vma->vm_end) { + data->type = VM_FAULT_SIGBUS; + goto out_unlock; + } + + dev = bo->dev; + err = drm_bo_pci_offset(dev, &bo->mem, &bus_base, &bus_offset, + &bus_size); + + if (err) { + data->type = VM_FAULT_SIGBUS; + goto out_unlock; + } + + page_offset = (address - vma->vm_start) >> PAGE_SHIFT; + + if (bus_size) { + drm_mem_type_manager_t *man = &dev->bm.man[bo->mem.mem_type]; + + pfn = ((bus_base + bus_offset) >> PAGE_SHIFT) + page_offset; + vma->vm_page_prot = drm_io_prot(man->drm_bus_maptype, vma); + } else { + ttm = bo->ttm; + + drm_ttm_fixup_caching(ttm); + page = drm_ttm_get_page(ttm, page_offset); + if (!page) { + data->type = VM_FAULT_OOM; + goto out_unlock; + } + pfn = page_to_pfn(page); + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + } + + err = vm_insert_pfn(vma, address, pfn); + + if (!err || err == -EBUSY) + data->type = VM_FAULT_MINOR; + else + data->type = VM_FAULT_OOM; +out_unlock: + mutex_unlock(&bo->mutex); + return NULL; +} +#endif + +static void drm_bo_vm_open_locked(struct vm_area_struct *vma) +{ + drm_buffer_object_t *bo = (drm_buffer_object_t *) vma->vm_private_data; + + drm_vm_open_locked(vma); + atomic_inc(&bo->usage); +#ifdef DRM_ODD_MM_COMPAT + drm_bo_add_vma(bo, vma); +#endif +} + +/** + * \c vma open method for buffer objects. + * + * \param vma virtual memory area. + */ + +static void drm_bo_vm_open(struct vm_area_struct *vma) +{ + drm_buffer_object_t *bo = (drm_buffer_object_t *) vma->vm_private_data; + drm_device_t *dev = bo->dev; + + mutex_lock(&dev->struct_mutex); + drm_bo_vm_open_locked(vma); + mutex_unlock(&dev->struct_mutex); +} + +/** + * \c vma close method for buffer objects. + * + * \param vma virtual memory area. + */ + +static void drm_bo_vm_close(struct vm_area_struct *vma) +{ + drm_buffer_object_t *bo = (drm_buffer_object_t *) vma->vm_private_data; + drm_device_t *dev = bo->dev; + + drm_vm_close(vma); + if (bo) { + mutex_lock(&dev->struct_mutex); +#ifdef DRM_ODD_MM_COMPAT + drm_bo_delete_vma(bo, vma); +#endif + drm_bo_usage_deref_locked(bo); + mutex_unlock(&dev->struct_mutex); + } + return; +} + +static struct vm_operations_struct drm_bo_vm_ops = { +#ifdef DRM_FULL_MM_COMPAT + .fault = drm_bo_vm_fault, +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) + .nopfn = drm_bo_vm_nopfn, +#else + .nopage = drm_bo_vm_nopage, +#endif +#endif + .open = drm_bo_vm_open, + .close = drm_bo_vm_close, +}; + +/** + * mmap buffer object memory. + * + * \param vma virtual memory area. + * \param filp file pointer. + * \param map The buffer object drm map. + * \return zero on success or a negative number on failure. + */ + +int drm_bo_mmap_locked(struct vm_area_struct *vma, + struct file *filp, + drm_local_map_t *map) +{ + vma->vm_ops = &drm_bo_vm_ops; + vma->vm_private_data = map->handle; + vma->vm_file = filp; + vma->vm_flags |= VM_RESERVED | VM_IO; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) + vma->vm_flags |= VM_PFNMAP; +#endif + drm_bo_vm_open_locked(vma); +#ifdef DRM_ODD_MM_COMPAT + drm_bo_map_bound(vma); +#endif + return 0; +} diff --git a/linux-core/i810_dma.c b/linux-core/i810_dma.c index ad4d2fce..41467001 100644 --- a/linux-core/i810_dma.c +++ b/linux-core/i810_dma.c @@ -125,8 +125,8 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (io_remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; } diff --git a/linux-core/i830_dma.c b/linux-core/i830_dma.c index e93307fb..406a3ff7 100644 --- a/linux-core/i830_dma.c +++ b/linux-core/i830_dma.c @@ -110,7 +110,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) unlock_kernel(); if (remap_pfn_range(vma, vma->vm_start, - VM_OFFSET(vma) >> PAGE_SHIFT, + vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; diff --git a/linux-core/i915_buffer.c b/linux-core/i915_buffer.c index c3e54468..8797de89 100644 --- a/linux-core/i915_buffer.c +++ b/linux-core/i915_buffer.c @@ -33,16 +33,15 @@ #include "i915_drm.h" #include "i915_drv.h" - drm_ttm_backend_t *i915_create_ttm_backend_entry(drm_device_t * dev) { return drm_agp_init_ttm(dev, NULL); } -int i915_fence_types(uint32_t buffer_flags, uint32_t * class, uint32_t * type) +int i915_fence_types(drm_buffer_object_t *bo, uint32_t * class, uint32_t * type) { *class = 0; - if (buffer_flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) + if (bo->mem.flags & (DRM_BO_FLAG_READ | DRM_BO_FLAG_WRITE)) *type = 3; else *type = 1; @@ -64,3 +63,173 @@ int i915_invalidate_caches(drm_device_t * dev, uint32_t flags) return i915_emit_mi_flush(dev, flush_cmd); } + +int i915_init_mem_type(drm_device_t * dev, uint32_t type, + drm_mem_type_manager_t * man) +{ + switch (type) { + case DRM_BO_MEM_LOCAL: + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CACHED; + man->drm_bus_maptype = 0; + break; + case DRM_BO_MEM_TT: + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CSELECT | _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_AGP; + break; + case DRM_BO_MEM_PRIV0: + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_FIXED | _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_AGP; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +uint32_t i915_evict_mask(drm_buffer_object_t *bo) +{ + switch (bo->mem.mem_type) { + case DRM_BO_MEM_LOCAL: + case DRM_BO_MEM_TT: + return DRM_BO_FLAG_MEM_LOCAL; + default: + return DRM_BO_FLAG_MEM_TT | DRM_BO_FLAG_CACHED; + } +} + +static void i915_emit_copy_blit(drm_device_t * dev, + uint32_t src_offset, + uint32_t dst_offset, + uint32_t pages, int direction) +{ + uint32_t cur_pages; + uint32_t stride = PAGE_SIZE; + drm_i915_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + if (!dev_priv) + return; + + i915_kernel_lost_context(dev); + while (pages > 0) { + cur_pages = pages; + if (cur_pages > 2048) + cur_pages = 2048; + pages -= cur_pages; + + BEGIN_LP_RING(6); + OUT_RING(SRC_COPY_BLT_CMD | XY_SRC_COPY_BLT_WRITE_ALPHA | + XY_SRC_COPY_BLT_WRITE_RGB); + OUT_RING((stride & 0xffff) | (0xcc << 16) | (1 << 24) | + (1 << 25) | (direction ? (1 << 30) : 0)); + OUT_RING((cur_pages << 16) | PAGE_SIZE); + OUT_RING(dst_offset); + OUT_RING(stride & 0xffff); + OUT_RING(src_offset); + ADVANCE_LP_RING(); + } + return; +} + +static int i915_move_blit(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem) +{ + drm_bo_mem_reg_t *old_mem = &bo->mem; + int dir = 0; + + if ((old_mem->mem_type == new_mem->mem_type) && + (new_mem->mm_node->start < + old_mem->mm_node->start + old_mem->mm_node->size)) { + dir = 1; + } + + i915_emit_copy_blit(bo->dev, + old_mem->mm_node->start << PAGE_SHIFT, + new_mem->mm_node->start << PAGE_SHIFT, + new_mem->num_pages, dir); + + i915_emit_mi_flush(bo->dev, MI_READ_FLUSH | MI_EXE_FLUSH); + + return drm_bo_move_accel_cleanup(bo, evict, no_wait, 0, + DRM_FENCE_TYPE_EXE | + DRM_I915_FENCE_TYPE_RW, + DRM_I915_FENCE_FLAG_FLUSHED, new_mem); +} + +/* + * Flip destination ttm into cached-coherent AGP, + * then blit and subsequently move out again. + */ + +static int i915_move_flip(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem) +{ + drm_device_t *dev = bo->dev; + drm_bo_mem_reg_t tmp_mem; + int ret; + + tmp_mem = *new_mem; + tmp_mem.mm_node = NULL; + tmp_mem.mask = DRM_BO_FLAG_MEM_TT | + DRM_BO_FLAG_CACHED | DRM_BO_FLAG_FORCE_CACHING; + + ret = drm_bo_mem_space(bo, &tmp_mem, no_wait); + if (ret) + return ret; + + ret = drm_bind_ttm(bo->ttm, 1, tmp_mem.mm_node->start); + if (ret) + goto out_cleanup; + + ret = i915_move_blit(bo, 1, no_wait, &tmp_mem); + if (ret) + goto out_cleanup; + + ret = drm_bo_move_ttm(bo, evict, no_wait, new_mem); +out_cleanup: + if (tmp_mem.mm_node) { + mutex_lock(&dev->struct_mutex); + if (tmp_mem.mm_node != bo->pinned_node) + drm_mm_put_block(tmp_mem.mm_node); + tmp_mem.mm_node = NULL; + mutex_unlock(&dev->struct_mutex); + } + return ret; +} + +int i915_move(drm_buffer_object_t * bo, + int evict, int no_wait, drm_bo_mem_reg_t * new_mem) +{ + drm_bo_mem_reg_t *old_mem = &bo->mem; + + if (old_mem->mem_type == DRM_BO_MEM_LOCAL) { + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else if (new_mem->mem_type == DRM_BO_MEM_LOCAL) { + if (i915_move_flip(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } else { + if (i915_move_blit(bo, evict, no_wait, new_mem)) + return drm_bo_move_memcpy(bo, evict, no_wait, new_mem); + } + return 0; +} diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 2c5b43d0..56e5998f 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -40,22 +40,32 @@ static struct pci_device_id pciidlist[] = { #ifdef I915_HAVE_FENCE static drm_fence_driver_t i915_fence_driver = { - .no_types = 2, + .num_classes = 1, .wrap_diff = (1 << 30), .flush_diff = (1 << 29), .sequence_mask = 0xffffffffU, .lazy_capable = 1, .emit = i915_fence_emit_sequence, .poke_flush = i915_poke_flush, + .has_irq = i915_fence_has_irq, }; #endif #ifdef I915_HAVE_BUFFER + +static uint32_t i915_mem_prios[] = {DRM_BO_MEM_PRIV0, DRM_BO_MEM_TT, DRM_BO_MEM_LOCAL}; +static uint32_t i915_busy_prios[] = {DRM_BO_MEM_TT, DRM_BO_MEM_PRIV0, DRM_BO_MEM_LOCAL}; + static drm_bo_driver_t i915_bo_driver = { - .iomap = {NULL, NULL}, - .cached = {1, 1}, + .mem_type_prio = i915_mem_prios, + .mem_busy_prio = i915_busy_prios, + .num_mem_type_prio = sizeof(i915_mem_prios)/sizeof(uint32_t), + .num_mem_busy_prio = sizeof(i915_busy_prios)/sizeof(uint32_t), .create_ttm_backend_entry = i915_create_ttm_backend_entry, .fence_type = i915_fence_types, - .invalidate_caches = i915_invalidate_caches + .invalidate_caches = i915_invalidate_caches, + .init_mem_type = i915_init_mem_type, + .evict_mask = i915_evict_mask, + .move = i915_move, }; #endif diff --git a/linux-core/i915_fence.c b/linux-core/i915_fence.c index 2182604c..88daa57c 100644 --- a/linux-core/i915_fence.c +++ b/linux-core/i915_fence.c @@ -42,36 +42,34 @@ static void i915_perform_flush(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_fence_manager_t *fm = &dev->fm; + drm_fence_class_manager_t *fc = &fm->class[0]; drm_fence_driver_t *driver = dev->driver->fence_driver; uint32_t flush_flags = 0; uint32_t flush_sequence = 0; uint32_t i_status; uint32_t diff; uint32_t sequence; + int rwflush; if (!dev_priv) return; - if (fm->pending_exe_flush) { + if (fc->pending_exe_flush) { sequence = READ_BREADCRUMB(dev_priv); /* * First update fences with the current breadcrumb. */ - diff = sequence - fm->last_exe_flush; + diff = sequence - fc->last_exe_flush; if (diff < driver->wrap_diff && diff != 0) { - drm_fence_handler(dev, sequence, DRM_FENCE_TYPE_EXE); + drm_fence_handler(dev, 0, sequence, DRM_FENCE_TYPE_EXE); } - diff = sequence - fm->exe_flush_sequence; - if (diff < driver->wrap_diff) { - fm->pending_exe_flush = 0; - if (dev_priv->fence_irq_on) { - i915_user_irq_off(dev_priv); - dev_priv->fence_irq_on = 0; - } - } else if (!dev_priv->fence_irq_on) { + if (dev_priv->fence_irq_on && !fc->pending_exe_flush) { + i915_user_irq_off(dev_priv); + dev_priv->fence_irq_on = 0; + } else if (!dev_priv->fence_irq_on && fc->pending_exe_flush) { i915_user_irq_on(dev_priv); dev_priv->fence_irq_on = 1; } @@ -84,17 +82,18 @@ static void i915_perform_flush(drm_device_t * dev) flush_flags = dev_priv->flush_flags; flush_sequence = dev_priv->flush_sequence; dev_priv->flush_pending = 0; - drm_fence_handler(dev, flush_sequence, flush_flags); + drm_fence_handler(dev, 0, flush_sequence, flush_flags); } } - if (fm->pending_flush && !dev_priv->flush_pending) { + rwflush = fc->pending_flush & DRM_I915_FENCE_TYPE_RW; + if (rwflush && !dev_priv->flush_pending) { dev_priv->flush_sequence = (uint32_t) READ_BREADCRUMB(dev_priv); - dev_priv->flush_flags = fm->pending_flush; + dev_priv->flush_flags = fc->pending_flush; dev_priv->saved_flush_status = READ_HWSP(dev_priv, 0); I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21)); dev_priv->flush_pending = 1; - fm->pending_flush = 0; + fc->pending_flush &= ~DRM_I915_FENCE_TYPE_RW; } if (dev_priv->flush_pending) { @@ -104,13 +103,13 @@ static void i915_perform_flush(drm_device_t * dev) flush_flags = dev_priv->flush_flags; flush_sequence = dev_priv->flush_sequence; dev_priv->flush_pending = 0; - drm_fence_handler(dev, flush_sequence, flush_flags); + drm_fence_handler(dev, 0, flush_sequence, flush_flags); } } } -void i915_poke_flush(drm_device_t * dev) +void i915_poke_flush(drm_device_t * dev, uint32_t class) { drm_fence_manager_t *fm = &dev->fm; unsigned long flags; @@ -120,7 +119,7 @@ void i915_poke_flush(drm_device_t * dev) write_unlock_irqrestore(&fm->lock, flags); } -int i915_fence_emit_sequence(drm_device_t * dev, uint32_t flags, +int i915_fence_emit_sequence(drm_device_t * dev, uint32_t class, uint32_t flags, uint32_t * sequence, uint32_t * native_type) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -144,3 +143,15 @@ void i915_fence_handler(drm_device_t * dev) i915_perform_flush(dev); write_unlock(&fm->lock); } + +int i915_fence_has_irq(drm_device_t *dev, uint32_t class, uint32_t flags) +{ + /* + * We have an irq that tells us when we have a new breadcrumb. + */ + + if (class == 0 && flags == DRM_FENCE_TYPE_EXE) + return 1; + + return 0; +} diff --git a/linux-core/sis_drv.c b/linux-core/sis_drv.c index 9b0b9830..114ec8f9 100644 --- a/linux-core/sis_drv.c +++ b/linux-core/sis_drv.c @@ -74,7 +74,7 @@ static struct drm_driver driver = { .context_dtor = NULL, .dma_quiescent = sis_idle, .reclaim_buffers = NULL, - .reclaim_buffers_locked = sis_reclaim_buffers_locked, + .reclaim_buffers_idlelocked = sis_reclaim_buffers_locked, .lastclose = sis_lastclose, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, diff --git a/linux-core/via_buffer.c b/linux-core/via_buffer.c new file mode 100644 index 00000000..f156ee6d --- /dev/null +++ b/linux-core/via_buffer.c @@ -0,0 +1,163 @@ +/************************************************************************** + * + * Copyright (c) 2007 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "via_drm.h" +#include "via_drv.h" + +drm_ttm_backend_t *via_create_ttm_backend_entry(drm_device_t * dev) +{ + return drm_agp_init_ttm(dev, NULL); +} + +int via_fence_types(drm_buffer_object_t *bo, uint32_t * class, uint32_t * type) +{ + *class = 0; + *type = 3; + return 0; +} + +int via_invalidate_caches(drm_device_t * dev, uint32_t flags) +{ + /* + * FIXME: Invalidate texture caches here. + */ + + return 0; +} + + +static int via_vram_info(drm_device_t *dev, + unsigned long *offset, + unsigned long *size) +{ + struct pci_dev *pdev = dev->pdev; + unsigned long flags; + + int ret = DRM_ERR(EINVAL); + int i; + for (i=0; i<6; ++i) { + flags = pci_resource_flags(pdev, i); + if ((flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) == + (IORESOURCE_MEM | IORESOURCE_PREFETCH)) { + ret = 0; + break; + } + } + + if (ret) { + DRM_ERROR("Could not find VRAM PCI resource\n"); + return ret; + } + + *offset = pci_resource_start(pdev, i); + *size = pci_resource_end(pdev, i) - *offset + 1; + return 0; +} + +int via_init_mem_type(drm_device_t * dev, uint32_t type, + drm_mem_type_manager_t * man) +{ + switch (type) { + case DRM_BO_MEM_LOCAL: + /* System memory */ + + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_CACHED; + man->drm_bus_maptype = 0; + break; + + case DRM_BO_MEM_TT: + /* Dynamic agpgart memory */ + + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_NEEDS_IOREMAP; + + /* Only to get pte protection right. */ + + man->drm_bus_maptype = _DRM_AGP; + break; + + case DRM_BO_MEM_VRAM: + /* "On-card" video ram */ + + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_FRAME_BUFFER; + man->io_addr = NULL; + return via_vram_info(dev, &man->io_offset, &man->io_size); + break; + + case DRM_BO_MEM_PRIV0: + /* Pre-bound agpgart memory */ + + if (!(drm_core_has_AGP(dev) && dev->agp)) { + DRM_ERROR("AGP is not enabled for memory type %u\n", + (unsigned)type); + return -EINVAL; + } + man->io_offset = dev->agp->agp_info.aper_base; + man->io_size = dev->agp->agp_info.aper_size * 1024 * 1024; + man->io_addr = NULL; + man->flags = _DRM_FLAG_MEMTYPE_MAPPABLE | + _DRM_FLAG_MEMTYPE_FIXED | _DRM_FLAG_NEEDS_IOREMAP; + man->drm_bus_maptype = _DRM_AGP; + break; + + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +uint32_t via_evict_mask(drm_buffer_object_t *bo) +{ + switch (bo->mem.mem_type) { + case DRM_BO_MEM_LOCAL: + case DRM_BO_MEM_TT: + return DRM_BO_FLAG_MEM_LOCAL; /* Evict TT to local */ + case DRM_BO_MEM_PRIV0: /* Evict pre-bound AGP to TT */ + return DRM_BO_MEM_TT; + case DRM_BO_MEM_VRAM: + if (bo->mem.num_pages > 128) + return DRM_BO_MEM_TT; + else + return DRM_BO_MEM_LOCAL; + default: + return DRM_BO_MEM_LOCAL; + } +} diff --git a/linux-core/via_fence.c b/linux-core/via_fence.c new file mode 100644 index 00000000..02249939 --- /dev/null +++ b/linux-core/via_fence.c @@ -0,0 +1,230 @@ +/************************************************************************** + * + * Copyright (c) 2007 Tungsten Graphics, Inc., Cedar Park, TX., USA, + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + **************************************************************************/ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include "drmP.h" +#include "via_drm.h" +#include "via_drv.h" + +/* + * DRM_FENCE_TYPE_EXE guarantees that all command buffers can be evicted. + * DRM_VIA_FENCE_TYPE_ACCEL guarantees that all 2D & 3D rendering is complete. + */ + + +static uint32_t via_perform_flush(drm_device_t *dev, uint32_t class) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_fence_class_manager_t *fc = &dev->fm.class[class]; + uint32_t pending_flush_types = 0; + uint32_t signaled_flush_types = 0; + uint32_t status; + + if (class != 0) + return 0; + + if (!dev_priv) + return 0; + + spin_lock(&dev_priv->fence_lock); + + pending_flush_types = fc->pending_flush | + ((fc->pending_exe_flush) ? DRM_FENCE_TYPE_EXE : 0); + + if (pending_flush_types) { + + /* + * Take the idlelock. This guarantees that the next time a client tries + * to grab the lock, it will stall until the idlelock is released. This + * guarantees that eventually, the GPU engines will be idle, but nothing + * else. It cannot be used to protect the hardware. + */ + + + if (!dev_priv->have_idlelock) { + drm_idlelock_take(&dev->lock); + dev_priv->have_idlelock = 1; + } + + /* + * Check if AGP command reader is idle. + */ + + if (pending_flush_types & DRM_FENCE_TYPE_EXE) + if (VIA_READ(0x41C) & 0x80000000) + signaled_flush_types |= DRM_FENCE_TYPE_EXE; + + /* + * Check VRAM command queue empty and 2D + 3D engines idle. + */ + + if (pending_flush_types & DRM_VIA_FENCE_TYPE_ACCEL) { + status = VIA_READ(VIA_REG_STATUS); + if ((status & VIA_VR_QUEUE_BUSY) && + !(status & (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY))) + signaled_flush_types |= DRM_VIA_FENCE_TYPE_ACCEL; + } + + if (signaled_flush_types) { + pending_flush_types &= ~signaled_flush_types; + if (!pending_flush_types && dev_priv->have_idlelock) { + drm_idlelock_release(&dev->lock); + dev_priv->have_idlelock = 0; + } + drm_fence_handler(dev, 0, dev_priv->emit_0_sequence, signaled_flush_types); + } + } + + spin_unlock(&dev_priv->fence_lock); + + return fc->pending_flush | + ((fc->pending_exe_flush) ? DRM_FENCE_TYPE_EXE : 0); +} + + +/** + * Emit a fence sequence. + */ + +int via_fence_emit_sequence(drm_device_t * dev, uint32_t class, uint32_t flags, + uint32_t * sequence, uint32_t * native_type) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + int ret = 0; + + if (!dev_priv) + return -EINVAL; + + switch(class) { + case 0: /* AGP command stream */ + + /* + * The sequence number isn't really used by the hardware yet. + */ + + spin_lock(&dev_priv->fence_lock); + *sequence = ++dev_priv->emit_0_sequence; + spin_unlock(&dev_priv->fence_lock); + + /* + * When drm_fence_handler() is called with flush type 0x01, and a + * sequence number, That means that the EXE flag is expired. + * Nothing else. No implicit flushing or other engines idle. + */ + + *native_type = DRM_FENCE_TYPE_EXE; + break; + default: + ret = DRM_ERR(EINVAL); + break; + } + return ret; +} + +/** + * Manual poll (from the fence manager). + */ + +void via_poke_flush(drm_device_t * dev, uint32_t class) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_fence_manager_t *fm = &dev->fm; + unsigned long flags; + uint32_t pending_flush; + + if (!dev_priv) + return ; + + write_lock_irqsave(&fm->lock, flags); + pending_flush = via_perform_flush(dev, class); + if (pending_flush) + pending_flush = via_perform_flush(dev, class); + write_unlock_irqrestore(&fm->lock, flags); + + /* + * Kick the timer if there are more flushes pending. + */ + + if (pending_flush && !timer_pending(&dev_priv->fence_timer)) { + dev_priv->fence_timer.expires = jiffies + 1; + add_timer(&dev_priv->fence_timer); + } +} + +/** + * No irq fence expirations implemented yet. + * Although both the HQV engines and PCI dmablit engines signal + * idle with an IRQ, we haven't implemented this yet. + * This means that the drm fence manager will always poll for engine idle, + * unless the caller wanting to wait for a fence object has indicated a lazy wait. + */ + +int via_fence_has_irq(struct drm_device * dev, uint32_t class, + uint32_t flags) +{ + return 0; +} + +/** + * Regularly call the flush function. This enables lazy waits, so we can + * set lazy_capable. Lazy waits don't really care when the fence expires, + * so a timer tick delay should be fine. + */ + +void via_fence_timer(unsigned long data) +{ + drm_device_t *dev = (drm_device_t *) data; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_fence_manager_t *fm = &dev->fm; + uint32_t pending_flush; + drm_fence_class_manager_t *fc = &dev->fm.class[0]; + + if (!dev_priv) + return; + if (!fm->initialized) + goto out_unlock; + + via_poke_flush(dev, 0); + pending_flush = fc->pending_flush | + ((fc->pending_exe_flush) ? DRM_FENCE_TYPE_EXE : 0); + + /* + * disable timer if there are no more flushes pending. + */ + + if (!pending_flush && timer_pending(&dev_priv->fence_timer)) { + BUG_ON(dev_priv->have_idlelock); + del_timer(&dev_priv->fence_timer); + } + return; +out_unlock: + return; + +} |