Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/radeon: Prevent races on pre DCE4 between flip submission and completion.

Pre DCE4 hw doesn't have reliable pageflip completion
interrupts, so instead polling for flip completion is
used from within the vblank irq handler to complete
page flips.

This causes a race if pageflip ioctl is called close to
vblank:

1. pageflip ioctl queues execution of radeon_flip_work_func.

2. vblank irq fires, radeon_crtc_handle_vblank checks for
flip_status == FLIP_SUBMITTED finds none, no-ops.

3. radeon_flip_work_func runs inside vblank, decides to
set flip_status == FLIP_SUBMITTED and programs the
flip into hw.

4. hw executes flip immediately (because in vblank), but
as 2 already happened, the flip completion routine only
emits the flip completion event one refresh later ->
wrong vblank count/timestamp for completion and no
performance gain, as instead of delaying the flip until
next vblank, we now delay the next flip by 1 refresh
while waiting for the delayed flip completion event.

Given we often don't gain anything due to this race, but
lose precision, prevent the programmed flip from executing
in vblank on pre DCE4 asics to avoid this race.

On pre-AVIVO hw we can't program the hw for edge-triggered
flips, they always execute anywhere in vblank. Therefore delay
the actual flip programming until after vblank on pre-AVIVO.

Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>
Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Mario Kleiner and committed by
Alex Deucher
363926dc 73d4c23f

+14 -10
+2 -2
drivers/gpu/drm/radeon/atombios_crtc.c
··· 1638 1638 WREG32(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset, 1639 1639 (viewport_w << 16) | viewport_h); 1640 1640 1641 - /* set pageflip to happen anywhere in vblank interval */ 1642 - WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); 1641 + /* set pageflip to happen only at start of vblank interval (front porch) */ 1642 + WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 3); 1643 1643 1644 1644 if (!atomic && fb && fb != crtc->primary->fb) { 1645 1645 radeon_fb = to_radeon_framebuffer(fb);
+10 -7
drivers/gpu/drm/radeon/radeon_display.c
··· 452 452 } 453 453 454 454 /* Wait until we're out of the vertical blank period before the one 455 - * targeted by the flip 455 + * targeted by the flip. Always wait on pre DCE4 to avoid races with 456 + * flip completion handling from vblank irq, as these old asics don't 457 + * have reliable pageflip completion interrupts. 456 458 */ 457 459 while (radeon_crtc->enabled && 458 - (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0, 459 - &vpos, &hpos, NULL, NULL, 460 - &crtc->hwmode) 460 + (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0, 461 + &vpos, &hpos, NULL, NULL, 462 + &crtc->hwmode) 461 463 & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) == 462 - (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && 463 - (int)(work->target_vblank - 464 - dev->driver->get_vblank_counter(dev, work->crtc_id)) > 0) 464 + (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) && 465 + (!ASIC_IS_AVIVO(rdev) || 466 + ((int) (work->target_vblank - 467 + dev->driver->get_vblank_counter(dev, work->crtc_id)) > 0))) 465 468 usleep_range(1000, 2000); 466 469 467 470 /* We borrow the event spin lock for protecting flip_status */
+2 -1
drivers/gpu/drm/radeon/rv515.c
··· 406 406 for (i = 0; i < rdev->num_crtc; i++) { 407 407 if (save->crtc_enabled[i]) { 408 408 tmp = RREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + crtc_offsets[i]); 409 - if ((tmp & 0x7) != 0) { 409 + if ((tmp & 0x7) != 3) { 410 410 tmp &= ~0x7; 411 + tmp |= 0x3; 411 412 WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + crtc_offsets[i], tmp); 412 413 } 413 414 tmp = RREG32(AVIVO_D1GRPH_UPDATE + crtc_offsets[i]);