drm/i915: Update write_domains on active list after flush.

Before changing the status of a buffer with a pending write we will await
upon a new flush for that buffer. So we can take advantage of any flushes
posted whilst the buffer is active and pending processing by the GPU, by
clearing its write_domain and updating its last_rendering_seqno -- thus
saving a potential flush in deep queues and improves flushing behaviour
upon eviction for both GTT space and fences.

In order to reduce the time spent searching the active list for matching
write_domains, we move those to a separate list whose elements are
the buffers belong to the active/flushing list with pending writes.

Orignal patch by Chris Wilson <chris@chris-wilson.co.uk>, forward-ported
by me.

In addition to better performance, this also fixes a real bug. Before
this changes, i915_gem_evict_everything didn't work as advertised. When
the gpu was actually busy and processing request, the flush and subsequent
wait would not move active and dirty buffers to the inactive list, but
just to the flushing list. Which triggered the BUG_ON at the end of this
function. With the more tight dirty buffer tracking, all currently busy and
dirty buffers get moved to the inactive list by one i915_gem_flush operation.

I've left the BUG_ON I've used to prove this in there.

References:
Bug 25911 - 2.10.0 causes kernel oops and system hangs
http://bugs.freedesktop.org/show_bug.cgi?id=25911

Bug 26101 - [i915] xf86-video-intel 2.10.0 (and git) triggers kernel oops
within seconds after login
http://bugs.freedesktop.org/show_bug.cgi?id=26101

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Tested-by: Adam Lantos <hege@playma.org>
Cc: stable@kernel.org
Signed-off-by: Eric Anholt <eric@anholt.net>

authored by Daniel Vetter and committed by Eric Anholt 99fcb766 b91ad0ec

+30 -4
+11
drivers/gpu/drm/i915/i915_drv.h
··· 493 struct list_head flushing_list; 494 495 /** 496 * LRU list of objects which are not in the ringbuffer and 497 * are ready to unbind, but are still in the GTT. 498 * ··· 601 602 /** This object's place on the active/flushing/inactive lists */ 603 struct list_head list; 604 605 /** This object's place on the fenced object LRU */ 606 struct list_head fence_list;
··· 493 struct list_head flushing_list; 494 495 /** 496 + * List of objects currently pending a GPU write flush. 497 + * 498 + * All elements on this list will belong to either the 499 + * active_list or flushing_list, last_rendering_seqno can 500 + * be used to differentiate between the two elements. 501 + */ 502 + struct list_head gpu_write_list; 503 + 504 + /** 505 * LRU list of objects which are not in the ringbuffer and 506 * are ready to unbind, but are still in the GTT. 507 * ··· 592 593 /** This object's place on the active/flushing/inactive lists */ 594 struct list_head list; 595 + /** This object's place on GPU write list */ 596 + struct list_head gpu_write_list; 597 598 /** This object's place on the fenced object LRU */ 599 struct list_head fence_list;
+19 -4
drivers/gpu/drm/i915/i915_gem.c
··· 1552 else 1553 list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); 1554 1555 obj_priv->last_rendering_seqno = 0; 1556 if (obj_priv->active) { 1557 obj_priv->active = 0; ··· 1624 struct drm_i915_gem_object *obj_priv, *next; 1625 1626 list_for_each_entry_safe(obj_priv, next, 1627 - &dev_priv->mm.flushing_list, list) { 1628 struct drm_gem_object *obj = obj_priv->obj; 1629 1630 if ((obj->write_domain & flush_domains) == ··· 1633 uint32_t old_write_domain = obj->write_domain; 1634 1635 obj->write_domain = 0; 1636 i915_gem_object_move_to_active(obj, seqno); 1637 1638 trace_i915_gem_object_change_domain(obj, ··· 2088 i915_gem_evict_everything(struct drm_device *dev) 2089 { 2090 drm_i915_private_t *dev_priv = dev->dev_private; 2091 - uint32_t seqno; 2092 int ret; 2093 bool lists_empty; 2094 2095 spin_lock(&dev_priv->mm.active_list_lock); ··· 2110 ret = i915_wait_request(dev, seqno); 2111 if (ret) 2112 return ret; 2113 2114 ret = i915_gem_evict_from_inactive_list(dev); 2115 if (ret) ··· 2707 old_write_domain = obj->write_domain; 2708 i915_gem_flush(dev, 0, obj->write_domain); 2709 seqno = i915_add_request(dev, NULL, obj->write_domain); 2710 - obj->write_domain = 0; 2711 i915_gem_object_move_to_active(obj, seqno); 2712 2713 trace_i915_gem_object_change_domain(obj, ··· 3856 i915_gem_flush(dev, 3857 dev->invalidate_domains, 3858 dev->flush_domains); 3859 - if (dev->flush_domains) 3860 (void)i915_add_request(dev, file_priv, 3861 dev->flush_domains); 3862 } 3863 3864 for (i = 0; i < args->buffer_count; i++) { 3865 struct drm_gem_object *obj = object_list[i]; 3866 uint32_t old_write_domain = obj->write_domain; 3867 3868 obj->write_domain = obj->pending_write_domain; 3869 trace_i915_gem_object_change_domain(obj, 3870 obj->read_domains, 3871 old_write_domain); ··· 4383 obj_priv->obj = obj; 4384 obj_priv->fence_reg = I915_FENCE_REG_NONE; 4385 INIT_LIST_HEAD(&obj_priv->list); 4386 INIT_LIST_HEAD(&obj_priv->fence_list); 4387 obj_priv->madv = I915_MADV_WILLNEED; 4388 ··· 4835 spin_lock_init(&dev_priv->mm.active_list_lock); 4836 INIT_LIST_HEAD(&dev_priv->mm.active_list); 4837 INIT_LIST_HEAD(&dev_priv->mm.flushing_list); 4838 INIT_LIST_HEAD(&dev_priv->mm.inactive_list); 4839 INIT_LIST_HEAD(&dev_priv->mm.request_list); 4840 INIT_LIST_HEAD(&dev_priv->mm.fence_list);
··· 1552 else 1553 list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); 1554 1555 + BUG_ON(!list_empty(&obj_priv->gpu_write_list)); 1556 + 1557 obj_priv->last_rendering_seqno = 0; 1558 if (obj_priv->active) { 1559 obj_priv->active = 0; ··· 1622 struct drm_i915_gem_object *obj_priv, *next; 1623 1624 list_for_each_entry_safe(obj_priv, next, 1625 + &dev_priv->mm.gpu_write_list, 1626 + gpu_write_list) { 1627 struct drm_gem_object *obj = obj_priv->obj; 1628 1629 if ((obj->write_domain & flush_domains) == ··· 1630 uint32_t old_write_domain = obj->write_domain; 1631 1632 obj->write_domain = 0; 1633 + list_del_init(&obj_priv->gpu_write_list); 1634 i915_gem_object_move_to_active(obj, seqno); 1635 1636 trace_i915_gem_object_change_domain(obj, ··· 2084 i915_gem_evict_everything(struct drm_device *dev) 2085 { 2086 drm_i915_private_t *dev_priv = dev->dev_private; 2087 int ret; 2088 + uint32_t seqno; 2089 bool lists_empty; 2090 2091 spin_lock(&dev_priv->mm.active_list_lock); ··· 2106 ret = i915_wait_request(dev, seqno); 2107 if (ret) 2108 return ret; 2109 + 2110 + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); 2111 2112 ret = i915_gem_evict_from_inactive_list(dev); 2113 if (ret) ··· 2701 old_write_domain = obj->write_domain; 2702 i915_gem_flush(dev, 0, obj->write_domain); 2703 seqno = i915_add_request(dev, NULL, obj->write_domain); 2704 + BUG_ON(obj->write_domain); 2705 i915_gem_object_move_to_active(obj, seqno); 2706 2707 trace_i915_gem_object_change_domain(obj, ··· 3850 i915_gem_flush(dev, 3851 dev->invalidate_domains, 3852 dev->flush_domains); 3853 + if (dev->flush_domains & I915_GEM_GPU_DOMAINS) 3854 (void)i915_add_request(dev, file_priv, 3855 dev->flush_domains); 3856 } 3857 3858 for (i = 0; i < args->buffer_count; i++) { 3859 struct drm_gem_object *obj = object_list[i]; 3860 + struct drm_i915_gem_object *obj_priv = obj->driver_private; 3861 uint32_t old_write_domain = obj->write_domain; 3862 3863 obj->write_domain = obj->pending_write_domain; 3864 + if (obj->write_domain) 3865 + list_move_tail(&obj_priv->gpu_write_list, 3866 + &dev_priv->mm.gpu_write_list); 3867 + else 3868 + list_del_init(&obj_priv->gpu_write_list); 3869 + 3870 trace_i915_gem_object_change_domain(obj, 3871 obj->read_domains, 3872 old_write_domain); ··· 4370 obj_priv->obj = obj; 4371 obj_priv->fence_reg = I915_FENCE_REG_NONE; 4372 INIT_LIST_HEAD(&obj_priv->list); 4373 + INIT_LIST_HEAD(&obj_priv->gpu_write_list); 4374 INIT_LIST_HEAD(&obj_priv->fence_list); 4375 obj_priv->madv = I915_MADV_WILLNEED; 4376 ··· 4821 spin_lock_init(&dev_priv->mm.active_list_lock); 4822 INIT_LIST_HEAD(&dev_priv->mm.active_list); 4823 INIT_LIST_HEAD(&dev_priv->mm.flushing_list); 4824 + INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list); 4825 INIT_LIST_HEAD(&dev_priv->mm.inactive_list); 4826 INIT_LIST_HEAD(&dev_priv->mm.request_list); 4827 INIT_LIST_HEAD(&dev_priv->mm.fence_list);