On Sat, Oct 15, 2011 at 01:47:16PM -0700, Ben Widawsky wrote: > Idle the GPU before doing any unmaps. We know if VT-d is in use through > an exported variable from iommu code. > > This should avoid a known HW issue. > > Signed-off-by: Ben Widawsky <b...@bwidawsk.net> > --- > drivers/char/agp/intel-gtt.c | 28 ++++++++++++++++++++++++++++ > drivers/gpu/drm/i915/i915_drv.h | 2 +- > drivers/gpu/drm/i915/i915_gem.c | 4 +++- > drivers/gpu/drm/i915/i915_gem_gtt.c | 19 ++++++++++++++++++- > include/drm/intel-gtt.h | 2 ++ > 5 files changed, 52 insertions(+), 3 deletions(-) > > diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c > index 8515101..80a7ed0 100644 > --- a/drivers/char/agp/intel-gtt.c > +++ b/drivers/char/agp/intel-gtt.c > @@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct > agp_memory *mem, > { > int ret = -EINVAL; > > + if (intel_private.base.do_idle_maps) > + return -ENODEV; > + > if (intel_private.clear_fake_agp) { > int start = intel_private.base.stolen_size / PAGE_SIZE; > int end = intel_private.base.gtt_mappable_entries; > @@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct > agp_memory *mem, > if (mem->page_count == 0) > return 0; > > + if (intel_private.base.do_idle_maps) > + return -ENODEV; > +
This reminds me to kill the fake agp driver fro gen5+ with fire (we only need it for ums code). Infrastructure is half-way there, but not quite yet ... > intel_gtt_clear_range(pg_start, mem->page_count); > > if (intel_private.base.needs_dmar) { > @@ -1177,6 +1183,25 @@ static void gen6_cleanup(void) > { > } > > +/* Certain Gen5 chipsets require require idling the GPU before > + * unmapping anything from the GTT when VT-d is enabled. > + */ > +extern int intel_iommu_gfx_mapped; > +static inline int needs_idle_maps(void) > +{ > + const unsigned short gpu_devid = intel_private.pcidev->device; > + > + /* Query intel_iommu to see if we need the workaround. Presumably that > + * was loaded first. > + */ > + if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || > + gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && > + intel_iommu_gfx_mapped) > + return 1; > + > + return 0; > +} > + > static int i9xx_setup(void) > { > u32 reg_addr; > @@ -1211,6 +1236,9 @@ static int i9xx_setup(void) > intel_private.gtt_bus_addr = reg_addr + gtt_offset; > } > > + if (needs_idle_maps()); > + intel_private.base.do_idle_maps = 1; > + > intel_i9xx_setup_flush(); > > return 0; > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index 15c0ca5..3aa8612 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -1219,7 +1219,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device > *dev); > int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj); > void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, > enum i915_cache_level cache_level); > -void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); > +int i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); > > /* i915_gem_evict.c */ > int __must_check i915_gem_evict_something(struct drm_device *dev, int > min_size, > diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c > index a925141..89d22eb 100644 > --- a/drivers/gpu/drm/i915/i915_gem.c > +++ b/drivers/gpu/drm/i915/i915_gem.c > @@ -2130,7 +2130,9 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) > > trace_i915_gem_object_unbind(obj); > > - i915_gem_gtt_unbind_object(obj); > + ret = i915_gem_gtt_unbind_object(obj); > + if (ret == -ERESTARTSYS) > + return ret; > i915_gem_object_put_pages_gtt(obj); > > list_del_init(&obj->gtt_list); > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c > b/drivers/gpu/drm/i915/i915_gem_gtt.c > index 7a709cd..1df6395 100644 > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c > @@ -49,6 +49,15 @@ static unsigned int cache_level_to_agp_type(struct > drm_device *dev, > } > } > > +static bool do_idling(struct drm_i915_private *dev_priv) > +{ > + if (dev_priv->mm.gtt->do_idle_maps) > + if (i915_gpu_idle(dev_priv->dev)) > + return false; > + > + return true; > +} > + > void i915_gem_restore_gtt_mappings(struct drm_device *dev) > { > struct drm_i915_private *dev_priv = dev->dev_private; > @@ -115,8 +124,14 @@ void i915_gem_gtt_rebind_object(struct > drm_i915_gem_object *obj, > agp_type); > } > > -void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) > +int 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 (do_idling(dev_priv) == false) > + return -ERESTARTSYS; That's a bit a lie, gpu_idle can also return -ENOMEM, -EAGAIN and -EIO. I think you could just inline do_idling to avoid passing back the retval twice ... > + > intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, > obj->base.size >> PAGE_SHIFT); > > @@ -124,4 +139,6 @@ void i915_gem_gtt_unbind_object(struct > drm_i915_gem_object *obj) > intel_gtt_unmap_memory(obj->sg_list, obj->num_sg); > obj->sg_list = NULL; > } > + > + return 0; > } > diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h > index 9e343c0..b174620 100644 > --- a/include/drm/intel-gtt.h > +++ b/include/drm/intel-gtt.h > @@ -13,6 +13,8 @@ const struct intel_gtt { > unsigned int gtt_mappable_entries; > /* Whether i915 needs to use the dmar apis or not. */ > unsigned int needs_dmar : 1; > + /* Whether we idle the gpu before mapping/unmapping */ > + unsigned int do_idle_maps : 1; > } *intel_gtt_get(void); > > void intel_gtt_chipset_flush(void); > -- > 1.7.7 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Daniel Vetter Mail: dan...@ffwll.ch Mobile: +41 (0)79 365 57 48 _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx