commit 27eb2d8333346831fc109fbbe1db8bfb445db12b Author: Daniel Vetter Date: Fri Sep 16 21:44:10 2011 +0200 drm/i915: work around i830/i845 tlb issue This time under the assumption that the tlb used for cpu access to the gtt is broken. The goal is to completely flush the tlb because the errata claims that the wrong tlb entries get flushed. To do this stop all cpu access to the gtt by shooting down all mappings and then rewrite the complete gtt table. To make a bit less expensive, try to batch up as much as possible but remember to rewrite the gtt when dropping the struct_mutex lock (for another process might jump in an pagefault). Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7916bd9..70cf03b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -643,6 +643,8 @@ typedef struct drm_i915_private { * transitioned away from for kernel modesetting. */ int suspended; + unsigned lazy_tlb : 1; + unsigned dirty_tlb : 1; /** * Flag if the hardware appears to be wedged. diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a546a71..4370962 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -139,6 +139,10 @@ void i915_gem_do_init(struct drm_device *dev, dev_priv->mm.gtt_total = end - start; dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; + if (IS_GEN2(dev)) { + dev_priv->mm.lazy_tlb = 1; + } + /* Take over this portion of the GTT */ intel_gtt_clear_range(start / PAGE_SIZE, (end-start) / PAGE_SIZE); } @@ -952,6 +956,7 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_pwrite *args = data; struct drm_i915_gem_object *obj; int ret; @@ -1006,6 +1011,11 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, if (ret) goto out_unpin; + if (dev_priv->mm.dirty_tlb) { + i915_gem_restore_gtt_mappings(dev); + dev_priv->mm.dirty_tlb = 0; + } + ret = i915_gem_object_put_fence(obj); if (ret) goto out_unpin; @@ -1237,6 +1247,11 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) pfn = ((dev->agp->base + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; + if (dev_priv->mm.dirty_tlb) { + i915_gem_restore_gtt_mappings(dev); + dev_priv->mm.dirty_tlb = 0; + } + /* Finally, remap it using the new GTT offset */ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); unlock: diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 4934cf8..5c4dcd8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -623,6 +623,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, struct drm_i915_gem_exec_object2 *exec, int count) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_relocation_entry *reloc; struct drm_i915_gem_object *obj; int *reloc_offset; @@ -637,6 +638,11 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, drm_gem_object_unreference(&obj->base); } + if (dev_priv->mm.dirty_tlb) { + i915_gem_restore_gtt_mappings(dev); + dev_priv->mm.dirty_tlb = 0; + } + mutex_unlock(&dev->struct_mutex); total = 0; @@ -1195,6 +1201,11 @@ err: drm_gem_object_unreference(&obj->base); } + if (dev_priv->mm.dirty_tlb) { + i915_gem_restore_gtt_mappings(dev); + dev_priv->mm.dirty_tlb = 0; + } + mutex_unlock(&dev->struct_mutex); pre_mutex_err: diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 7a709cd..81e5763 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -29,6 +29,21 @@ #include "i915_trace.h" #include "intel_drv.h" +static void i915_stop_cpu_gtt_acccess(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + + if (dev_priv->mm.dirty_tlb) + return; + + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + i915_gem_release_mmap(obj); + } + + dev_priv->mm.dirty_tlb = 1; +} + /* XXX kill agp_type! */ static unsigned int cache_level_to_agp_type(struct drm_device *dev, enum i915_cache_level cache_level) @@ -73,6 +88,11 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj) unsigned int agp_type = cache_level_to_agp_type(dev, obj->cache_level); int ret; + if (dev_priv->mm.lazy_tlb) { + i915_stop_cpu_gtt_acccess(dev); + return 0; + } + if (dev_priv->mm.gtt->needs_dmar) { ret = intel_gtt_map_memory(obj->pages, obj->base.size >> PAGE_SHIFT, @@ -117,6 +137,13 @@ void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->mm.lazy_tlb) { + i915_stop_cpu_gtt_acccess(dev); + } + intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, obj->base.size >> PAGE_SHIFT);