When we need to clear some space in the GTT in order to pin a new buffer, scan through the inactive list amalgamating objects in LRU order until we find a large enough contiguous space to fit the new buffer.
Doing throughput testing on a PineView machine with cairo-perf-trace indicates that there is very little difference with the new LRU scan, perhaps a small improvement. Reference: Bug 15911 - Intermittent X crash (freeze) https://bugzilla.kernel.org/show_bug.cgi?id=15911 Bug 20152 - cannot view JPG in firefox when running UXA https://bugs.freedesktop.org/show_bug.cgi?id=20152 Bug 24369 - Hang when scrolling firefox page with window in front https://bugs.freedesktop.org/show_bug.cgi?id=24369 Bug 28478 - Intermittent graphics lockups due to overflow/loop https://bugs.freedesktop.org/show_bug.cgi?id=28478 v2: Process active and flushing lists using roster. v3: Update to apply LRU across the render and bsd rings. --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_drv.h | 7 + drivers/gpu/drm/i915/i915_gem.c | 352 +++++------------------ drivers/gpu/drm/i915/i915_gem_evict.c | 489 +++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.c | 41 ++-- drivers/gpu/drm/i915/intel_ringbuffer.h | 3 - 6 files changed, 594 insertions(+), 299 deletions(-) create mode 100644 drivers/gpu/drm/i915/i915_gem_evict.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index da78f2c..384fd45 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -8,6 +8,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \ i915_suspend.o \ i915_gem.o \ i915_gem_debug.o \ + i915_gem_evict.o \ i915_gem_tiling.o \ i915_trace_points.o \ intel_display.o \ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 98e6980..339ce42 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -236,6 +236,7 @@ typedef struct drm_i915_private { struct pci_dev *bridge_dev; struct intel_ring_buffer render_ring; struct intel_ring_buffer bsd_ring; + u32 next_seqno; drm_dma_handle_t *status_page_dmah; void *seqno_page; @@ -966,6 +967,7 @@ void i915_gem_cleanup_ringbuffer(struct drm_device *dev); int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end); int i915_gem_idle(struct drm_device *dev); +int i915_gpu_idle(struct drm_device *dev); uint32_t i915_add_request(struct drm_device *dev, struct drm_file *file_priv, uint32_t flush_domains, @@ -990,6 +992,11 @@ int i915_gem_object_flush_write_domain(struct drm_gem_object *obj); void i915_gem_shrinker_init(void); void i915_gem_shrinker_exit(void); +/* i915_gem_eviction.c */ +int i915_gem_evict_from_inactive_list(struct drm_device *dev); +int i915_gem_evict_something(struct drm_device *dev, int size, unsigned align); +int i915_gem_evict_everything(struct drm_device *dev); + /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a6fbe3b..7d20901 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -48,8 +48,6 @@ static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); -static int i915_gem_evict_something(struct drm_device *dev, int min_size); -static int i915_gem_evict_from_inactive_list(struct drm_device *dev); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, struct drm_i915_gem_pwrite *args, struct drm_file *file_priv); @@ -300,6 +298,42 @@ fail_unlock: return ret; } +/** + * i915_gem_get_gtt_alignment - return required GTT alignment for an object + * @obj: object to check + * + * Return the required GTT alignment for an object, taking into account + * potential fence register mapping if needed. + */ +static uint32_t +i915_gem_get_fence_alignment(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); + int start, i; + + /* + * Minimum alignment is 4k (GTT page size), but might be greater + * if a fence register is needed for the object. + */ + if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE) + return 4096; + + /* + * Previous chips need to be aligned to the size of the smallest + * fence register that can contain the object. + */ + if (IS_I9XX(dev)) + start = 1024*1024; + else + start = 512*1024; + + for (i = start; i < obj->size; i <<= 1) + ; + + return i; +} + static int i915_gem_object_get_pages_or_evict(struct drm_gem_object *obj) { @@ -313,8 +347,10 @@ i915_gem_object_get_pages_or_evict(struct drm_gem_object *obj) if (ret == -ENOMEM) { struct drm_device *dev = obj->dev; - ret = i915_gem_evict_something(dev, obj->size); - if (ret) + ret = i915_gem_evict_something(dev, + obj->size, + i915_gem_get_fence_alignment(obj)); + if (ret != -ENOSPC) return ret; ret = i915_gem_object_get_pages(obj, 0); @@ -1308,42 +1344,6 @@ i915_gem_free_mmap_offset(struct drm_gem_object *obj) } /** - * i915_gem_get_fence_alignment - return required GTT alignment for an object - * @obj: object to check - * - * Return the required GTT alignment for an object, taking into account - * potential fence register mapping if needed. - */ -static uint32_t -i915_gem_get_fence_alignment(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); - int start, i; - - /* - * Minimum alignment is 4k (GTT page size), but might be greater - * if a fence register is needed for the object. - */ - if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE) - return 4096; - - /* - * Previous chips need to be aligned to the size of the smallest - * fence register that can contain the object. - */ - if (IS_I9XX(dev)) - start = 1024*1024; - else - start = 512*1024; - - for (i = start; i < obj->size; i <<= 1) - ; - - return i; -} - -/** * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing * @dev: DRM device * @data: GTT mapping ioctl data @@ -1866,19 +1866,6 @@ i915_gem_flush(struct drm_device *dev, flush_domains); } -static void -i915_gem_flush_ring(struct drm_device *dev, - uint32_t invalidate_domains, - uint32_t flush_domains, - struct intel_ring_buffer *ring) -{ - if (flush_domains & I915_GEM_DOMAIN_CPU) - drm_agp_chipset_flush(dev); - ring->flush(dev, ring, - invalidate_domains, - flush_domains); -} - /** * Ensures that all rendering to the object has completed and the object is * safe to unbind from the GTT or access from the CPU. @@ -1988,34 +1975,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return 0; } -static struct drm_gem_object * -i915_gem_find_inactive_object(struct drm_device *dev, int min_size) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj_priv; - struct drm_gem_object *best = NULL; - struct drm_gem_object *first = NULL; - - /* Try to find the smallest clean object */ - list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { - struct drm_gem_object *obj = &obj_priv->base; - if (obj->size >= min_size) { - if ((!obj_priv->dirty || - i915_gem_object_is_purgeable(obj_priv)) && - (!best || obj->size < best->size)) { - best = obj; - if (best->size == min_size) - return best; - } - if (!first) - first = obj; - } - } - - return best ? best : first; -} - -static int +int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -2056,158 +2016,6 @@ i915_gpu_idle(struct drm_device *dev) return ret; } -static int -i915_gem_evict_everything(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int ret; - bool lists_empty; - - spin_lock(&dev_priv->mm.active_list_lock); - lists_empty = (list_empty(&dev_priv->mm.inactive_list) && - list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - (!HAS_BSD(dev) - || list_empty(&dev_priv->bsd_ring.active_list))); - spin_unlock(&dev_priv->mm.active_list_lock); - - if (lists_empty) - return -ENOSPC; - - /* Flush everything (on to the inactive lists) and evict */ - ret = i915_gpu_idle(dev); - if (ret) - return ret; - - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - - ret = i915_gem_evict_from_inactive_list(dev); - if (ret) - return ret; - - spin_lock(&dev_priv->mm.active_list_lock); - lists_empty = (list_empty(&dev_priv->mm.inactive_list) && - list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->render_ring.active_list) && - (!HAS_BSD(dev) - || list_empty(&dev_priv->bsd_ring.active_list))); - spin_unlock(&dev_priv->mm.active_list_lock); - BUG_ON(!lists_empty); - - return 0; -} - -static int -i915_gem_evict_something(struct drm_device *dev, int min_size) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_gem_object *obj; - int ret; - - struct intel_ring_buffer *render_ring = &dev_priv->render_ring; - struct intel_ring_buffer *bsd_ring = &dev_priv->bsd_ring; - for (;;) { - i915_gem_retire_requests(dev, render_ring); - - if (HAS_BSD(dev)) - i915_gem_retire_requests(dev, bsd_ring); - - /* If there's an inactive buffer available now, grab it - * and be done. - */ - obj = i915_gem_find_inactive_object(dev, min_size); - if (obj) { - struct drm_i915_gem_object *obj_priv; - -#if WATCH_LRU - DRM_INFO("%s: evicting %p\n", __func__, obj); -#endif - obj_priv = to_intel_bo(obj); - BUG_ON(obj_priv->pin_count != 0); - BUG_ON(obj_priv->active); - - /* Wait on the rendering and unbind the buffer. */ - return i915_gem_object_unbind(obj); - } - - /* If we didn't get anything, but the ring is still processing - * things, wait for the next to finish and hopefully leave us - * a buffer to evict. - */ - if (!list_empty(&render_ring->request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&render_ring->request_list, - struct drm_i915_gem_request, - list); - - ret = i915_wait_request(dev, - request->seqno, request->ring); - if (ret) - return ret; - - continue; - } - - if (HAS_BSD(dev) && !list_empty(&bsd_ring->request_list)) { - struct drm_i915_gem_request *request; - - request = list_first_entry(&bsd_ring->request_list, - struct drm_i915_gem_request, - list); - - ret = i915_wait_request(dev, - request->seqno, request->ring); - if (ret) - return ret; - - continue; - } - - /* If we didn't have anything on the request list but there - * are buffers awaiting a flush, emit one and try again. - * When we wait on it, those buffers waiting for that flush - * will get moved to inactive. - */ - if (!list_empty(&dev_priv->mm.flushing_list)) { - struct drm_i915_gem_object *obj_priv; - - /* Find an object that we can immediately reuse */ - list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) { - obj = &obj_priv->base; - if (obj->size >= min_size) - break; - - obj = NULL; - } - - if (obj != NULL) { - uint32_t seqno; - - i915_gem_flush_ring(dev, - obj->write_domain, - obj->write_domain, - obj_priv->ring); - seqno = i915_add_request(dev, NULL, - obj->write_domain, - obj_priv->ring); - if (seqno == 0) - return -ENOMEM; - continue; - } - } - - /* If we didn't do any of the above, there's no single buffer - * large enough to swap out for the new one, so just evict - * everything and start again. (This should be rare.) - */ - if (!list_empty (&dev_priv->mm.inactive_list)) - return i915_gem_evict_from_inactive_list(dev); - else - return i915_gem_evict_everything(dev); - } -} - int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) @@ -2640,28 +2448,25 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) search_free: free_space = drm_mm_search_free(&dev_priv->mm.gtt_space, obj->size, alignment, 0); - if (free_space != NULL) { - obj_priv->gtt_space = drm_mm_get_block(free_space, obj->size, - alignment); - if (obj_priv->gtt_space != NULL) { - obj_priv->gtt_space->private = obj; - obj_priv->gtt_offset = obj_priv->gtt_space->start; - } - } - if (obj_priv->gtt_space == NULL) { - /* If the gtt is empty and we're still having trouble - * fitting our object in, we're out of memory. - */ + if (free_space == NULL) { #if WATCH_LRU DRM_INFO("%s: GTT full, evicting something\n", __func__); #endif - ret = i915_gem_evict_something(dev, obj->size); + ret = i915_gem_evict_something(dev, obj->size, alignment); if (ret) return ret; goto search_free; } + obj_priv->gtt_space = drm_mm_get_block(free_space, obj->size, + alignment); + if (obj_priv->gtt_space == NULL) + return -ENOMEM; + + obj_priv->gtt_space->private = obj; + obj_priv->gtt_offset = obj_priv->gtt_space->start; + #if WATCH_BUF DRM_INFO("Binding object of size %zd at 0x%08x\n", obj->size, obj_priv->gtt_offset); @@ -2673,8 +2478,11 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) if (ret == -ENOMEM) { /* first try to clear up some space from the GTT */ - ret = i915_gem_evict_something(dev, obj->size); - if (ret) { + ret = i915_gem_evict_something(dev, obj->size, alignment); + if (ret == 0) + goto search_free; + + if (ret == -ENOSPC) { /* now try to shrink everyone else */ if (gfpmask) { gfpmask = 0; @@ -2687,7 +2495,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) goto search_free; } - return ret; + return -ENOMEM; } /* Create an AGP memory structure pointing at our pages, and bind it @@ -2703,11 +2511,19 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) drm_mm_put_block(obj_priv->gtt_space); obj_priv->gtt_space = NULL; - ret = i915_gem_evict_something(dev, obj->size); - if (ret) - return ret; + ret = i915_gem_evict_something(dev, obj->size, alignment); + if (ret == 0) + goto search_free; - goto search_free; + if (ret == -ENOSPC) { + /* now try to shrink everyone else */ + if (gfpmask) { + gfpmask = 0; + goto search_free; + } + } + + return -ENOMEM; } atomic_inc(&dev->gtt_count); atomic_add(obj->size, &dev->gtt_memory); @@ -3866,7 +3682,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* evict everyone we can from the aperture */ ret = i915_gem_evict_everything(dev); - if (ret && ret != -ENOSPC) + if (ret) goto err; } @@ -4522,30 +4338,6 @@ void i915_gem_free_object(struct drm_gem_object *obj) kfree(obj_priv); } -/** Unbinds all inactive objects. */ -static int -i915_gem_evict_from_inactive_list(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - - while (!list_empty(&dev_priv->mm.inactive_list)) { - struct drm_gem_object *obj; - int ret; - - obj = &list_first_entry(&dev_priv->mm.inactive_list, - struct drm_i915_gem_object, - list)->base; - - ret = i915_gem_object_unbind(obj); - if (ret != 0) { - DRM_ERROR("Error unbinding object: %d\n", ret); - return ret; - } - } - - return 0; -} - int i915_gem_idle(struct drm_device *dev) { @@ -4686,7 +4478,11 @@ i915_gem_init_ringbuffer(struct drm_device *dev) ret = intel_init_ring_buffer(dev, &dev_priv->bsd_ring); if (ret) goto cleanup_render_ring; + } else { + INIT_LIST_HEAD(&dev_priv->bsd_ring.active_list); + INIT_LIST_HEAD(&dev_priv->bsd_ring.request_list); } + dev_priv->next_seqno = 1; return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c new file mode 100644 index 0000000..d2e8f04 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -0,0 +1,489 @@ +/* + * Copyright © 2008-2010 Intel Corporation + * + * 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, sublicense, + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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: + * Eric Anholt <e...@anholt.net> + * Chris Wilson <ch...@chris-wilson.co.uk> + * + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +struct i915_gem_eviction_entry { + struct list_head link; + unsigned long start, end, size; + struct i915_gem_eviction_objects { + struct drm_i915_gem_object *obj_priv[16]; + unsigned num_obj; + struct list_head link; + } objects; +}; + +struct i915_gem_eviction_roster { + struct list_head list; + struct list_head objects_free_list; +}; + +static void +i915_gem_eviction_roster_entry_free(struct i915_gem_eviction_roster *roster, + struct i915_gem_eviction_entry *entry) +{ + while(!list_empty(&entry->objects.link)) { + struct i915_gem_eviction_objects *objects; + + objects = list_first_entry(&entry->objects.link, + struct i915_gem_eviction_objects, + link); + + list_move(&objects->link, &roster->objects_free_list); + } + + list_del(&entry->link); + kfree(entry); +} + +static void +i915_gem_eviction_roster_fini(struct i915_gem_eviction_roster *roster) +{ + while(!list_empty(&roster->list)) { + struct i915_gem_eviction_entry *entry; + + entry = list_first_entry(&roster->list, + struct i915_gem_eviction_entry, + link); + i915_gem_eviction_roster_entry_free(roster, entry); + } + + while(!list_empty(&roster->objects_free_list)) { + struct i915_gem_eviction_objects *objects; + + objects = list_first_entry(&roster->objects_free_list, + struct i915_gem_eviction_objects, + link); + + list_del(&objects->link); + kfree(objects); + } +} + +static int +i915_gem_eviction_roster_init(struct drm_device *dev, + struct i915_gem_eviction_roster *roster) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_mm_node *mm; + + INIT_LIST_HEAD(&roster->list); + INIT_LIST_HEAD(&roster->objects_free_list); + + list_for_each_entry(mm, &dev_priv->mm.gtt_space.fl_entry, fl_entry) { + struct i915_gem_eviction_entry *entry; + + entry = kmalloc(sizeof (*entry), GFP_KERNEL); + if (entry == NULL) + return -ENOMEM; + + entry->start = mm->start; + entry->end = mm->start + mm->size; + entry->size = mm->size; + entry->objects.num_obj = 0; + INIT_LIST_HEAD(&entry->objects.link); + + list_add(&entry->link, &roster->list); + } + + return 0; +} + +static int +i915_gem_eviction_roster_entry_add(struct i915_gem_eviction_roster *roster, + struct i915_gem_eviction_entry *entry, + struct drm_i915_gem_object *obj_priv) +{ + struct i915_gem_eviction_objects *objects; + + if (list_empty(&entry->objects.link)) { + objects = &entry->objects; + } else { + objects = list_first_entry(&entry->objects.link, + struct i915_gem_eviction_objects, + link); + } + if (objects->num_obj == ARRAY_SIZE(objects->obj_priv)) { + if (list_empty (&roster->objects_free_list)) { + objects = kmalloc (sizeof (*objects), GFP_KERNEL); + if (objects == NULL) + return -ENOMEM; + } else { + struct i915_gem_eviction_objects *objects; + + objects = list_first_entry(&roster->objects_free_list, + struct i915_gem_eviction_objects, + link); + + list_del(&objects->link); + } + + objects->num_obj = 0; + list_add(&objects->link, &entry->objects.link); + } + + objects->obj_priv[objects->num_obj++] = obj_priv; + return 0; +} + +static int +i915_gem_eviction_roster_add(struct i915_gem_eviction_roster *roster, + struct drm_i915_gem_object *obj_priv) +{ + struct i915_gem_eviction_entry *before, *after, *entry = NULL; + long start = obj_priv->gtt_offset; + long end = start + obj_priv->base.size; + int i, ret; + + list_for_each_entry(before, &roster->list, link) { + if (before->end == start) { + i915_gem_eviction_roster_entry_add(roster, before, obj_priv); + entry = before; + entry->end = end; + break; + } + } + + list_for_each_entry(after, &roster->list, link) { + if (after->start == end) { + if (entry) { + struct i915_gem_eviction_objects *objects; + + entry->end = after->end; + for (i = 0; i < after->objects.num_obj; i++) { + ret = i915_gem_eviction_roster_entry_add(roster, entry, obj_priv); + if (ret) + return ret; + } + + list_for_each_entry(objects, &after->objects.link, link) { + for (i = 0; i < objects->num_obj; i++) { + ret = i915_gem_eviction_roster_entry_add(roster, entry, obj_priv); + if (ret) + return ret; + } + } + i915_gem_eviction_roster_entry_free(roster, entry); + } else { + ret = i915_gem_eviction_roster_entry_add(roster, after, obj_priv); + if (ret) + return ret; + + entry = after; + entry->start = start; + } + entry->size = entry->end - entry->start; + break; + } + } + + if (entry == NULL) { + entry = kmalloc(sizeof (*entry), GFP_KERNEL); + if (entry == NULL) + return -ENOMEM; + + entry->start = start; + entry->end = end; + entry->size = obj_priv->base.size; + entry->objects.num_obj = 0; + INIT_LIST_HEAD(&entry->objects.link); + + list_add(&entry->link, &roster->list); + + ret = i915_gem_eviction_roster_entry_add(roster, entry, obj_priv); + if (ret) + return ret; + } + + return 0; +} + +static struct i915_gem_eviction_entry * +i915_gem_eviction_roster_search(struct i915_gem_eviction_roster *roster, + unsigned long size, + unsigned align) +{ + struct i915_gem_eviction_entry *entry; + + list_for_each_entry(entry, &roster->list, link) { + unsigned wasted = 0; + + if (entry->size < size) + continue; + + if (align) { + unsigned tmp = entry->start & (align - 1); + if (tmp) + wasted += align - tmp; + } + + if (entry->size >= size + wasted) + return entry; + } + + return NULL; +} + +static int +i915_gem_eviction_entry_evict(struct i915_gem_eviction_entry *entry) +{ + struct i915_gem_eviction_objects *objects; + int i, ret; + + for (i = 0; i < entry->objects.num_obj; i++) { + ret = i915_gem_object_unbind(&entry->objects.obj_priv[i]->base); + if (ret) + return ret; + } + + list_for_each_entry(objects, &entry->objects.link, link) { + for (i = 0; i < objects->num_obj; i++) { + ret = i915_gem_object_unbind(&objects->obj_priv[i]->base); + if (ret) + return ret; + } + } + + return 0; +} + +static struct drm_i915_gem_object * +i915_gem_next_active_object(struct drm_device *dev, + struct list_head **render_iter, + struct list_head **bsd_iter) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *render_obj, *bsd_obj; + + if (*render_iter != &dev_priv->render_ring.active_list) + render_obj = list_entry(*render_iter, + struct drm_i915_gem_object, + list); + + if (HAS_BSD(dev)) { + if (*bsd_iter != &dev_priv->bsd_ring.active_list) + bsd_obj = list_entry(*bsd_iter, + struct drm_i915_gem_object, + list); + + /* XXX can we handle seqno wrapping? */ + if (render_obj->last_rendering_seqno < bsd_obj->last_rendering_seqno) { + *render_iter = (*render_iter)->next; + return render_obj; + } else { + *bsd_iter = (*bsd_iter)->next; + return bsd_obj; + } + } else { + *render_iter = (*render_iter)->next; + return render_obj; + } +} + +static int +i915_gem_evict_space(struct drm_device *dev, int size, unsigned align) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_gem_eviction_roster roster; + struct i915_gem_eviction_entry *entry; + struct drm_i915_gem_object *obj_priv; + struct list_head *render_iter, *bsd_iter; + int ret; + + /* Build an eviction roster, and find the oldest objects that + * could be evicted to free enough space for this request. + */ + ret = i915_gem_eviction_roster_init(dev, &roster); + if (ret) + goto err; + + BUG_ON(i915_gem_eviction_roster_search(&roster, size, align)); + + /* First search the objects that are ready to be evicted. */ + list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) { + ret = i915_gem_eviction_roster_add(&roster, obj_priv); + if (ret) + goto err; + + entry = i915_gem_eviction_roster_search(&roster, size, align); + if (entry) + goto done; + } + + /* Now search through the soon-to-be-expired objects. */ + render_iter = dev_priv->render_ring.active_list.next; + bsd_iter = dev_priv->bsd_ring.active_list.next; + while ((obj_priv = i915_gem_next_active_object(dev, &render_iter, &bsd_iter)) != NULL) { + /* Does the object require an outstanding flush? */ + if (obj_priv->base.write_domain || obj_priv->pin_count) + continue; + + ret = i915_gem_eviction_roster_add(&roster, obj_priv); + if (ret) + goto err; + + entry = i915_gem_eviction_roster_search(&roster, size, align); + if (entry) + goto done; + } + + /* Finally add anything with a pending flush (in order of retirement). */ + list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list, list) { + if (obj_priv->pin_count) + continue; + + ret = i915_gem_eviction_roster_add(&roster, obj_priv); + if (ret) + goto err; + + entry = i915_gem_eviction_roster_search(&roster, size, align); + if (entry) + goto done; + } + render_iter = dev_priv->render_ring.active_list.next; + bsd_iter = dev_priv->bsd_ring.active_list.next; + while ((obj_priv = i915_gem_next_active_object(dev, &render_iter, &bsd_iter)) != NULL) { + if (! obj_priv->base.write_domain || obj_priv->pin_count) + continue; + + ret = i915_gem_eviction_roster_add(&roster, obj_priv); + if (ret) + goto err; + + entry = i915_gem_eviction_roster_search(&roster, size, align); + if (entry) + goto done; + } + + ret = -ENOSPC; + goto err; +done: + ret = i915_gem_eviction_entry_evict(entry); +err: + i915_gem_eviction_roster_fini(&roster); + return ret; +} + +int +i915_gem_evict_something(struct drm_device *dev, int size, unsigned align) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + spin_lock(&dev_priv->mm.active_list_lock); + ret = list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->render_ring.active_list) && + list_empty(&dev_priv->bsd_ring.active_list); + spin_unlock(&dev_priv->mm.active_list_lock); + + if (ret) + return -ENOSPC; + + i915_gem_retire_requests(dev, &dev_priv->render_ring); + if (HAS_BSD(dev)) + i915_gem_retire_requests(dev, &dev_priv->bsd_ring); + + /* re-check for free space after retiring requests */ + if (drm_mm_search_free(&dev_priv->mm.gtt_space, + size, align, 0)) + return 0; + + ret = i915_gem_evict_space(dev, size, align); + if (ret != -ENOSPC) + return ret; + + /* If we didn't do any of the above, there's no single buffer + * large enough to swap out for the new one, so just evict + * everything and start again. (This should be rare.) + */ + return i915_gem_evict_everything(dev); +} + +int +i915_gem_evict_from_inactive_list(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + while (!list_empty(&dev_priv->mm.inactive_list)) { + struct drm_i915_gem_object *obj_priv; + int ret; + + obj_priv = list_first_entry(&dev_priv->mm.inactive_list, + struct drm_i915_gem_object, + list); + + ret = i915_gem_object_unbind(&obj_priv->base); + if (ret) + return ret; + } + + return 0; +} + +int +i915_gem_evict_everything(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + bool lists_empty; + + spin_lock(&dev_priv->mm.active_list_lock); + lists_empty = (list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->render_ring.active_list) && + list_empty(&dev_priv->bsd_ring.active_list)); + spin_unlock(&dev_priv->mm.active_list_lock); + + if (lists_empty) + return 0; + + /* Flush everything (on to the inactive lists) and evict */ + ret = i915_gpu_idle(dev); + if (ret) + return ret; + + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + + ret = i915_gem_evict_from_inactive_list(dev); + if (ret) + return ret; + + spin_lock(&dev_priv->mm.active_list_lock); + lists_empty = (list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->render_ring.active_list) && + list_empty(&dev_priv->bsd_ring.active_list)); + spin_unlock(&dev_priv->mm.active_list_lock); + BUG_ON(!lists_empty); + + return 0; +} diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 2a3d2fa..f15e174 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -39,12 +39,14 @@ render_ring_flush(struct drm_device *dev, u32 invalidate_domains, u32 flush_domains) { + drm_i915_private_t *dev_priv = dev->dev_private; + #if WATCH_EXEC DRM_INFO("%s: invalidate %08x flush %08x\n", __func__, invalidate_domains, flush_domains); #endif u32 cmd; - trace_i915_gem_request_flush(dev, ring->next_seqno, + trace_i915_gem_request_flush(dev, dev_priv->next_seqno, invalidate_domains, flush_domains); if ((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) { @@ -219,6 +221,20 @@ do { \ OUT_RING(0); \ } while (0) +static u32 i915_get_next_seqno(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 seqno; + + seqno = dev_priv->next_seqno; + + /* reserve 0 for non-seqno */ + if (++dev_priv->next_seqno == 0) + dev_priv->next_seqno = 1; + + return seqno; +} + /** * Creates a new sequence number, emitting a write of it to the status page * plus an interrupt, which will trigger i915_user_interrupt_handler. @@ -233,9 +249,10 @@ render_ring_add_request(struct drm_device *dev, struct drm_file *file_priv, u32 flush_domains) { - u32 seqno; drm_i915_private_t *dev_priv = dev->dev_private; - seqno = intel_ring_get_seqno(dev, ring); + u32 seqno; + + seqno = i915_get_next_seqno(dev); if (IS_GEN6(dev)) { BEGIN_LP_RING(6); @@ -405,7 +422,9 @@ bsd_ring_add_request(struct drm_device *dev, u32 flush_domains) { u32 seqno; - seqno = intel_ring_get_seqno(dev, ring); + + seqno = i915_get_next_seqno(dev); + intel_ring_begin(dev, ring, 4); intel_ring_emit(dev, ring, MI_STORE_DWORD_INDEX); intel_ring_emit(dev, ring, @@ -838,18 +857,6 @@ void intel_fill_struct(struct drm_device *dev, intel_ring_advance(dev, ring); } -u32 intel_ring_get_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring) -{ - u32 seqno; - seqno = ring->next_seqno; - - /* reserve 0 for non-seqno */ - if (++ring->next_seqno == 0) - ring->next_seqno = 1; - return seqno; -} - struct intel_ring_buffer render_ring = { .name = "render ring", .regs = { @@ -867,7 +874,6 @@ struct intel_ring_buffer render_ring = { .head = 0, .tail = 0, .space = 0, - .next_seqno = 1, .user_irq_refcount = 0, .irq_gem_seqno = 0, .waiting_gem_seqno = 0, @@ -906,7 +912,6 @@ struct intel_ring_buffer bsd_ring = { .head = 0, .tail = 0, .space = 0, - .next_seqno = 1, .user_irq_refcount = 0, .irq_gem_seqno = 0, .waiting_gem_seqno = 0, diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index ff5db57..d16ec36 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -128,9 +128,6 @@ void intel_fill_struct(struct drm_device *dev, void intel_ring_advance(struct drm_device *dev, struct intel_ring_buffer *ring); -u32 intel_ring_get_seqno(struct drm_device *dev, - struct intel_ring_buffer *ring); - extern struct intel_ring_buffer render_ring; extern struct intel_ring_buffer bsd_ring; -- 1.7.1
_______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx