[Intel-gfx] [PATCH] drm/i915: fix locking around ironlake_enable|disable_display_irq

Paulo Zanoni przanoni at gmail.com
Wed Jun 26 23:15:42 CEST 2013


2013/6/25 Daniel Vetter <daniel.vetter at ffwll.ch>:
> The haswell unclaimed register handling code forgot to take the
> spinlock. Since this is in the context of the non-rentrant interupt
> handler and we only have one interrupt handler it is sufficient to
> just grab the spinlock - we do not need to exclude any other
> interrupts from running on the same cpu.
>
> To prevent such gaffles in the future sprinkle assert_spin_locked over
> these functions. Unfornately this requires us to hold the spinlock in
> the ironlake postinstall hook where it is not strictly required:
> Currently that is run in single-threaded context and with userspace
> exlcuded from running concurrent ioctls. Add a comment explaining
> this.
>
> v2: ivb_can_enable_err_int also needs to be protected by the spinlock.
> To ensure this won't happen in the future again also sprinkle a
> spinlock assert in there.

Why does ivb_can_enable_err_int need it? Maybe we should add a comment
inside the function explaining why it needs it.


>
> Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
>  drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++++++++++++++++---
>  1 file changed, 24 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index c482e8a..ff1fed4 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -95,6 +95,8 @@ static void i915_hpd_irq_setup(struct drm_device *dev);
>  static void
>  ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
>  {
> +       assert_spin_locked(&dev_priv->irq_lock);
> +
>         if ((dev_priv->irq_mask & mask) != 0) {
>                 dev_priv->irq_mask &= ~mask;
>                 I915_WRITE(DEIMR, dev_priv->irq_mask);
> @@ -105,6 +107,8 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
>  static void
>  ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
>  {
> +       assert_spin_locked(&dev_priv->irq_lock);
> +
>         if ((dev_priv->irq_mask & mask) != mask) {
>                 dev_priv->irq_mask |= mask;
>                 I915_WRITE(DEIMR, dev_priv->irq_mask);
> @@ -118,6 +122,8 @@ static bool ivb_can_enable_err_int(struct drm_device *dev)
>         struct intel_crtc *crtc;
>         enum pipe pipe;
>
> +       assert_spin_locked(&dev_priv->irq_lock);
> +
>         for_each_pipe(pipe) {
>                 crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
>
> @@ -1218,8 +1224,11 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
>         /* On Haswell, also mask ERR_INT because we don't want to risk
>          * generating "unclaimed register" interrupts from inside the interrupt
>          * handler. */
> -       if (IS_HASWELL(dev))
> +       if (IS_HASWELL(dev)) {
> +               spin_lock(&dev_priv->irq_lock);
>                 ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
> +               spin_unlock(&dev_priv->irq_lock);
> +       }
>
>         gt_iir = I915_READ(GTIIR);
>         if (gt_iir) {
> @@ -1272,8 +1281,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
>                 ret = IRQ_HANDLED;
>         }
>
> -       if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev))
> -               ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
> +       if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev)) {
> +               spin_lock(&dev_priv->irq_lock);
> +               if (ivb_can_enable_err_int(dev))

You're calling ivb_can_enable_err_int twice here.


> +                       ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
> +               spin_unlock(&dev_priv->irq_lock);
> +       }
>
>         I915_WRITE(DEIER, de_ier);
>         POSTING_READ(DEIER);
> @@ -2633,6 +2646,8 @@ static void ibx_irq_postinstall(struct drm_device *dev)
>
>  static int ironlake_irq_postinstall(struct drm_device *dev)
>  {
> +       unsigned long irqflags;
> +
>         drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
>         /* enable kind of interrupts always enabled */
>         u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
> @@ -2671,7 +2686,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
>                 /* Clear & enable PCU event interrupts */
>                 I915_WRITE(DEIIR, DE_PCU_EVENT);
>                 I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT);
> +
> +               /* spinlocking not required here for correctness since interrupt
> +                * setup is guaranteed to run in single-threaded context. But we
> +                * need it to make the assert_spin_locked happy. */
> +               spin_lock_irqsave(&dev_priv->irq_lock, irqflags);

If spinlocking is not even required, why take the irqsave one instead
of just spin_lock()?


>                 ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT);
> +               spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
>         }
>
>         return 0;
> --
> 1.8.1.4
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Paulo Zanoni



More information about the Intel-gfx mailing list