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