[PATCH 06/10] drm/radeon: fix & improve ih ring handling

j.glisse j.glisse at gmail.com
Thu May 24 08:59:09 PDT 2012


On Thu, May 24, 2012 at 09:49:10AM +0200, Christian König wrote:
> From: Christian Koenig <christian.koenig at amd.com>
> 
> The spinlock was actually there to protect the
> rptr, but rptr was read outside of the locked area.
> 
> Also we don't really need a spinlock here, an
> atomic should to quite fine since we only need to
> prevent it from being reentrant.
> 
> Signed-off-by: Christian Koenig <christian.koenig at amd.com>

Reviewed-by: Jerome Glisse <jglisse at redhat.com>

> ---
>  drivers/gpu/drm/radeon/evergreen.c     |   29 ++++++++++++++++-------------
>  drivers/gpu/drm/radeon/r600.c          |   30 +++++++++++++++---------------
>  drivers/gpu/drm/radeon/radeon.h        |    3 +--
>  drivers/gpu/drm/radeon/radeon_device.c |    3 +--
>  drivers/gpu/drm/radeon/si.c            |   30 ++++++++++++++++--------------
>  5 files changed, 49 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
> index dd3cea4..bfcb39e 100644
> --- a/drivers/gpu/drm/radeon/evergreen.c
> +++ b/drivers/gpu/drm/radeon/evergreen.c
> @@ -2943,7 +2943,6 @@ int evergreen_irq_process(struct radeon_device *rdev)
>  	u32 rptr;
>  	u32 src_id, src_data;
>  	u32 ring_index;
> -	unsigned long flags;
>  	bool queue_hotplug = false;
>  	bool queue_hdmi = false;
>  
> @@ -2951,22 +2950,24 @@ int evergreen_irq_process(struct radeon_device *rdev)
>  		return IRQ_NONE;
>  
>  	wptr = evergreen_get_ih_wptr(rdev);
> +
> +restart_ih:
> +	/* is somebody else already processing irqs? */
> +	if (atomic_xchg(&rdev->ih.lock, 1))
> +		return IRQ_NONE;
> +
>  	rptr = rdev->ih.rptr;
> +	if (rptr == wptr)
> +		return IRQ_NONE;
> +
>  	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
>  
> -	spin_lock_irqsave(&rdev->ih.lock, flags);
> -	if (rptr == wptr) {
> -		spin_unlock_irqrestore(&rdev->ih.lock, flags);
> -		return IRQ_NONE;
> -	}
> -restart_ih:
>  	/* Order reading of wptr vs. reading of IH ring data */
>  	rmb();
>  
>  	/* display interrupts */
>  	evergreen_irq_ack(rdev);
>  
> -	rdev->ih.wptr = wptr;
>  	while (rptr != wptr) {
>  		/* wptr/rptr are in bytes! */
>  		ring_index = rptr / 4;
> @@ -3265,17 +3266,19 @@ restart_ih:
>  		rptr += 16;
>  		rptr &= rdev->ih.ptr_mask;
>  	}
> -	/* make sure wptr hasn't changed while processing */
> -	wptr = evergreen_get_ih_wptr(rdev);
> -	if (wptr != rdev->ih.wptr)
> -		goto restart_ih;
>  	if (queue_hotplug)
>  		schedule_work(&rdev->hotplug_work);
>  	if (queue_hdmi)
>  		schedule_work(&rdev->audio_work);
>  	rdev->ih.rptr = rptr;
>  	WREG32(IH_RB_RPTR, rdev->ih.rptr);
> -	spin_unlock_irqrestore(&rdev->ih.lock, flags);
> +	atomic_set(&rdev->ih.lock, 0);
> +
> +	/* make sure wptr hasn't changed while processing */
> +	wptr = evergreen_get_ih_wptr(rdev);
> +	if (wptr != rptr)
> +		goto restart_ih;
> +
>  	return IRQ_HANDLED;
>  }
>  
> diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
> index a8d8c44..eadbb06 100644
> --- a/drivers/gpu/drm/radeon/r600.c
> +++ b/drivers/gpu/drm/radeon/r600.c
> @@ -2921,7 +2921,6 @@ void r600_disable_interrupts(struct radeon_device *rdev)
>  	WREG32(IH_RB_RPTR, 0);
>  	WREG32(IH_RB_WPTR, 0);
>  	rdev->ih.enabled = false;
> -	rdev->ih.wptr = 0;
>  	rdev->ih.rptr = 0;
>  }
>  
> @@ -3373,7 +3372,6 @@ int r600_irq_process(struct radeon_device *rdev)
>  	u32 rptr;
>  	u32 src_id, src_data;
>  	u32 ring_index;
> -	unsigned long flags;
>  	bool queue_hotplug = false;
>  	bool queue_hdmi = false;
>  
> @@ -3385,24 +3383,24 @@ int r600_irq_process(struct radeon_device *rdev)
>  		RREG32(IH_RB_WPTR);
>  
>  	wptr = r600_get_ih_wptr(rdev);
> -	rptr = rdev->ih.rptr;
> -	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
>  
> -	spin_lock_irqsave(&rdev->ih.lock, flags);
> +restart_ih:
> +	/* is somebody else already processing irqs? */
> +	if (atomic_xchg(&rdev->ih.lock, 1))
> +		return IRQ_NONE;
>  
> -	if (rptr == wptr) {
> -		spin_unlock_irqrestore(&rdev->ih.lock, flags);
> +	rptr = rdev->ih.rptr;
> +	if (rptr == wptr)
>  		return IRQ_NONE;
> -	}
>  
> -restart_ih:
> +	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
> +
>  	/* Order reading of wptr vs. reading of IH ring data */
>  	rmb();
>  
>  	/* display interrupts */
>  	r600_irq_ack(rdev);
>  
> -	rdev->ih.wptr = wptr;
>  	while (rptr != wptr) {
>  		/* wptr/rptr are in bytes! */
>  		ring_index = rptr / 4;
> @@ -3556,17 +3554,19 @@ restart_ih:
>  		rptr += 16;
>  		rptr &= rdev->ih.ptr_mask;
>  	}
> -	/* make sure wptr hasn't changed while processing */
> -	wptr = r600_get_ih_wptr(rdev);
> -	if (wptr != rdev->ih.wptr)
> -		goto restart_ih;
>  	if (queue_hotplug)
>  		schedule_work(&rdev->hotplug_work);
>  	if (queue_hdmi)
>  		schedule_work(&rdev->audio_work);
>  	rdev->ih.rptr = rptr;
>  	WREG32(IH_RB_RPTR, rdev->ih.rptr);
> -	spin_unlock_irqrestore(&rdev->ih.lock, flags);
> +	atomic_set(&rdev->ih.lock, 0);
> +
> +	/* make sure wptr hasn't changed while processing */
> +	wptr = r600_get_ih_wptr(rdev);
> +	if (wptr != rptr)
> +		goto restart_ih;
> +
>  	return IRQ_HANDLED;
>  }
>  
> diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
> index 618df9a..8479096 100644
> --- a/drivers/gpu/drm/radeon/radeon.h
> +++ b/drivers/gpu/drm/radeon/radeon.h
> @@ -733,11 +733,10 @@ struct r600_ih {
>  	struct radeon_bo	*ring_obj;
>  	volatile uint32_t	*ring;
>  	unsigned		rptr;
> -	unsigned		wptr;
>  	unsigned		ring_size;
>  	uint64_t		gpu_addr;
>  	uint32_t		ptr_mask;
> -	spinlock_t              lock;
> +	atomic_t		lock;
>  	bool                    enabled;
>  };
>  
> diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
> index 7667184..3c563d1 100644
> --- a/drivers/gpu/drm/radeon/radeon_device.c
> +++ b/drivers/gpu/drm/radeon/radeon_device.c
> @@ -731,8 +731,7 @@ int radeon_device_init(struct radeon_device *rdev,
>  	radeon_mutex_init(&rdev->cs_mutex);
>  	mutex_init(&rdev->ring_lock);
>  	mutex_init(&rdev->dc_hw_i2c_mutex);
> -	if (rdev->family >= CHIP_R600)
> -		spin_lock_init(&rdev->ih.lock);
> +	atomic_set(&rdev->ih.lock, 0);
>  	mutex_init(&rdev->gem.mutex);
>  	mutex_init(&rdev->pm.mutex);
>  	init_rwsem(&rdev->pm.mclk_lock);
> diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
> index 5ca8ef5..f969487 100644
> --- a/drivers/gpu/drm/radeon/si.c
> +++ b/drivers/gpu/drm/radeon/si.c
> @@ -3095,7 +3095,6 @@ static void si_disable_interrupts(struct radeon_device *rdev)
>  	WREG32(IH_RB_RPTR, 0);
>  	WREG32(IH_RB_WPTR, 0);
>  	rdev->ih.enabled = false;
> -	rdev->ih.wptr = 0;
>  	rdev->ih.rptr = 0;
>  }
>  
> @@ -3512,29 +3511,30 @@ int si_irq_process(struct radeon_device *rdev)
>  	u32 rptr;
>  	u32 src_id, src_data, ring_id;
>  	u32 ring_index;
> -	unsigned long flags;
>  	bool queue_hotplug = false;
>  
>  	if (!rdev->ih.enabled || rdev->shutdown)
>  		return IRQ_NONE;
>  
>  	wptr = si_get_ih_wptr(rdev);
> +
> +restart_ih:
> +	/* is somebody else already processing irqs? */
> +	if (atomic_xchg(&rdev->ih.lock, 1))
> +		return IRQ_NONE;
> +
>  	rptr = rdev->ih.rptr;
> +	if (rptr == wptr)
> +		return IRQ_NONE;
> +
>  	DRM_DEBUG("si_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
>  
> -	spin_lock_irqsave(&rdev->ih.lock, flags);
> -	if (rptr == wptr) {
> -		spin_unlock_irqrestore(&rdev->ih.lock, flags);
> -		return IRQ_NONE;
> -	}
> -restart_ih:
>  	/* Order reading of wptr vs. reading of IH ring data */
>  	rmb();
>  
>  	/* display interrupts */
>  	si_irq_ack(rdev);
>  
> -	rdev->ih.wptr = wptr;
>  	while (rptr != wptr) {
>  		/* wptr/rptr are in bytes! */
>  		ring_index = rptr / 4;
> @@ -3785,15 +3785,17 @@ restart_ih:
>  		rptr += 16;
>  		rptr &= rdev->ih.ptr_mask;
>  	}
> -	/* make sure wptr hasn't changed while processing */
> -	wptr = si_get_ih_wptr(rdev);
> -	if (wptr != rdev->ih.wptr)
> -		goto restart_ih;
>  	if (queue_hotplug)
>  		schedule_work(&rdev->hotplug_work);
>  	rdev->ih.rptr = rptr;
>  	WREG32(IH_RB_RPTR, rdev->ih.rptr);
> -	spin_unlock_irqrestore(&rdev->ih.lock, flags);
> +	atomic_set(&rdev->ih.lock, 0);
> +
> +	/* make sure wptr hasn't changed while processing */
> +	wptr = si_get_ih_wptr(rdev);
> +	if (wptr != rptr)
> +		goto restart_ih;
> +
>  	return IRQ_HANDLED;
>  }
>  
> -- 
> 1.7.9.5
> 


More information about the dri-devel mailing list