[Intel-gfx] [PATCH 1/2] drm/i915: Avoid concurrent access when marking the device as idle/busy
Daniel Vetter
daniel at ffwll.ch
Tue May 8 15:14:43 CEST 2012
On Thu, May 03, 2012 at 12:35:20PM +0100, Chris Wilson wrote:
> Whilst marking the device as active is protect by struct_mutex, we would
> mark the individual components as not-busy without any locking, leading
> to potential confusion and missed powersaving (or even performance
> loss). Move the softirq accesses to an atomic and defer the actual
> update until we acquire the struct_mutex in the worker.
>
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
I'm still confused about the locking, i.e. I think dev_priv->busy and
dev_priv->idle deserve small comments to explain what they're for. The
other thing is that the gpu_idle_timer reinvents our perfectly useable
delayed request retiring code (and in doing so is rather racy). I think we
should kill that and update the gpu busy/idleness in retire_work_handler.
That way the gpu would only be marked idle when it truely is.
-Daniel
> ---
> drivers/gpu/drm/i915/i915_dma.c | 2 +-
> drivers/gpu/drm/i915/i915_drv.h | 3 +-
> drivers/gpu/drm/i915/intel_display.c | 86 +++++++++++++++-------------------
> drivers/gpu/drm/i915/intel_drv.h | 1 -
> 4 files changed, 42 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
> index ee25584..ee402c5 100644
> --- a/drivers/gpu/drm/i915/i915_dma.c
> +++ b/drivers/gpu/drm/i915/i915_dma.c
> @@ -1701,7 +1701,7 @@ bool i915_gpu_busy(void)
> goto out_unlock;
> dev_priv = i915_mch_dev;
>
> - ret = dev_priv->busy;
> + ret = !!(dev_priv->busy & (1 << 31));
>
> out_unlock:
> spin_unlock(&mchdev_lock);
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 3815d9a..47e5722 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -778,7 +778,8 @@ typedef struct drm_i915_private {
> int lvds_downclock;
> struct work_struct idle_work;
> struct timer_list idle_timer;
> - bool busy;
> + unsigned busy;
> + atomic_t idle;
> u16 orig_clock;
> int child_dev_num;
> struct child_device_config *child_dev;
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index d5aa2d2..1428e6e 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -5519,14 +5519,7 @@ static void intel_gpu_idle_timer(unsigned long arg)
> struct drm_device *dev = (struct drm_device *)arg;
> drm_i915_private_t *dev_priv = dev->dev_private;
>
> - if (!list_empty(&dev_priv->mm.active_list)) {
> - /* Still processing requests, so just re-arm the timer. */
> - mod_timer(&dev_priv->idle_timer, jiffies +
> - msecs_to_jiffies(GPU_IDLE_TIMEOUT));
> - return;
> - }
> -
> - dev_priv->busy = false;
> + atomic_set_mask(1 << 31, &dev_priv->idle);
> queue_work(dev_priv->wq, &dev_priv->idle_work);
> }
>
> @@ -5535,19 +5528,9 @@ static void intel_gpu_idle_timer(unsigned long arg)
> static void intel_crtc_idle_timer(unsigned long arg)
> {
> struct intel_crtc *intel_crtc = (struct intel_crtc *)arg;
> - struct drm_crtc *crtc = &intel_crtc->base;
> - drm_i915_private_t *dev_priv = crtc->dev->dev_private;
> - struct intel_framebuffer *intel_fb;
> + drm_i915_private_t *dev_priv = intel_crtc->base.dev->dev_private;
>
> - intel_fb = to_intel_framebuffer(crtc->fb);
> - if (intel_fb && intel_fb->obj->active) {
> - /* The framebuffer is still being accessed by the GPU. */
> - mod_timer(&intel_crtc->idle_timer, jiffies +
> - msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
> - return;
> - }
> -
> - intel_crtc->busy = false;
> + atomic_set_mask(1 << intel_crtc->pipe, &dev_priv->idle);
> queue_work(dev_priv->wq, &dev_priv->idle_work);
> }
>
> @@ -5651,26 +5634,32 @@ static void intel_idle_update(struct work_struct *work)
> idle_work);
> struct drm_device *dev = dev_priv->dev;
> struct drm_crtc *crtc;
> - struct intel_crtc *intel_crtc;
> -
> - if (!i915_powersave)
> - return;
> + unsigned idle;
>
> mutex_lock(&dev->struct_mutex);
>
> - i915_update_gfx_val(dev_priv);
> + idle = atomic_xchg(&dev_priv->idle, 0);
> + if (idle & (1 << 31))
> + intel_sanitize_pm(dev);
>
> + if (!i915_powersave)
> + goto unlock;
> +
> + i915_update_gfx_val(dev_priv);
> list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> + struct intel_crtc *intel_crtc;
> +
> /* Skip inactive CRTCs */
> if (!crtc->fb)
> continue;
>
> intel_crtc = to_intel_crtc(crtc);
> - if (!intel_crtc->busy)
> + if (idle & (1 << intel_crtc->pipe))
> intel_decrease_pllclock(crtc);
> }
>
> -
> +unlock:
> + dev_priv->busy &= ~idle;
> mutex_unlock(&dev->struct_mutex);
> }
>
> @@ -5687,38 +5676,42 @@ static void intel_idle_update(struct work_struct *work)
> void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj)
> {
> drm_i915_private_t *dev_priv = dev->dev_private;
> - struct drm_crtc *crtc = NULL;
> - struct intel_framebuffer *intel_fb;
> - struct intel_crtc *intel_crtc;
> -
> - if (!drm_core_check_feature(dev, DRIVER_MODESET))
> - return;
> + struct drm_crtc *crtc;
> + unsigned busy = 0;
>
> - if (!dev_priv->busy) {
> + if ((dev_priv->busy & (1 << 31)) == 0) {
> intel_sanitize_pm(dev);
> - dev_priv->busy = true;
> - } else
> - mod_timer(&dev_priv->idle_timer, jiffies +
> - msecs_to_jiffies(GPU_IDLE_TIMEOUT));
> + busy |= 1 << 31;
> + }
> +
> + if (!i915_powersave)
> + goto out;
>
> list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> + struct intel_crtc *intel_crtc;
> +
> if (!crtc->fb)
> continue;
>
> intel_crtc = to_intel_crtc(crtc);
> - intel_fb = to_intel_framebuffer(crtc->fb);
> - if (intel_fb->obj == obj) {
> - if (!intel_crtc->busy) {
> + if (to_intel_framebuffer(crtc->fb)->obj == obj) {
> + if ((dev_priv->busy & (1 << intel_crtc->pipe)) == 0) {
> /* Non-busy -> busy, upclock */
> intel_increase_pllclock(crtc);
> - intel_crtc->busy = true;
> - } else {
> - /* Busy -> busy, put off timer */
> - mod_timer(&intel_crtc->idle_timer, jiffies +
> - msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
> + busy |= 1 << intel_crtc->pipe;
> }
> +
> + mod_timer(&intel_crtc->idle_timer, jiffies +
> + msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
> }
> }
> +
> +out:
> + mod_timer(&dev_priv->idle_timer, jiffies +
> + msecs_to_jiffies(GPU_IDLE_TIMEOUT));
> +
> + atomic_clear_mask(busy, &dev_priv->idle);
> + dev_priv->busy |= busy;
> }
>
> static void intel_crtc_destroy(struct drm_crtc *crtc)
> @@ -6311,7 +6304,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
>
> drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
>
> - intel_crtc->busy = false;
> setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
> (unsigned long)intel_crtc);
>
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 0bd6d8a..64c2e7d 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -165,7 +165,6 @@ struct intel_crtc {
> u8 lut_r[256], lut_g[256], lut_b[256];
> int dpms_mode;
> bool active; /* is the crtc on? independent of the dpms mode */
> - bool busy; /* is scanout buffer being updated frequently? */
> struct timer_list idle_timer;
> bool lowfreq_avail;
> struct intel_overlay *overlay;
> --
> 1.7.10
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Daniel Vetter
Mail: daniel at ffwll.ch
Mobile: +41 (0)79 365 57 48
More information about the Intel-gfx
mailing list