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

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: 570e86963a51 ("drm: Widen vblank count to 64-bits [v3]")
Cc: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190723131337.22031-1-daniel.vetter@ffwll.ch

+54 -6
+41 -4
drivers/gpu/drm/drm_vblank.c
··· 106 106 107 107 write_seqlock(&vblank->seqlock); 108 108 vblank->time = t_vblank; 109 - vblank->count += vblank_count_inc; 109 + atomic64_add(vblank_count_inc, &vblank->count); 110 110 write_sequnlock(&vblank->seqlock); 111 111 } 112 112 ··· 272 272 273 273 DRM_DEBUG_VBL("updating vblank count on crtc %u:" 274 274 " current=%llu, diff=%u, hw=%u hw_last=%u\n", 275 - pipe, vblank->count, diff, cur_vblank, vblank->last); 275 + pipe, atomic64_read(&vblank->count), diff, 276 + cur_vblank, vblank->last); 276 277 277 278 if (diff == 0) { 278 279 WARN_ON_ONCE(cur_vblank != vblank->last); ··· 295 294 static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) 296 295 { 297 296 struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; 297 + u64 count; 298 298 299 299 if (WARN_ON(pipe >= dev->num_crtcs)) 300 300 return 0; 301 301 302 - return vblank->count; 302 + count = atomic64_read(&vblank->count); 303 + 304 + /* 305 + * This read barrier corresponds to the implicit write barrier of the 306 + * write seqlock in store_vblank(). Note that this is the only place 307 + * where we need an explicit barrier, since all other access goes 308 + * through drm_vblank_count_and_time(), which already has the required 309 + * read barrier curtesy of the read seqlock. 310 + */ 311 + smp_rmb(); 312 + 313 + return count; 303 314 } 304 315 305 316 /** ··· 776 763 * vblank interrupt (since it only reports the software vblank counter), see 777 764 * drm_crtc_accurate_vblank_count() for such use-cases. 778 765 * 766 + * Note that for a given vblank counter value drm_crtc_handle_vblank() 767 + * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time() 768 + * provide a barrier: Any writes done before calling 769 + * drm_crtc_handle_vblank() will be visible to callers of the later 770 + * functions, iff the vblank count is the same or a later one. 771 + * 772 + * See also &drm_vblank_crtc.count. 773 + * 779 774 * Returns: 780 775 * The software vblank counter. 781 776 */ ··· 821 800 822 801 do { 823 802 seq = read_seqbegin(&vblank->seqlock); 824 - vblank_count = vblank->count; 803 + vblank_count = atomic64_read(&vblank->count); 825 804 *vblanktime = vblank->time; 826 805 } while (read_seqretry(&vblank->seqlock, seq)); 827 806 ··· 838 817 * vblank events since the system was booted, including lost events due to 839 818 * modesetting activity. Returns corresponding system timestamp of the time 840 819 * of the vblank interval that corresponds to the current vblank counter value. 820 + * 821 + * Note that for a given vblank counter value drm_crtc_handle_vblank() 822 + * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time() 823 + * provide a barrier: Any writes done before calling 824 + * drm_crtc_handle_vblank() will be visible to callers of the later 825 + * functions, iff the vblank count is the same or a later one. 826 + * 827 + * See also &drm_vblank_crtc.count. 841 828 */ 842 829 u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, 843 830 ktime_t *vblanktime) ··· 1835 1806 * update the vblank counter and send any signals that may be pending. 1836 1807 * 1837 1808 * This is the native KMS version of drm_handle_vblank(). 1809 + * 1810 + * Note that for a given vblank counter value drm_crtc_handle_vblank() 1811 + * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time() 1812 + * provide a barrier: Any writes done before calling 1813 + * drm_crtc_handle_vblank() will be visible to callers of the later 1814 + * functions, iff the vblank count is the same or a later one. 1815 + * 1816 + * See also &drm_vblank_crtc.count. 1838 1817 * 1839 1818 * Returns: 1840 1819 * True if the event was successfully handled, false on failure.
+13 -2
include/drm/drm_vblank.h
··· 109 109 seqlock_t seqlock; 110 110 111 111 /** 112 - * @count: Current software vblank counter. 112 + * @count: 113 + * 114 + * Current software vblank counter. 115 + * 116 + * Note that for a given vblank counter value drm_crtc_handle_vblank() 117 + * and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time() 118 + * provide a barrier: Any writes done before calling 119 + * drm_crtc_handle_vblank() will be visible to callers of the later 120 + * functions, iff the vblank count is the same or a later one. 121 + * 122 + * IMPORTANT: This guarantee requires barriers, therefor never access 123 + * this field directly. Use drm_crtc_vblank_count() instead. 113 124 */ 114 - u64 count; 125 + atomic64_t count; 115 126 /** 116 127 * @time: Vblank timestamp corresponding to @count. 117 128 */