[Intel-gfx] [PATCH v2 7/8] drm/i915: sanitize rps irq disabling
Imre Deak
imre.deak at intel.com
Mon Nov 17 20:15:21 CET 2014
On Mon, 2014-11-17 at 16:54 -0200, Paulo Zanoni wrote:
> 2014-11-10 11:44 GMT-02:00 Imre Deak <imre.deak at intel.com>:
> > When disabling the RPS interrupts there is a tricky dependency between
> > the thread disabling the interrupts, the RPS interrupt handler and the
> > corresponding RPS work. The RPS work can reenable the interrupts, so
> > there is no straightforward order in the disabling thread to (1) make
> > sure that any RPS work is flushed and to (2) disable all RPS
> > interrupts. Currently this is solved by masking the interrupts using two
> > separate mask registers (first level display IMR and PM IMR) and doing
> > the disabling when all first level interrupts are disabled.
> >
> > This works, but the requirement to run with all first level interrupts
> > disabled is unnecessary making the suspend / unload time ordering of RPS
> > disabling wrt. other unitialization steps difficult and error prone.
> > Removing this restriction allows us to disable RPS early during suspend
> > / unload and forget about it for the rest of the sequence. By adding a
> > more explicit method for avoiding the above race, it also becomes easier
> > to prove its correctness. Finally currently we can hit the WARN in
> > snb_update_pm_irq(), when a final RPS work runs with the first level
> > interrupts already disabled. This won't lead to any problem (due to the
> > separate interrupt masks), but with the change in this and the next
> > patch we can get rid of the WARN, while leaving it in place for other
> > scenarios.
> >
> > To address the above points, add a new RPS interrupts_enabled flag and
> > use this during RPS disabling to avoid requeuing the RPS work and
> > reenabling of the RPS interrupts. Since the interrupt disabling happens
> > now in intel_suspend_gt_powersave(), we will disable RPS interrupts
> > explicitly during suspend (and not just through the first level mask),
> > but there is no problem doing so, it's also more consistent and allows
> > us to unify more of the RPS disabling during suspend and unload time in
> > the next patch.
> >
>
> First of all: yes, this is a tricky problem and I like the general
> idea of adding interrupts_enabled to help make things a little more
> sane.
>
> Is there any test case to exercise the WARN you mentioned above? Is it
> possible to write one? Is there any bug reported on that WARN? It
> would be nice to see some "Testcase:" and "Bugzilla:" tags in this
> patch.
This one doesn't yet fix the problem, only the next patch. And there I
think I added the reference to the bug and testcase. I saw this with the
pm_rpm testcase on VLV, so hopefully that's enough :)
>
> A little more below:
>
> > v2:
> > - rebase on v2 of patch "drm/i915: move rps irq disable one level up"
> >
> > Signed-off-by: Imre Deak <imre.deak at intel.com>
> > ---
> > drivers/gpu/drm/i915/i915_drv.h | 6 +++++-
> > drivers/gpu/drm/i915/i915_irq.c | 23 ++++++++++++++++-------
> > drivers/gpu/drm/i915/intel_pm.c | 17 +++++++++--------
> > 3 files changed, 30 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index f830596..14e8f82 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -978,8 +978,12 @@ struct intel_rps_ei {
> > };
> >
> > struct intel_gen6_power_mgmt {
> > - /* work and pm_iir are protected by dev_priv->irq_lock */
> > + /*
> > + * work, interrupts_enabled and pm_iir are protected by
> > + * dev_priv->irq_lock
> > + */
> > struct work_struct work;
> > + bool interrupts_enabled;
> > u32 pm_iir;
> >
> > /* Frequencies are stored in potentially platform dependent multiples.
> > diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> > index 89a7be1..2677760 100644
> > --- a/drivers/gpu/drm/i915/i915_irq.c
> > +++ b/drivers/gpu/drm/i915/i915_irq.c
> > @@ -271,6 +271,7 @@ void gen6_enable_rps_interrupts(struct drm_device *dev)
> >
> > spin_lock_irq(&dev_priv->irq_lock);
> > WARN_ON(dev_priv->rps.pm_iir);
> > + dev_priv->rps.interrupts_enabled = true;
> > gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
> > spin_unlock_irq(&dev_priv->irq_lock);
> > }
> > @@ -279,14 +280,16 @@ void gen6_disable_rps_interrupts(struct drm_device *dev)
> > {
> > struct drm_i915_private *dev_priv = dev->dev_private;
> >
> > + spin_lock_irq(&dev_priv->irq_lock);
> > + dev_priv->rps.interrupts_enabled = false;
> > + spin_unlock_irq(&dev_priv->irq_lock);
> > +
> > + cancel_work_sync(&dev_priv->rps.work);
> > +
> > I915_WRITE(GEN6_PMINTRMSK, INTEL_INFO(dev_priv)->gen >= 8 ?
> > ~GEN8_PMINTR_REDIRECT_TO_NON_DISP : ~0);
> > I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) &
> > ~dev_priv->pm_rps_events);
> > - /* Complete PM interrupt masking here doesn't race with the rps work
> > - * item again unmasking PM interrupts because that is using a different
> > - * register (PMIMR) to mask PM interrupts. The only risk is in leaving
> > - * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
> >
> > spin_lock_irq(&dev_priv->irq_lock);
> > dev_priv->rps.pm_iir = 0;
> > @@ -1133,6 +1136,11 @@ static void gen6_pm_rps_work(struct work_struct *work)
> > int new_delay, adj;
> >
> > spin_lock_irq(&dev_priv->irq_lock);
> > + /* Speed up work cancelation during disabling rps interrupts. */
> > + if (!dev_priv->rps.interrupts_enabled) {
> > + spin_unlock_irq(&dev_priv->irq_lock);
> > + return;
> > + }
>
> Don't you also need to "dev_priv->rps.pm_iir = 0" before the return
> above? Just in case things were canceled after rps.work was launched,
> but before it was ran.
That wouldn't in itself guarantee that we'll have rps.pm_iir = 0 in the
end. But rps.pm_iir is reset in gen6_disable_rps_interrupts() after no
RPS interrupts or work can happen, which should be enough.
>
>
> > pm_iir = dev_priv->rps.pm_iir;
> > dev_priv->rps.pm_iir = 0;
> > /* Make sure not to corrupt PMIMR state used by ringbuffer on GEN6 */
> > @@ -1706,11 +1714,12 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
> >
> > if (pm_iir & dev_priv->pm_rps_events) {
> > spin_lock(&dev_priv->irq_lock);
> > - dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
> > gen6_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
> > + if (dev_priv->rps.interrupts_enabled) {
> > + dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
> > + queue_work(dev_priv->wq, &dev_priv->rps.work);
> > + }
> > spin_unlock(&dev_priv->irq_lock);
> > -
> > - queue_work(dev_priv->wq, &dev_priv->rps.work);
> > }
> >
> > if (INTEL_INFO(dev_priv)->gen >= 8)
> > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> > index f555810..dcdd269 100644
> > --- a/drivers/gpu/drm/i915/intel_pm.c
> > +++ b/drivers/gpu/drm/i915/intel_pm.c
> > @@ -6179,9 +6179,17 @@ void intel_suspend_gt_powersave(struct drm_device *dev)
> > /* Interrupts should be disabled already to avoid re-arming. */
> > WARN_ON(intel_irqs_enabled(dev_priv));
> >
> > + if (INTEL_INFO(dev)->gen < 6)
> > + return;
> > +
> > flush_delayed_work(&dev_priv->rps.delayed_resume_work);
> >
> > - cancel_work_sync(&dev_priv->rps.work);
> > + /*
> > + * TODO: disable RPS interrupts on GEN9 too once RPS support
> > + * is added for it.
> > + */
> > + if (INTEL_INFO(dev)->gen != 9)
> > + gen6_disable_rps_interrupts(dev);
> >
> > /* Force GPU to min freq during suspend */
> > gen6_rps_idle(dev_priv);
> > @@ -6210,13 +6218,6 @@ void intel_disable_gt_powersave(struct drm_device *dev)
> > else
> > gen6_disable_rps(dev);
> >
> > - /*
> > - * TODO: disable RPS interrupts on GEN9 too once RPS support
> > - * is added for it.
> > - */
> > - if (INTEL_INFO(dev)->gen != 9)
> > - gen6_disable_rps_interrupts(dev);
>
> But then, what about the users that call intel_disable_gt_powersave()
> without calling intel_suspend_gt_powersave()? Don't they get problems
> because the interrupts are still enabled?
intel_suspend_gt_powersave() is called from
intel_disable_gt_powersave(). Or do you meant something else?
>
>
> > -
> > dev_priv->rps.enabled = false;
> > mutex_unlock(&dev_priv->rps.hw_lock);
> > }
> > --
> > 1.8.4
> >
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx at lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
>
>
More information about the Intel-gfx
mailing list