From 88f356b725c8a18c4da3ee0b6dcbc647418268f2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 4 Aug 2010 13:55:32 +0100 Subject: drm/i915: Only emit flushes on active rings. This avoids the excess flush and requests on idle rings (and spamming the debug log ;-) Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2a4ed7ca8b4e..fc61542cce8a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3117,6 +3117,7 @@ static void i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; @@ -3179,6 +3180,13 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj) obj->pending_write_domain = obj->write_domain; obj->read_domains = obj->pending_read_domains; + if (flush_domains & I915_GEM_GPU_DOMAINS) { + if (obj_priv->ring == &dev_priv->render_ring) + dev_priv->flush_rings |= FLUSH_RENDER_RING; + else if (obj_priv->ring == &dev_priv->bsd_ring) + dev_priv->flush_rings |= FLUSH_BSD_RING; + } + dev->invalidate_domains |= invalidate_domains; dev->flush_domains |= flush_domains; #if WATCH_BUF @@ -3718,7 +3726,6 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, ring = &dev_priv->render_ring; } - if (args->buffer_count < 1) { DRM_ERROR("execbuf with %d buffers\n", args->buffer_count); return -EINVAL; @@ -3892,6 +3899,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, */ dev->invalidate_domains = 0; dev->flush_domains = 0; + dev_priv->flush_rings = 0; for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; @@ -3912,16 +3920,14 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, i915_gem_flush(dev, dev->invalidate_domains, dev->flush_domains); - if (dev->flush_domains & I915_GEM_GPU_DOMAINS) { + if (dev_priv->flush_rings & FLUSH_RENDER_RING) (void)i915_add_request(dev, file_priv, - dev->flush_domains, - &dev_priv->render_ring); - - if (HAS_BSD(dev)) - (void)i915_add_request(dev, file_priv, - dev->flush_domains, - &dev_priv->bsd_ring); - } + dev->flush_domains, + &dev_priv->render_ring); + if (dev_priv->flush_rings & FLUSH_BSD_RING) + (void)i915_add_request(dev, file_priv, + dev->flush_domains, + &dev_priv->bsd_ring); } for (i = 0; i < args->buffer_count; i++) { -- cgit v1.2.1 From 0be555b66a871d711a89c301b37e763058d34d33 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 4 Aug 2010 15:36:30 +0100 Subject: drm/i915: report all active objects as busy Incorporates a similar patch by Daniel Vetter, the alteration being to report the current busy state after retiring. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fc61542cce8a..24ee4622484f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4365,22 +4365,34 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, } mutex_lock(&dev->struct_mutex); - /* Update the active list for the hardware's current position. - * Otherwise this only updates on a delayed timer or when irqs are - * actually unmasked, and our working set ends up being larger than - * required. - */ - i915_gem_retire_requests(dev); - obj_priv = to_intel_bo(obj); - /* Don't count being on the flushing list against the object being - * done. Otherwise, a buffer left on the flushing list but not getting - * flushed (because nobody's flushing that domain) won't ever return - * unbusy and get reused by libdrm's bo cache. The other expected - * consumer of this interface, OpenGL's occlusion queries, also specs - * that the objects get unbusy "eventually" without any interference. + /* Count all active objects as busy, even if they are currently not used + * by the gpu. Users of this interface expect objects to eventually + * become non-busy without any further actions, therefore emit any + * necessary flushes here. */ - args->busy = obj_priv->active && obj_priv->last_rendering_seqno != 0; + obj_priv = to_intel_bo(obj); + args->busy = obj_priv->active; + if (args->busy) { + /* Unconditionally flush objects, even when the gpu still uses this + * object. Userspace calling this function indicates that it wants to + * use this buffer rather sooner than later, so issuing the required + * flush earlier is beneficial. + */ + if (obj->write_domain) { + i915_gem_flush(dev, 0, obj->write_domain); + (void)i915_add_request(dev, file_priv, obj->write_domain, obj_priv->ring); + } + + /* Update the active list for the hardware's current position. + * Otherwise this only updates on a delayed timer or when irqs + * are actually unmasked, and our working set ends up being + * larger than required. + */ + i915_gem_retire_requests_ring(dev, obj_priv->ring); + + args->busy = obj_priv->active; + } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.1 From ae7d49d879fd196bded306e9465e0aa5a203e971 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 4 Aug 2010 12:37:41 +0100 Subject: drm/i915: Emit a backtrace if we attempt to rebind a pinned buffer This debugging trace was useful for finding the fbcon regression on i965, and it may prove useful again in future. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 24ee4622484f..b0fb394b9ff4 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4198,6 +4198,10 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) if (alignment == 0) alignment = i915_gem_get_gtt_alignment(obj); if (obj_priv->gtt_offset & (alignment - 1)) { + WARN(obj_priv->pin_count, + "bo is already pinned with incorrect alignment:" + " offset=%x, req.alignment=%x\n", + obj_priv->gtt_offset, alignment); ret = i915_gem_object_unbind(obj); if (ret) return ret; -- cgit v1.2.1 From bf1a10923920f56da23a118de2511a72af341d61 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 11:01:20 +0100 Subject: drm/i915: Append the object onto the inactive list on binding. In order to properly track bound objects, they need to exist on one of the inactive/active lists or be pinned. As this is a requirement, do the work inside i915_gem_bind_to_gtt() rather than dotted around the callsites. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b0fb394b9ff4..75e7b899e033 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1137,7 +1137,6 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); pgoff_t page_offset; unsigned long pfn; @@ -1155,8 +1154,6 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (ret) goto unlock; - list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); - ret = i915_gem_object_set_to_gtt_domain(obj, write); if (ret) goto unlock; @@ -1363,7 +1360,6 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_i915_gem_mmap_gtt *args = data; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; int ret; @@ -1409,7 +1405,6 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); return ret; } - list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); } drm_gem_object_unreference(obj); @@ -2723,6 +2718,9 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) atomic_inc(&dev->gtt_count); atomic_add(obj->size, &dev->gtt_memory); + /* keep track of bounds object by adding it to the inactive list */ + list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + /* Assert that the object is not currently in any GPU domain. As it * wasn't in the GTT, there shouldn't be any way it could have been in * a GPU cache @@ -4223,8 +4221,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) atomic_inc(&dev->pin_count); atomic_add(obj->size, &dev->pin_memory); if (!obj_priv->active && - (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0 && - !list_empty(&obj_priv->list)) + (obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) list_del_init(&obj_priv->list); } i915_verify_inactive(dev, __FILE__, __LINE__); -- cgit v1.2.1 From 0108a3edd5c2e3b150a550d565b6aa1a67c0edbe Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 7 Aug 2010 11:01:21 +0100 Subject: drm/i915: prepare for fair lru eviction This does two little changes: - Add an alignment parameter for evict_something. It's not really great to whack a carefully sized hole into the gtt with the wrong alignment. Especially since the fallback path is a full evict. - With the inactive scan stuff we need to evict more that one object, so move the unbind call into the helper function that scans for the object to be evicted, too. And adjust its name. No functional changes in this patch, just preparation. Signed-Off-by: Daniel Vetter Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 67 +++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 75e7b899e033..f150bfd2c851 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -35,6 +35,7 @@ #include #include +static uint32_t i915_gem_get_gtt_alignment(struct drm_gem_object *obj); static int i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); @@ -48,7 +49,8 @@ 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_something(struct drm_device *dev, int min_size, + unsigned alignment); 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, @@ -313,7 +315,8 @@ 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); + ret = i915_gem_evict_something(dev, obj->size, + i915_gem_get_gtt_alignment(obj)); if (ret) return ret; @@ -2005,10 +2008,12 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return ret; } -static struct drm_gem_object * -i915_gem_find_inactive_object(struct drm_device *dev, int min_size) +static int +i915_gem_scan_inactive_list_and_evict(struct drm_device *dev, int min_size, + unsigned alignment, int *found) { drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; struct drm_i915_gem_object *obj_priv; struct drm_gem_object *best = NULL; struct drm_gem_object *first = NULL; @@ -2022,14 +2027,31 @@ i915_gem_find_inactive_object(struct drm_device *dev, int min_size) (!best || obj->size < best->size)) { best = obj; if (best->size == min_size) - return best; + break; } if (!first) first = obj; } } - return best ? best : first; + obj = best ? best : first; + + if (!obj) { + *found = 0; + return 0; + } + + *found = 1; + +#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); } static int @@ -2115,11 +2137,11 @@ i915_gem_evict_everything(struct drm_device *dev) } static int -i915_gem_evict_something(struct drm_device *dev, int min_size) +i915_gem_evict_something(struct drm_device *dev, + int min_size, unsigned alignment) { drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_gem_object *obj; - int ret; + int ret, found; struct intel_ring_buffer *render_ring = &dev_priv->render_ring; struct intel_ring_buffer *bsd_ring = &dev_priv->bsd_ring; @@ -2129,20 +2151,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size) /* 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); - } + ret = i915_gem_scan_inactive_list_and_evict(dev, min_size, + alignment, + &found); + if (found) + return ret; /* If we didn't get anything, but the ring is still processing * things, wait for the next to finish and hopefully leave us @@ -2184,6 +2197,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size) * will get moved to inactive. */ if (!list_empty(&dev_priv->mm.flushing_list)) { + struct drm_gem_object *obj = NULL; struct drm_i915_gem_object *obj_priv; /* Find an object that we can immediately reuse */ @@ -2661,7 +2675,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) #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; @@ -2679,7 +2693,8 @@ 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); + ret = i915_gem_evict_something(dev, obj->size, + alignment); if (ret) { /* now try to shrink everyone else */ if (gfpmask) { @@ -2709,7 +2724,7 @@ 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); + ret = i915_gem_evict_something(dev, obj->size, alignment); if (ret) return ret; -- cgit v1.2.1 From 6f392d548658a17600da7faaf8a5df25ee5f01f6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 11:01:22 +0100 Subject: drm/i915: Use a common seqno for all rings. This will be used by the eviction logic to maintain fairness between the rings. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f150bfd2c851..45b998218d0c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4714,6 +4714,8 @@ i915_gem_init_ringbuffer(struct drm_device *dev) goto cleanup_render_ring; } + dev_priv->next_seqno = 1; + return 0; cleanup_render_ring: -- cgit v1.2.1 From b47eb4a2b302f33adaed2a27d2b3bfc74fe35ac5 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 11:01:23 +0100 Subject: drm/i915: Move the eviction logic to its own file. The eviction code is the gnarly underbelly of memory management, and is clearer if kept separated from the normal domain management in GEM. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 231 +--------------------------------------- 1 file changed, 2 insertions(+), 229 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 45b998218d0c..b5a7b00264a6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -49,9 +49,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, - unsigned alignment); -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); @@ -1885,19 +1882,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. @@ -2008,53 +1992,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return ret; } -static int -i915_gem_scan_inactive_list_and_evict(struct drm_device *dev, int min_size, - unsigned alignment, int *found) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_gem_object *obj; - 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) - break; - } - if (!first) - first = obj; - } - } - - obj = best ? best : first; - - if (!obj) { - *found = 0; - return 0; - } - - *found = 1; - -#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); -} - -static int +int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -2095,147 +2033,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, unsigned alignment) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int ret, found; - - 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); - - /* If there's an inactive buffer available now, grab it - * and be done. - */ - ret = i915_gem_scan_inactive_list_and_evict(dev, min_size, - alignment, - &found); - if (found) - return ret; - - /* 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_gem_object *obj = NULL; - 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) @@ -4548,30 +4345,6 @@ void i915_gem_free_object(struct drm_gem_object *obj) i915_gem_free_object_tail(obj); } -/** 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) { @@ -4596,7 +4369,7 @@ i915_gem_idle(struct drm_device *dev) /* Under UMS, be paranoid and evict. */ if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_gem_evict_from_inactive_list(dev); + ret = i915_gem_evict_inactive(dev); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; -- cgit v1.2.1 From 7d1c4804ae98cdee572d7d10d8a5deaa2e686285 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 21:45:03 +0100 Subject: drm/i915: Maintain LRU order of inactive objects upon access by CPU (v2) In order to reduce the penalty of fallbacks under memory pressure and to avoid a potential immediate ping-pong of evicting a mmaped buffer, we move the object to the tail of the inactive list when a page is freshly faulted or the object is moved into the CPU domain. We choose not to protect the CPU objects from casual eviction, preferring to keep the GPU active for as long as possible. v2: Daniel Vetter found a bug where I forgot that pinned objects are kept off the inactive list. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b5a7b00264a6..8f3e0c10c080 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -57,6 +57,14 @@ static void i915_gem_free_object_tail(struct drm_gem_object *obj); static LIST_HEAD(shrink_list); static DEFINE_SPINLOCK(shrink_list_lock); +static inline bool +i915_gem_object_is_inactive(struct drm_i915_gem_object *obj_priv) +{ + return obj_priv->gtt_space && + !obj_priv->active && + obj_priv->pin_count == 0; +} + int i915_gem_do_init(struct drm_device *dev, unsigned long start, unsigned long end) { @@ -1036,6 +1044,11 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } + + /* Maintain LRU order of "inactive" objects */ + if (ret == 0 && i915_gem_object_is_inactive(obj_priv)) + list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; @@ -1137,6 +1150,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); pgoff_t page_offset; unsigned long pfn; @@ -1166,6 +1180,9 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) goto unlock; } + if (i915_gem_object_is_inactive(obj_priv)) + list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) + page_offset; -- cgit v1.2.1 From ae9fed6b601821d70928797c56da0e2008ef840d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 11:01:30 +0100 Subject: drm/i915: Truncate the shmem backing pages on purge shmfs doesn't actually implement i_ops->truncate() so we were not immedatiately releasing the backing pages when shrinking the gfx cache under OOM. Instead use a combination of truncate_inode_pages() and i_ops->truncate_range() as is used by shmem_delete_inode(). Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8f3e0c10c080..41306217bd7a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1505,9 +1505,16 @@ i915_gem_object_truncate(struct drm_gem_object *obj) struct drm_i915_gem_object *obj_priv = to_intel_bo(obj); struct inode *inode; + /* Our goal here is to return as much of the memory as + * is possible back to the system as we are called from OOM. + * To do this we must instruct the shmfs to drop all of its + * backing pages, *now*. Here we mirror the actions taken + * when by shmem_delete_inode() to release the backing store. + */ inode = obj->filp->f_path.dentry->d_inode; - if (inode->i_op->truncate) - inode->i_op->truncate (inode); + truncate_inode_pages(inode->i_mapping, 0); + if (inode->i_op->truncate_range) + inode->i_op->truncate_range(inode, 0, (loff_t)-1); obj_priv->madv = __I915_MADV_PURGED; } -- cgit v1.2.1 From 6eeefaf3c86b8937db8ad930c48bfb592fc5e32e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 7 Aug 2010 11:01:39 +0100 Subject: drm/i915: Apply i830 errata for cursor alignment i830 requires 32bpp cursors to be aligned to 16KB, so we have to expose the alignment parameter to i915_gem_attach_phys_object(). Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 41306217bd7a..b4b25e17d4e9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4674,7 +4674,7 @@ i915_gem_load(struct drm_device *dev) * e.g. for cursor + overlay regs */ int i915_gem_init_phys_object(struct drm_device *dev, - int id, int size) + int id, int size, int align) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_phys_object *phys_obj; @@ -4689,7 +4689,7 @@ int i915_gem_init_phys_object(struct drm_device *dev, phys_obj->id = id; - phys_obj->handle = drm_pci_alloc(dev, size, 0); + phys_obj->handle = drm_pci_alloc(dev, size, align); if (!phys_obj->handle) { ret = -ENOMEM; goto kfree_obj; @@ -4771,7 +4771,9 @@ out: int i915_gem_attach_phys_object(struct drm_device *dev, - struct drm_gem_object *obj, int id) + struct drm_gem_object *obj, + int id, + int align) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv; @@ -4790,11 +4792,10 @@ i915_gem_attach_phys_object(struct drm_device *dev, i915_gem_detach_phys_object(dev, obj); } - /* create a new object */ if (!dev_priv->mm.phys_objs[id - 1]) { ret = i915_gem_init_phys_object(dev, id, - obj->size); + obj->size, align); if (ret) { DRM_ERROR("failed to init phys object %d size: %zu\n", id, obj->size); goto out; -- cgit v1.2.1 From 156dadc180a1bd3a25d644ee6c361afc465ccd0e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 15 Aug 2010 10:52:34 +0100 Subject: drm/i915: Remove the conflicting BUG_ON() We now attempt to free "active" objects following a GPU hang as either the GPU will be reset or the hang is permenant. In either case, the GPU writes will not be flushed to main memory and it should be safe to return that memory back to the system. The BUG_ON(active) is thus overkill and can erroneously fire after a EIO. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_gem.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b4b25e17d4e9..994e9f2d688b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1979,8 +1979,6 @@ i915_gem_object_unbind(struct drm_gem_object *obj) * cause memory corruption through use-after-free. */ - BUG_ON(obj_priv->active); - /* release the fence reg _after_ flushing */ if (obj_priv->fence_reg != I915_FENCE_REG_NONE) i915_gem_clear_fence_reg(obj); -- cgit v1.2.1