aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2017-10-11 11:20:12 -0400
committerDave Airlie <airlied@redhat.com>2017-10-12 18:34:46 -0400
commit67680d3c046450b3901aa4e5a9cf2f8fbd7ed9a2 (patch)
treee62cd147951abc2b077336a1bd3157115bd192e8
parentc5c7bc71a0e6bc382744497e45c6c70503f9d7e6 (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.c123
-rw-r--r--include/drm/drm_drv.h2
-rw-r--r--include/drm/drm_vblank.h6
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
79static bool 79static bool
80drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, 80drm_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
83static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ 83static 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
100static void store_vblank(struct drm_device *dev, unsigned int pipe, 100static 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
286static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) 282static 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);
584bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, 580bool 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}
698EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); 695EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
699 696
700static struct timeval get_drm_timestamp(void) 697static 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