Skip to content

Commit

Permalink
drm/radeon: fix & improve ih ring handling v3
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Christian Koenig authored and Christian König committed Jun 21, 2012
1 parent 6823d74 commit c20dc36
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 47 deletions.
26 changes: 13 additions & 13 deletions drivers/gpu/drm/radeon/evergreen.c
Original file line number Diff line number Diff line change
Expand Up @@ -2676,30 +2676,28 @@ 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;

if (!rdev->ih.enabled || rdev->shutdown)
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;
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;
Expand Down Expand Up @@ -2998,17 +2996,19 @@ int evergreen_irq_process(struct radeon_device *rdev)
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;
}

Expand Down
29 changes: 13 additions & 16 deletions drivers/gpu/drm/radeon/r600.c
Original file line number Diff line number Diff line change
Expand Up @@ -2858,7 +2858,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;
}

Expand Down Expand Up @@ -3310,7 +3309,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;

Expand All @@ -3322,24 +3320,21 @@ 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);

if (rptr == wptr) {
spin_unlock_irqrestore(&rdev->ih.lock, flags);
restart_ih:
/* is somebody else already processing irqs? */
if (atomic_xchg(&rdev->ih.lock, 1))
return IRQ_NONE;
}

restart_ih:
rptr = rdev->ih.rptr;
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;
Expand Down Expand Up @@ -3493,17 +3488,19 @@ int r600_irq_process(struct radeon_device *rdev)
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;
}

Expand Down
3 changes: 1 addition & 2 deletions drivers/gpu/drm/radeon/radeon.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,11 +738,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;
};

Expand Down
3 changes: 1 addition & 2 deletions drivers/gpu/drm/radeon/radeon_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 13 additions & 14 deletions drivers/gpu/drm/radeon/si.c
Original file line number Diff line number Diff line change
Expand Up @@ -2942,7 +2942,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;
}

Expand Down Expand Up @@ -3359,29 +3358,27 @@ 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;
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;
Expand Down Expand Up @@ -3632,15 +3629,17 @@ int si_irq_process(struct radeon_device *rdev)
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;
}

Expand Down

0 comments on commit c20dc36

Please sign in to comment.