diff options
author | Christian Koenig <christian.koenig@amd.com> | 2012-05-16 15:45:24 -0400 |
---|---|---|
committer | Christian König <deathsimple@vodafone.de> | 2012-06-21 03:38:48 -0400 |
commit | c20dc3698dc7ecf053e2bf77299ae5982c0c2c45 (patch) | |
tree | e150268483bee4137d9c22e3121171b23a53be76 /drivers/gpu/drm/radeon/r600.c | |
parent | 6823d74003abedd688a3f535aefe6ce0e06444fd (diff) |
drm/radeon: fix & improve ih ring handling v3
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.
v2: Keep the spinlock....
v3: Back to an atomic again after finding & fixing the real bug.
Signed-off-by: Christian Koenig <christian.koenig@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/r600.c')
-rw-r--r-- | drivers/gpu/drm/radeon/r600.c | 29 |
1 files changed, 13 insertions, 16 deletions
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index aced97bb79e0..b487c69a486d 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c | |||
@@ -2858,7 +2858,6 @@ void r600_disable_interrupts(struct radeon_device *rdev) | |||
2858 | WREG32(IH_RB_RPTR, 0); | 2858 | WREG32(IH_RB_RPTR, 0); |
2859 | WREG32(IH_RB_WPTR, 0); | 2859 | WREG32(IH_RB_WPTR, 0); |
2860 | rdev->ih.enabled = false; | 2860 | rdev->ih.enabled = false; |
2861 | rdev->ih.wptr = 0; | ||
2862 | rdev->ih.rptr = 0; | 2861 | rdev->ih.rptr = 0; |
2863 | } | 2862 | } |
2864 | 2863 | ||
@@ -3310,7 +3309,6 @@ int r600_irq_process(struct radeon_device *rdev) | |||
3310 | u32 rptr; | 3309 | u32 rptr; |
3311 | u32 src_id, src_data; | 3310 | u32 src_id, src_data; |
3312 | u32 ring_index; | 3311 | u32 ring_index; |
3313 | unsigned long flags; | ||
3314 | bool queue_hotplug = false; | 3312 | bool queue_hotplug = false; |
3315 | bool queue_hdmi = false; | 3313 | bool queue_hdmi = false; |
3316 | 3314 | ||
@@ -3322,24 +3320,21 @@ int r600_irq_process(struct radeon_device *rdev) | |||
3322 | RREG32(IH_RB_WPTR); | 3320 | RREG32(IH_RB_WPTR); |
3323 | 3321 | ||
3324 | wptr = r600_get_ih_wptr(rdev); | 3322 | wptr = r600_get_ih_wptr(rdev); |
3325 | rptr = rdev->ih.rptr; | ||
3326 | DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); | ||
3327 | |||
3328 | spin_lock_irqsave(&rdev->ih.lock, flags); | ||
3329 | 3323 | ||
3330 | if (rptr == wptr) { | 3324 | restart_ih: |
3331 | spin_unlock_irqrestore(&rdev->ih.lock, flags); | 3325 | /* is somebody else already processing irqs? */ |
3326 | if (atomic_xchg(&rdev->ih.lock, 1)) | ||
3332 | return IRQ_NONE; | 3327 | return IRQ_NONE; |
3333 | } | ||
3334 | 3328 | ||
3335 | restart_ih: | 3329 | rptr = rdev->ih.rptr; |
3330 | DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); | ||
3331 | |||
3336 | /* Order reading of wptr vs. reading of IH ring data */ | 3332 | /* Order reading of wptr vs. reading of IH ring data */ |
3337 | rmb(); | 3333 | rmb(); |
3338 | 3334 | ||
3339 | /* display interrupts */ | 3335 | /* display interrupts */ |
3340 | r600_irq_ack(rdev); | 3336 | r600_irq_ack(rdev); |
3341 | 3337 | ||
3342 | rdev->ih.wptr = wptr; | ||
3343 | while (rptr != wptr) { | 3338 | while (rptr != wptr) { |
3344 | /* wptr/rptr are in bytes! */ | 3339 | /* wptr/rptr are in bytes! */ |
3345 | ring_index = rptr / 4; | 3340 | ring_index = rptr / 4; |
@@ -3493,17 +3488,19 @@ restart_ih: | |||
3493 | rptr += 16; | 3488 | rptr += 16; |
3494 | rptr &= rdev->ih.ptr_mask; | 3489 | rptr &= rdev->ih.ptr_mask; |
3495 | } | 3490 | } |
3496 | /* make sure wptr hasn't changed while processing */ | ||
3497 | wptr = r600_get_ih_wptr(rdev); | ||
3498 | if (wptr != rdev->ih.wptr) | ||
3499 | goto restart_ih; | ||
3500 | if (queue_hotplug) | 3491 | if (queue_hotplug) |
3501 | schedule_work(&rdev->hotplug_work); | 3492 | schedule_work(&rdev->hotplug_work); |
3502 | if (queue_hdmi) | 3493 | if (queue_hdmi) |
3503 | schedule_work(&rdev->audio_work); | 3494 | schedule_work(&rdev->audio_work); |
3504 | rdev->ih.rptr = rptr; | 3495 | rdev->ih.rptr = rptr; |
3505 | WREG32(IH_RB_RPTR, rdev->ih.rptr); | 3496 | WREG32(IH_RB_RPTR, rdev->ih.rptr); |
3506 | spin_unlock_irqrestore(&rdev->ih.lock, flags); | 3497 | atomic_set(&rdev->ih.lock, 0); |
3498 | |||
3499 | /* make sure wptr hasn't changed while processing */ | ||
3500 | wptr = r600_get_ih_wptr(rdev); | ||
3501 | if (wptr != rptr) | ||
3502 | goto restart_ih; | ||
3503 | |||
3507 | return IRQ_HANDLED; | 3504 | return IRQ_HANDLED; |
3508 | } | 3505 | } |
3509 | 3506 | ||