diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2017-10-11 11:20:12 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2017-10-12 18:34:46 -0400 |
| commit | 67680d3c046450b3901aa4e5a9cf2f8fbd7ed9a2 (patch) | |
| tree | e62cd147951abc2b077336a1bd3157115bd192e8 | |
| parent | c5c7bc71a0e6bc382744497e45c6c70503f9d7e6 (diff) | |
drm: vblank: use ktime_t instead of timeval
The drm vblank handling uses 'timeval' to store timestamps in either
monotonic or wall-clock time base. In either case, it reads the current
time as a ktime_t in get_drm_timestamp() and converts it from there.
This is a bit suspicious, as users of 'timeval' often suffer from
the time_t overflow in y2038. I have gone through this code and
found that it is unlikely to cause problems here:
- The user space ABI does not use time_t or timeval, but uses
'u32' and 'long' as the types. This means at least that rebuilding
user programs against a new libc with 64-bit time_t does not
change the ABI.
- As of commit c61eef726a78 ("drm: add support for monotonic vblank
timestamps") in linux-3.8, the monotonic timestamp is the default
and can only get reverted to wall-clock through a module-parameter.
- With the default monotonic timestamps, there is no problem at all.
- The drm_wait_vblank_ioctl() interface is alway safe on 64-bit
architectures, on 32-bit it might overflow the 'long' timestamps
in 2038 with wall-clock timestamps.
- The event handling uses 'u32' seconds, which overflow in 2106
on both 32-bit and 64-bit machines, when wall-clock timestamps
are used.
- The effect of overflowing either of the two is only temporary
(during the overflow, and is likely to keep working again
afterwards. It is likely the same problem as observing a
'settimeofday()' call, which was the reason for moving to the
monotonic timestamps in the first place.
Overall, this seems good enough, so my patch removes the use of
'timeval' from the vblank handling altogether and uses ktime_t
consistently, except for the part where we copy the data to user
space structures in the existing format.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reviewed-by: Keith Packard <keithp@keithp.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
| -rw-r--r-- | drivers/gpu/drm/drm_vblank.c | 123 | ||||
| -rw-r--r-- | include/drm/drm_drv.h | 2 | ||||
| -rw-r--r-- | include/drm/drm_vblank.h | 6 |
3 files changed, 71 insertions, 60 deletions
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 70f2b9593edc..c605c3ad6b6e 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c | |||
| @@ -78,7 +78,7 @@ | |||
| 78 | 78 | ||
| 79 | static bool | 79 | static bool |
| 80 | drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, | 80 | drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, |
| 81 | struct timeval *tvblank, bool in_vblank_irq); | 81 | ktime_t *tvblank, bool in_vblank_irq); |
| 82 | 82 | ||
| 83 | static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ | 83 | static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ |
| 84 | 84 | ||
| @@ -99,7 +99,7 @@ MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); | |||
| 99 | 99 | ||
| 100 | static void store_vblank(struct drm_device *dev, unsigned int pipe, | 100 | static void store_vblank(struct drm_device *dev, unsigned int pipe, |
| 101 | u32 vblank_count_inc, | 101 | u32 vblank_count_inc, |
| 102 | struct timeval *t_vblank, u32 last) | 102 | ktime_t t_vblank, u32 last) |
| 103 | { | 103 | { |
| 104 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; | 104 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; |
| 105 | 105 | ||
| @@ -108,7 +108,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, | |||
| 108 | vblank->last = last; | 108 | vblank->last = last; |
| 109 | 109 | ||
| 110 | write_seqlock(&vblank->seqlock); | 110 | write_seqlock(&vblank->seqlock); |
| 111 | vblank->time = *t_vblank; | 111 | vblank->time = t_vblank; |
| 112 | vblank->count += vblank_count_inc; | 112 | vblank->count += vblank_count_inc; |
| 113 | write_sequnlock(&vblank->seqlock); | 113 | write_sequnlock(&vblank->seqlock); |
| 114 | } | 114 | } |
| @@ -151,7 +151,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe | |||
| 151 | { | 151 | { |
| 152 | u32 cur_vblank; | 152 | u32 cur_vblank; |
| 153 | bool rc; | 153 | bool rc; |
| 154 | struct timeval t_vblank; | 154 | ktime_t t_vblank; |
| 155 | int count = DRM_TIMESTAMP_MAXRETRIES; | 155 | int count = DRM_TIMESTAMP_MAXRETRIES; |
| 156 | 156 | ||
| 157 | spin_lock(&dev->vblank_time_lock); | 157 | spin_lock(&dev->vblank_time_lock); |
| @@ -171,13 +171,13 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe | |||
| 171 | * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. | 171 | * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. |
| 172 | */ | 172 | */ |
| 173 | if (!rc) | 173 | if (!rc) |
| 174 | t_vblank = (struct timeval) {0, 0}; | 174 | t_vblank = 0; |
| 175 | 175 | ||
| 176 | /* | 176 | /* |
| 177 | * +1 to make sure user will never see the same | 177 | * +1 to make sure user will never see the same |
| 178 | * vblank counter value before and after a modeset | 178 | * vblank counter value before and after a modeset |
| 179 | */ | 179 | */ |
| 180 | store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); | 180 | store_vblank(dev, pipe, 1, t_vblank, cur_vblank); |
| 181 | 181 | ||
| 182 | spin_unlock(&dev->vblank_time_lock); | 182 | spin_unlock(&dev->vblank_time_lock); |
| 183 | } | 183 | } |
| @@ -200,7 +200,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, | |||
| 200 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; | 200 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; |
| 201 | u32 cur_vblank, diff; | 201 | u32 cur_vblank, diff; |
| 202 | bool rc; | 202 | bool rc; |
| 203 | struct timeval t_vblank; | 203 | ktime_t t_vblank; |
| 204 | int count = DRM_TIMESTAMP_MAXRETRIES; | 204 | int count = DRM_TIMESTAMP_MAXRETRIES; |
| 205 | int framedur_ns = vblank->framedur_ns; | 205 | int framedur_ns = vblank->framedur_ns; |
| 206 | 206 | ||
| @@ -225,11 +225,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, | |||
| 225 | /* trust the hw counter when it's around */ | 225 | /* trust the hw counter when it's around */ |
| 226 | diff = (cur_vblank - vblank->last) & dev->max_vblank_count; | 226 | diff = (cur_vblank - vblank->last) & dev->max_vblank_count; |
| 227 | } else if (rc && framedur_ns) { | 227 | } else if (rc && framedur_ns) { |
| 228 | const struct timeval *t_old; | 228 | u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time)); |
| 229 | u64 diff_ns; | ||
| 230 | |||
| 231 | t_old = &vblank->time; | ||
| 232 | diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); | ||
| 233 | 229 | ||
| 234 | /* | 230 | /* |
| 235 | * Figure out how many vblanks we've missed based | 231 | * Figure out how many vblanks we've missed based |
| @@ -278,9 +274,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, | |||
| 278 | * for now, to mark the vblanktimestamp as invalid. | 274 | * for now, to mark the vblanktimestamp as invalid. |
| 279 | */ | 275 | */ |
| 280 | if (!rc && !in_vblank_irq) | 276 | if (!rc && !in_vblank_irq) |
| 281 | t_vblank = (struct timeval) {0, 0}; | 277 | t_vblank = 0; |
| 282 | 278 | ||
| 283 | store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); | 279 | store_vblank(dev, pipe, diff, t_vblank, cur_vblank); |
| 284 | } | 280 | } |
| 285 | 281 | ||
| 286 | static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) | 282 | static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) |
| @@ -556,7 +552,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); | |||
| 556 | * @pipe: index of CRTC whose vblank timestamp to retrieve | 552 | * @pipe: index of CRTC whose vblank timestamp to retrieve |
| 557 | * @max_error: Desired maximum allowable error in timestamps (nanosecs) | 553 | * @max_error: Desired maximum allowable error in timestamps (nanosecs) |
| 558 | * On return contains true maximum error of timestamp | 554 | * On return contains true maximum error of timestamp |
| 559 | * @vblank_time: Pointer to struct timeval which should receive the timestamp | 555 | * @vblank_time: Pointer to time which should receive the timestamp |
| 560 | * @in_vblank_irq: | 556 | * @in_vblank_irq: |
| 561 | * True when called from drm_crtc_handle_vblank(). Some drivers | 557 | * True when called from drm_crtc_handle_vblank(). Some drivers |
| 562 | * need to apply some workarounds for gpu-specific vblank irq quirks | 558 | * need to apply some workarounds for gpu-specific vblank irq quirks |
| @@ -584,10 +580,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); | |||
| 584 | bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, | 580 | bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, |
| 585 | unsigned int pipe, | 581 | unsigned int pipe, |
| 586 | int *max_error, | 582 | int *max_error, |
| 587 | struct timeval *vblank_time, | 583 | ktime_t *vblank_time, |
| 588 | bool in_vblank_irq) | 584 | bool in_vblank_irq) |
| 589 | { | 585 | { |
| 590 | struct timeval tv_etime; | 586 | struct timespec64 ts_etime, ts_vblank_time; |
| 591 | ktime_t stime, etime; | 587 | ktime_t stime, etime; |
| 592 | bool vbl_status; | 588 | bool vbl_status; |
| 593 | struct drm_crtc *crtc; | 589 | struct drm_crtc *crtc; |
| @@ -680,29 +676,27 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, | |||
| 680 | etime = ktime_mono_to_real(etime); | 676 | etime = ktime_mono_to_real(etime); |
| 681 | 677 | ||
| 682 | /* save this only for debugging purposes */ | 678 | /* save this only for debugging purposes */ |
| 683 | tv_etime = ktime_to_timeval(etime); | 679 | ts_etime = ktime_to_timespec64(etime); |
| 680 | ts_vblank_time = ktime_to_timespec64(*vblank_time); | ||
| 684 | /* Subtract time delta from raw timestamp to get final | 681 | /* Subtract time delta from raw timestamp to get final |
| 685 | * vblank_time timestamp for end of vblank. | 682 | * vblank_time timestamp for end of vblank. |
| 686 | */ | 683 | */ |
| 687 | etime = ktime_sub_ns(etime, delta_ns); | 684 | etime = ktime_sub_ns(etime, delta_ns); |
| 688 | *vblank_time = ktime_to_timeval(etime); | 685 | *vblank_time = etime; |
| 689 | 686 | ||
| 690 | DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", | 687 | DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n", |
| 691 | pipe, hpos, vpos, | 688 | pipe, hpos, vpos, |
| 692 | (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, | 689 | (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000, |
| 693 | (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, | 690 | (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000, |
| 694 | duration_ns/1000, i); | 691 | duration_ns / 1000, i); |
| 695 | 692 | ||
| 696 | return true; | 693 | return true; |
| 697 | } | 694 | } |
| 698 | EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); | 695 | EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); |
| 699 | 696 | ||
| 700 | static struct timeval get_drm_timestamp(void) | 697 | static ktime_t get_drm_timestamp(void) |
| 701 | { | 698 | { |
| 702 | ktime_t now; | 699 | return drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); |
| 703 | |||
| 704 | now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); | ||
| 705 | return ktime_to_timeval(now); | ||
| 706 | } | 700 | } |
| 707 | 701 | ||
| 708 | /** | 702 | /** |
| @@ -710,7 +704,7 @@ static struct timeval get_drm_timestamp(void) | |||
| 710 | * vblank interval | 704 | * vblank interval |
| 711 | * @dev: DRM device | 705 | * @dev: DRM device |
| 712 | * @pipe: index of CRTC whose vblank timestamp to retrieve | 706 | * @pipe: index of CRTC whose vblank timestamp to retrieve |
| 713 | * @tvblank: Pointer to target struct timeval which should receive the timestamp | ||
