In drm_crtc_vblank_get_vblank_timeout(), there's a loop to read
a consistent vblank count and time. Only read the time once per
iteration and avoid costly locking and an atomic read.

Return an error after 10 retries. This indicates that the vblank
counter is broken or being updated way too often.

Signed-off-by: Thomas Zimmermann <[email protected]>
---
 drivers/gpu/drm/drm_vblank.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 75e2183be0ab..05f28e27cbff 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -2316,7 +2316,8 @@ bool drm_crtc_vblank_get_vblank_timeout(struct drm_crtc 
*crtc, ktime_t *vblank_t
 
        if (READ_ONCE(vblank->enabled)) {
                ktime_t cur_time;
-               u64 cur_count;
+               u64 lst_count, cur_count;
+               unsigned int retries = 10;
 
                /*
                 * A concurrent vblank timeout could update the expires field 
before
@@ -2324,10 +2325,15 @@ bool drm_crtc_vblank_get_vblank_timeout(struct drm_crtc 
*crtc, ktime_t *vblank_t
                 * expiry time to the new vblank time; deducing the timer had 
already
                 * expired. Reread until we get consistent values from both 
fields.
                 */
+               cur_count = drm_crtc_vblank_count(crtc);
                do {
-                       cur_count = drm_crtc_vblank_count_and_time(crtc, 
&cur_time);
+                       lst_count = cur_count;
                        *vblank_time = READ_ONCE(vtimer->timer.node.expires);
-               } while (cur_count != drm_crtc_vblank_count_and_time(crtc, 
&cur_time));
+                       cur_count = drm_crtc_vblank_count_and_time(crtc, 
&cur_time);
+               } while (cur_count != lst_count && retries--);
+
+               if (drm_WARN_ON(dev, cur_count != lst_count))
+                       return false; /* broken vblank counter */
 
                if (drm_WARN_ON(dev, !ktime_after(*vblank_time, cur_time)))
                        return false; /* already expired */
-- 
2.54.0

Reply via email to