drm/nouveau/kms/nv50: Fix atomic pageflip events.
authorMario Kleiner <mario.kleiner.de@gmail.com>
Wed, 23 Nov 2016 06:58:54 +0000 (07:58 +0100)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 28 Nov 2016 05:39:35 +0000 (15:39 +1000)
The new atomic modesetting/pageflip code for nv50+ for
Linux 4.10+ no longer uses pageflip irq's to signal
flip completion. Instead it polls for flip completion
from within a kthread/work queue.

This creates a race between the vblank irq handler
updating the vblank count and timestamp for the
vblank of flip completion, and the kthread's
polling code detecting flip completion and sending
out the flip completion event.

Depending on who executes a few microseconds earlier,
the flip completion event will either contain correct
count/timestamp or a stale count/timestamp from the
previous vblank. This error was observed for about
50% of all executed flips, e.g., observable under DRI2
by the Xorg.log filling with flip handler warning
messages.

Call drm_accurate_vblank_count() before sending
out flip completion events to enforce a vblank
count/ts update for the vblank of flip completion
and avoid stale counts/timestamps.

This fix leads to one redundant call to drm_update_vblank_count
for each completed flip, but no other side effects. On
a ~6 year old Core i7 M620@ 2.67GHz the redundant call
costs about 10 usecs per flip

Successfully tested on GeForce 9500/9600/330M so far.

Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nv50_display.c

index 22a8b70a4d1e24e3751849b722bd3e86573ffd0b..7a1aa916198259b6fa644d13a16f551234409da4 100644 (file)
@@ -4090,6 +4090,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                if (crtc->state->event) {
                        unsigned long flags;
+                       /* Get correct count/ts if racing with vblank irq */
+                       drm_accurate_vblank_count(crtc);
                        spin_lock_irqsave(&crtc->dev->event_lock, flags);
                        drm_crtc_send_vblank_event(crtc, crtc->state->event);
                        spin_unlock_irqrestore(&crtc->dev->event_lock, flags);