Commit bd7e3f3b authored by Daniel Vetter's avatar Daniel Vetter

drm/vblank: Document and fix vblank count barrier semantics

Noticed while reviewing code. I'm not sure whether this might or might
not explain some of the missed vblank hilarity we've been seeing on
various drivers (but those got tracked down to driver issues, at least
mostly). I think those all go through the vblank completion event,
which has unconditional barriers - it always takes the spinlock.
Therefore no cc stable.

v2:
- Barrriers are hard, put them in in the right order (Chris).
- Improve the comments a bit.

v3:

Ville noticed that on 32bit we might be breaking up the load/stores,
now that the vblank counter has been switched over to be 64 bit. Fix
that up by switching to atomic64_t. This this happens so rarely in
practice I figured no need to cc: stable ...

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Keith Packard <keithp@keithp.com>
References: 570e8696 ("drm: Widen vblank count to 64-bits [v3]")
Cc: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190723131337.22031-1-daniel.vetter@ffwll.ch
parent 5fcd0551
...@@ -106,7 +106,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, ...@@ -106,7 +106,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
write_seqlock(&vblank->seqlock); write_seqlock(&vblank->seqlock);
vblank->time = t_vblank; vblank->time = t_vblank;
vblank->count += vblank_count_inc; atomic64_add(vblank_count_inc, &vblank->count);
write_sequnlock(&vblank->seqlock); write_sequnlock(&vblank->seqlock);
} }
...@@ -272,7 +272,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ...@@ -272,7 +272,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
DRM_DEBUG_VBL("updating vblank count on crtc %u:" DRM_DEBUG_VBL("updating vblank count on crtc %u:"
" current=%llu, diff=%u, hw=%u hw_last=%u\n", " current=%llu, diff=%u, hw=%u hw_last=%u\n",
pipe, vblank->count, diff, cur_vblank, vblank->last); pipe, atomic64_read(&vblank->count), diff,
cur_vblank, vblank->last);
if (diff == 0) { if (diff == 0) {
WARN_ON_ONCE(cur_vblank != vblank->last); WARN_ON_ONCE(cur_vblank != vblank->last);
...@@ -294,11 +295,23 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, ...@@ -294,11 +295,23 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
{ {
struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
u64 count;
if (WARN_ON(pipe >= dev->num_crtcs)) if (WARN_ON(pipe >= dev->num_crtcs))
return 0; return 0;
return vblank->count; count = atomic64_read(&vblank->count);
/*
* This read barrier corresponds to the implicit write barrier of the
* write seqlock in store_vblank(). Note that this is the only place
* where we need an explicit barrier, since all other access goes
* through drm_vblank_count_and_time(), which already has the required
* read barrier curtesy of the read seqlock.
*/
smp_rmb();
return count;
} }
/** /**
...@@ -763,6 +776,14 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, ...@@ -763,6 +776,14 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
* vblank interrupt (since it only reports the software vblank counter), see * vblank interrupt (since it only reports the software vblank counter), see
* drm_crtc_accurate_vblank_count() for such use-cases. * drm_crtc_accurate_vblank_count() for such use-cases.
* *
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*
* Returns: * Returns:
* The software vblank counter. * The software vblank counter.
*/ */
...@@ -800,7 +821,7 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, ...@@ -800,7 +821,7 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
do { do {
seq = read_seqbegin(&vblank->seqlock); seq = read_seqbegin(&vblank->seqlock);
vblank_count = vblank->count; vblank_count = atomic64_read(&vblank->count);
*vblanktime = vblank->time; *vblanktime = vblank->time;
} while (read_seqretry(&vblank->seqlock, seq)); } while (read_seqretry(&vblank->seqlock, seq));
...@@ -817,6 +838,14 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, ...@@ -817,6 +838,14 @@ static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
* vblank events since the system was booted, including lost events due to * vblank events since the system was booted, including lost events due to
* modesetting activity. Returns corresponding system timestamp of the time * modesetting activity. Returns corresponding system timestamp of the time
* of the vblank interval that corresponds to the current vblank counter value. * of the vblank interval that corresponds to the current vblank counter value.
*
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*/ */
u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
ktime_t *vblanktime) ktime_t *vblanktime)
...@@ -1807,6 +1836,14 @@ EXPORT_SYMBOL(drm_handle_vblank); ...@@ -1807,6 +1836,14 @@ EXPORT_SYMBOL(drm_handle_vblank);
* *
* This is the native KMS version of drm_handle_vblank(). * This is the native KMS version of drm_handle_vblank().
* *
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* See also &drm_vblank_crtc.count.
*
* Returns: * Returns:
* True if the event was successfully handled, false on failure. * True if the event was successfully handled, false on failure.
*/ */
......
...@@ -109,9 +109,20 @@ struct drm_vblank_crtc { ...@@ -109,9 +109,20 @@ struct drm_vblank_crtc {
seqlock_t seqlock; seqlock_t seqlock;
/** /**
* @count: Current software vblank counter. * @count:
*
* Current software vblank counter.
*
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* IMPORTANT: This guarantee requires barriers, therefor never access
* this field directly. Use drm_crtc_vblank_count() instead.
*/ */
u64 count; atomic64_t count;
/** /**
* @time: Vblank timestamp corresponding to @count. * @time: Vblank timestamp corresponding to @count.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment